mirror of
https://github.com/apache/thrift.git
synced 2026-10-29 00:46:21 +08:00
Client: php * TSocket constructor now accepts only ?LoggerInterface for $debugHandler. * Remove TSocket::DEFAULT_DEBUG_HANDLER, TSocket::setDebug(), and the legacy callable/string debug-handler path. * Remove the E_USER_DEPRECATED fallback notice in TSocket::open(); callers should set a connect timeout explicitly with setConnectTimeout(). * TSSLSocket and TSocketPool constructors accept only ?LoggerInterface. * Remove public TSSLServerSocket::getSSLHost() (ssl:// prefix is applied automatically by the constructor). * Update unit tests and the PHP cross-test client to the LoggerInterface API. * Remove the now-unused UserDeprecationCapture test trait. * Document the breaking changes in lib/php/README.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
550 lines
13 KiB
PHP
Executable File
550 lines
13 KiB
PHP
Executable File
<?php
|
|
|
|
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
namespace test\php;
|
|
|
|
use Psr\Log\AbstractLogger;
|
|
use Psr\Log\LoggerInterface;
|
|
use Thrift\ClassLoader\ThriftClassLoader;
|
|
use Thrift\Transport\TBufferedTransport;
|
|
use Thrift\Transport\TFramedTransport;
|
|
use Thrift\Transport\TPsrHttpClient;
|
|
use Thrift\Transport\TSocketPool;
|
|
|
|
/** @var \Composer\Autoload\ClassLoader $loader */
|
|
$loader = require __DIR__ . '/../../vendor/autoload.php';
|
|
|
|
if (!isset($GEN_DIR)) {
|
|
$GEN_DIR = 'gen-php';
|
|
}
|
|
if (!isset($MODE)) {
|
|
$MODE = 'normal';
|
|
}
|
|
|
|
if ($GEN_DIR == 'gen-php') {
|
|
$loader->addPsr4('', $GEN_DIR);
|
|
} else {
|
|
$loader = new ThriftClassLoader();
|
|
$loader->registerDefinition('ThriftTest', $GEN_DIR);
|
|
$loader->register();
|
|
}
|
|
|
|
require_once __DIR__ . '/protocols.php';
|
|
|
|
/**
|
|
* Minimal PSR-3 logger that forwards every message to PHP's error_log
|
|
* (stderr in CLI mode).
|
|
*/
|
|
final class StderrLogger extends AbstractLogger implements LoggerInterface
|
|
{
|
|
/**
|
|
* @param string|\Stringable $level
|
|
* @param string|\Stringable $message
|
|
* @param array<mixed> $context
|
|
*/
|
|
public function log($level, $message, array $context = []): void
|
|
{
|
|
error_log('[' . (string) $level . '] ' . (string) $message);
|
|
}
|
|
}
|
|
|
|
$port = 9090;
|
|
|
|
foreach ($argv as $arg) {
|
|
if (substr($arg, 0, 7) == '--port=') {
|
|
$port = (int) substr($arg, 7);
|
|
} elseif (substr($arg, 0, 12) == '--transport=') {
|
|
$MODE = substr($arg, 12);
|
|
} elseif (substr($arg, 0, 11) == '--protocol=') {
|
|
$PROTO = substr($arg, 11);
|
|
}
|
|
}
|
|
|
|
// TPsrHttpClient buffers internally, so no framed/buffered wrapper is needed.
|
|
// Inline mode passes the raw transport to the generated client without a
|
|
// protocol wrapper, matching the legacy code path.
|
|
$transport = match ($MODE) {
|
|
'http' => new TPsrHttpClient(sprintf('http://127.0.0.1:%d/', $port)),
|
|
default => new TSocketPool(['localhost'], $port, false, new StderrLogger()),
|
|
};
|
|
$transport = match ($MODE) {
|
|
'framed' => new TFramedTransport($transport),
|
|
'http', 'inline' => $transport,
|
|
default => new TBufferedTransport($transport, 1024, 1024),
|
|
};
|
|
|
|
$protocol = $MODE === 'inline' ? null : thrift_test_protocol_factory($PROTO)->getProtocol($transport);
|
|
$testClient = new \ThriftTest\ThriftTestClient($protocol ?? $transport);
|
|
|
|
$transport->open();
|
|
|
|
$start = microtime(true);
|
|
|
|
define('ERR_BASETYPES', 1);
|
|
define('ERR_STRUCTS', 2);
|
|
define('ERR_CONTAINERS', 4);
|
|
define('ERR_EXCEPTIONS', 8);
|
|
define('ERR_UNKNOWN', 64);
|
|
$exitcode = 0;
|
|
|
|
/**
|
|
* VOID TEST
|
|
*/
|
|
print_r("testVoid()");
|
|
$testClient->testVoid();
|
|
print_r(" = void\n");
|
|
|
|
function roundtrip($testClient, $method, $value)
|
|
{
|
|
global $exitcode;
|
|
print_r("$method($value)");
|
|
$ret = $testClient->$method($value);
|
|
print_r(" = \"$ret\"\n");
|
|
if ($value !== $ret) {
|
|
print_r("*** FAILED ***\n");
|
|
$exitcode |= ERR_BASETYPES;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* STRING TEST
|
|
*/
|
|
roundtrip($testClient, 'testString', "Test");
|
|
roundtrip($testClient, 'testString', 'path /tmp/thrift and url https://127.0.0.1/service');
|
|
|
|
/**
|
|
* BOOL TEST
|
|
*/
|
|
roundtrip($testClient, 'testBool', true);
|
|
roundtrip($testClient, 'testBool', false);
|
|
|
|
/**
|
|
* BYTE TEST
|
|
*/
|
|
roundtrip($testClient, 'testByte', 1);
|
|
roundtrip($testClient, 'testByte', -1);
|
|
roundtrip($testClient, 'testByte', 127);
|
|
roundtrip($testClient, 'testByte', -128);
|
|
|
|
/**
|
|
* I32 TEST
|
|
*/
|
|
roundtrip($testClient, 'testI32', -1);
|
|
|
|
/**
|
|
* I64 TEST
|
|
*/
|
|
roundtrip($testClient, 'testI64', 0);
|
|
roundtrip($testClient, 'testI64', 1);
|
|
roundtrip($testClient, 'testI64', -1);
|
|
roundtrip($testClient, 'testI64', -34359738368);
|
|
|
|
/**
|
|
* DOUBLE TEST
|
|
*/
|
|
roundtrip($testClient, 'testDouble', -852.234234234);
|
|
|
|
/**
|
|
* BINARY TEST -- TODO
|
|
*/
|
|
|
|
/**
|
|
* UUID TEST
|
|
*/
|
|
print_r("testUuid('00000000-0000-0000-0000-000000000000')");
|
|
$uuid_in = '00000000-0000-0000-0000-000000000000';
|
|
$uuid_out = $testClient->testUuid($uuid_in);
|
|
print_r(" = \"$uuid_out\"\n");
|
|
if ($uuid_in !== $uuid_out) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_BASETYPES;
|
|
}
|
|
|
|
roundtrip($testClient, 'testUuid', '00000000-0000-0000-0000-000000000000');
|
|
roundtrip($testClient, 'testUuid', '550e8400-e29b-41d4-a716-446655440000');
|
|
|
|
/**
|
|
* STRUCT TEST
|
|
*/
|
|
print_r("testStruct({\"Zero\", 1, -3, -5})");
|
|
$out = new \ThriftTest\Xtruct();
|
|
$out->string_thing = "Zero";
|
|
$out->byte_thing = 1;
|
|
$out->i32_thing = -3;
|
|
$out->i64_thing = -5;
|
|
$in = $testClient->testStruct($out);
|
|
print_r(" = {\"" . $in->string_thing . "\", " .
|
|
$in->byte_thing . ", " .
|
|
$in->i32_thing . ", " .
|
|
$in->i64_thing . "}\n");
|
|
|
|
if ($in != $out) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_STRUCTS;
|
|
}
|
|
|
|
/**
|
|
* NESTED STRUCT TEST
|
|
*/
|
|
print_r("testNest({1, {\"Zero\", 1, -3, -5}), 5}");
|
|
$out2 = new \ThriftTest\Xtruct2();
|
|
$out2->byte_thing = 1;
|
|
$out2->struct_thing = $out;
|
|
$out2->i32_thing = 5;
|
|
$in2 = $testClient->testNest($out2);
|
|
$in = $in2->struct_thing;
|
|
print_r(" = {" . $in2->byte_thing . ", {\"" .
|
|
$in->string_thing . "\", " .
|
|
$in->byte_thing . ", " .
|
|
$in->i32_thing . ", " .
|
|
$in->i64_thing . "}, " .
|
|
$in2->i32_thing . "}\n");
|
|
|
|
if ($in2 != $out2) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_STRUCTS;
|
|
}
|
|
|
|
/**
|
|
* MAP TEST
|
|
*/
|
|
$mapout = [];
|
|
for ($i = 0; $i < 5; ++$i) {
|
|
$mapout[$i] = $i - 10;
|
|
}
|
|
print_r("testMap({");
|
|
$first = true;
|
|
foreach ($mapout as $key => $val) {
|
|
if ($first) {
|
|
$first = false;
|
|
} else {
|
|
print_r(", ");
|
|
}
|
|
print_r("$key => $val");
|
|
}
|
|
print_r("})");
|
|
|
|
$mapin = $testClient->testMap($mapout);
|
|
print_r(" = {");
|
|
$first = true;
|
|
foreach ($mapin as $key => $val) {
|
|
if ($first) {
|
|
$first = false;
|
|
} else {
|
|
print_r(", ");
|
|
}
|
|
print_r("$key => $val");
|
|
}
|
|
print_r("}\n");
|
|
|
|
if ($mapin != $mapout) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_CONTAINERS;
|
|
}
|
|
|
|
$mapout = [];
|
|
for ($i = 0; $i < 10; $i++) {
|
|
$mapout["key$i"] = "val$i";
|
|
}
|
|
print_r('testStringMap({');
|
|
$first = true;
|
|
foreach ($mapout as $key => $val) {
|
|
if ($first) {
|
|
$first = false;
|
|
} else {
|
|
print_r(", ");
|
|
}
|
|
print_r("\"$key\" => \"$val\"");
|
|
}
|
|
print_r("})");
|
|
$mapin = $testClient->testStringMap($mapout);
|
|
print_r(" = {");
|
|
$first = true;
|
|
foreach ($mapin as $key => $val) {
|
|
if ($first) {
|
|
$first = false;
|
|
} else {
|
|
print_r(", ");
|
|
}
|
|
print_r("\"$key\" => \"$val\"");
|
|
}
|
|
print_r("}\n");
|
|
ksort($mapin);
|
|
if ($mapin != $mapout) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_CONTAINERS;
|
|
}
|
|
|
|
/**
|
|
* SET TEST
|
|
*/
|
|
$setout = [];
|
|
for ($i = -2; $i < 3; ++$i) {
|
|
$setout[$i] = true;
|
|
}
|
|
print_r("testSet({");
|
|
echo implode(',', array_keys($setout));
|
|
print_r("})");
|
|
$setin = $testClient->testSet($setout);
|
|
print_r(" = {");
|
|
echo implode(', ', array_keys($setin));
|
|
print_r("}\n");
|
|
// Order of keys in set does not matter
|
|
ksort($setin);
|
|
if ($setout !== $setin) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_CONTAINERS;
|
|
}
|
|
// Regression test for corrupted array
|
|
if ($setin[2] !== $setout[2] || is_int($setin[2])) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_CONTAINERS;
|
|
}
|
|
|
|
/**
|
|
* LIST TEST
|
|
*/
|
|
$listout = [];
|
|
for ($i = -2; $i < 3; ++$i) {
|
|
$listout[] = $i;
|
|
}
|
|
print_r("testList({");
|
|
$first = true;
|
|
foreach ($listout as $val) {
|
|
if ($first) {
|
|
$first = false;
|
|
} else {
|
|
print_r(", ");
|
|
}
|
|
print_r($val);
|
|
}
|
|
print_r("})");
|
|
$listin = $testClient->testList($listout);
|
|
print_r(" = {");
|
|
$first = true;
|
|
foreach ($listin as $val) {
|
|
if ($first) {
|
|
$first = false;
|
|
} else {
|
|
print_r(", ");
|
|
}
|
|
print_r($val);
|
|
}
|
|
print_r("}\n");
|
|
if ($listin !== $listout) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_CONTAINERS;
|
|
}
|
|
|
|
/**
|
|
* ENUM TEST
|
|
*/
|
|
print_r("testEnum(ONE)");
|
|
$ret = $testClient->testEnum(\ThriftTest\Numberz::ONE);
|
|
print_r(" = $ret\n");
|
|
if ($ret != \ThriftTest\Numberz::ONE) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_STRUCTS;
|
|
}
|
|
|
|
print_r("testEnum(TWO)");
|
|
$ret = $testClient->testEnum(\ThriftTest\Numberz::TWO);
|
|
print_r(" = $ret\n");
|
|
if ($ret != \ThriftTest\Numberz::TWO) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_STRUCTS;
|
|
}
|
|
|
|
print_r("testEnum(THREE)");
|
|
$ret = $testClient->testEnum(\ThriftTest\Numberz::THREE);
|
|
print_r(" = $ret\n");
|
|
if ($ret != \ThriftTest\Numberz::THREE) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_STRUCTS;
|
|
}
|
|
|
|
print_r("testEnum(FIVE)");
|
|
$ret = $testClient->testEnum(\ThriftTest\Numberz::FIVE);
|
|
print_r(" = $ret\n");
|
|
if ($ret != \ThriftTest\Numberz::FIVE) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_STRUCTS;
|
|
}
|
|
|
|
print_r("testEnum(EIGHT)");
|
|
$ret = $testClient->testEnum(\ThriftTest\Numberz::EIGHT);
|
|
print_r(" = $ret\n");
|
|
if ($ret != \ThriftTest\Numberz::EIGHT) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_STRUCTS;
|
|
}
|
|
|
|
/**
|
|
* TYPEDEF TEST
|
|
*/
|
|
print_r("testTypedef(309858235082523)");
|
|
$uid = $testClient->testTypedef(309858235082523);
|
|
print_r(" = $uid\n");
|
|
if ($uid !== 309858235082523) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_STRUCTS;
|
|
}
|
|
|
|
/**
|
|
* NESTED MAP TEST
|
|
*/
|
|
print_r("testMapMap(1)");
|
|
$mm = $testClient->testMapMap(1);
|
|
print_r(" = {");
|
|
foreach ($mm as $key => $val) {
|
|
print_r("$key => {");
|
|
foreach ($val as $k2 => $v2) {
|
|
print_r("$k2 => $v2, ");
|
|
}
|
|
print_r("}, ");
|
|
}
|
|
print_r("}\n");
|
|
$expected_mm = [
|
|
-4 => [-4 => -4, -3 => -3, -2 => -2, -1 => -1],
|
|
4 => [4 => 4, 3 => 3, 2 => 2, 1 => 1],
|
|
];
|
|
if ($mm != $expected_mm) {
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_CONTAINERS;
|
|
}
|
|
|
|
/**
|
|
* INSANITY TEST
|
|
*/
|
|
$insane = new \ThriftTest\Insanity();
|
|
$insane->userMap[\ThriftTest\Numberz::FIVE] = 5000;
|
|
$truck = new \ThriftTest\Xtruct();
|
|
$truck->string_thing = "Truck";
|
|
$truck->byte_thing = 8;
|
|
$truck->i32_thing = 8;
|
|
$truck->i64_thing = 8;
|
|
$insane->xtructs[] = $truck;
|
|
print_r("testInsanity()");
|
|
$whoa = $testClient->testInsanity($insane);
|
|
print_r(" = {");
|
|
foreach ($whoa as $key => $val) {
|
|
print_r("$key => {");
|
|
foreach ($val as $k2 => $v2) {
|
|
print_r("$k2 => {");
|
|
$userMap = $v2->userMap;
|
|
print_r("{");
|
|
if (is_array($userMap)) {
|
|
foreach ($userMap as $k3 => $v3) {
|
|
print_r("$k3 => $v3, ");
|
|
}
|
|
}
|
|
print_r("}, ");
|
|
|
|
$xtructs = $v2->xtructs;
|
|
print_r("{");
|
|
if (is_array($xtructs)) {
|
|
foreach ($xtructs as $x) {
|
|
print_r("{\"" . $x->string_thing . "\", " .
|
|
$x->byte_thing . ", " . $x->i32_thing . ", " . $x->i64_thing . "}, ");
|
|
}
|
|
}
|
|
print_r("}");
|
|
|
|
print_r("}, ");
|
|
}
|
|
print_r("}, ");
|
|
}
|
|
print_r("}\n");
|
|
|
|
/**
|
|
* EXCEPTION TEST
|
|
*/
|
|
print_r("testException('Xception')");
|
|
try {
|
|
$testClient->testException('Xception');
|
|
print_r(" void\nFAILURE\n");
|
|
$exitcode |= ERR_EXCEPTIONS;
|
|
} catch (\ThriftTest\Xception $x) {
|
|
print_r(' caught xception ' . $x->errorCode . ': ' . $x->message . "\n");
|
|
}
|
|
|
|
// Regression test for THRIFT-4263
|
|
print_r("testBinarySerializer_Deserialize('foo')");
|
|
try {
|
|
\Thrift\Serializer\TBinarySerializer::deserialize(base64_decode('foo'), \ThriftTest\Xtruct2::class);
|
|
echo "**FAILED**\n";
|
|
$exitcode |= ERR_STRUCTS;
|
|
} catch (\Thrift\Exception\TTransportException $happy_exception) {
|
|
// We expected this due to binary data of base64_decode('foo') is less then 4
|
|
// bytes and it tries to find thrift version number in the transport by
|
|
// reading i32() at the beginning. Casting to string validates that
|
|
// exception is still accessible in memory and not corrupted. Without patch,
|
|
// PHP will error log that the exception doesn't have any tostring method,
|
|
// which is a lie due to corrupted memory.
|
|
for ($i = 99; $i > 0; $i--) {
|
|
(string) $happy_exception;
|
|
}
|
|
print_r(" SUCCESS\n");
|
|
}
|
|
|
|
/**
|
|
* Normal tests done.
|
|
*/
|
|
|
|
$stop = microtime(true);
|
|
$elp = round(1000 * ($stop - $start), 0);
|
|
print_r("Total time: $elp ms\n");
|
|
|
|
/**
|
|
* Extraneous "I don't trust PHP to pack/unpack integer" tests
|
|
*/
|
|
|
|
if ($protocol instanceof \Thrift\Protocol\TBinaryProtocolAccelerated) {
|
|
// Regression check: check that method name is not double-freed
|
|
// Method name should not be an interned string.
|
|
$method_name = "Void";
|
|
$method_name = "test$method_name";
|
|
|
|
$seqid = 0;
|
|
$args = new \ThriftTest\ThriftTest_testVoid_args();
|
|
thrift_protocol_write_binary($protocol, $method_name, \Thrift\Type\TMessageType::CALL, $args, $seqid, $protocol->isStrictWrite());
|
|
$testClient->recv_testVoid();
|
|
}
|
|
|
|
// Max I32
|
|
$num = pow(2, 30) + (pow(2, 30) - 1);
|
|
roundtrip($testClient, 'testI32', $num);
|
|
|
|
// Min I32
|
|
$num = 0 - pow(2, 31);
|
|
roundtrip($testClient, 'testI32', $num);
|
|
|
|
// Max I64
|
|
$num = pow(2, 62) + (pow(2, 62) - 1);
|
|
roundtrip($testClient, 'testI64', $num);
|
|
|
|
// Min I64
|
|
$num = 0 - pow(2, 62) - pow(2, 62);
|
|
roundtrip($testClient, 'testI64', $num);
|
|
|
|
$transport->close();
|
|
exit($exitcode);
|