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:
parent
bd58840220
commit
01d31e291d
32 changed files with 4318 additions and 2555 deletions
|
@ -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',
|
||||
|
|
|
@ -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 )------------------------------------------------------- */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -11,7 +11,7 @@ final class PhutilRemarkupTestInterpreterRule
|
|||
return sprintf(
|
||||
"Content: (%s)\nArgv: (%s)",
|
||||
$content,
|
||||
http_build_query($argv));
|
||||
phutil_build_http_querystring($argv));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
599
src/parser/xhpast/__tests__/data/anonymous_class.php.test
Normal file
599
src/parser/xhpast/__tests__/data/anonymous_class.php.test
Normal 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
|
||||
]
|
||||
]
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.'));
|
||||
}
|
||||
|
|
23
src/xsprintf/AphrontDatabaseTableRef.php
Normal file
23
src/xsprintf/AphrontDatabaseTableRef.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
8
src/xsprintf/AphrontDatabaseTableRefInterface.php
Normal file
8
src/xsprintf/AphrontDatabaseTableRefInterface.php
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
interface AphrontDatabaseTableRefInterface {
|
||||
|
||||
public function getAphrontRefDatabaseName();
|
||||
public function getAphrontRefTableName();
|
||||
|
||||
}
|
39
src/xsprintf/PhutilQueryString.php
Normal file
39
src/xsprintf/PhutilQueryString.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 /* , ... */) {
|
||||
|
|
|
@ -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
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue