1<?php
2
3namespace test\php;
4
5/** @var \Composer\Autoload\ClassLoader $loader */
6$loader = require __DIR__ . '/../../vendor/autoload.php';
7
8use Thrift\ClassLoader\ThriftClassLoader;
9
10if (!isset($GEN_DIR)) {
11  $GEN_DIR = 'gen-php';
12}
13if (!isset($MODE)) {
14  $MODE = 'normal';
15}
16
17
18if ($GEN_DIR == 'gen-php') {
19  $loader->addPsr4('', $GEN_DIR);
20} else {
21  $loader = new ThriftClassLoader();
22  $loader->registerDefinition('ThriftTest', $GEN_DIR);
23  $loader->register();
24}
25
26/*
27 * Licensed to the Apache Software Foundation (ASF) under one
28 * or more contributor license agreements. See the NOTICE file
29 * distributed with this work for additional information
30 * regarding copyright ownership. The ASF licenses this file
31 * to you under the Apache License, Version 2.0 (the
32 * "License"); you may not use this file except in compliance
33 * with the License. You may obtain a copy of the License at
34 *
35 *   http://www.apache.org/licenses/LICENSE-2.0
36 *
37 * Unless required by applicable law or agreed to in writing,
38 * software distributed under the License is distributed on an
39 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
40 * KIND, either express or implied. See the License for the
41 * specific language governing permissions and limitations
42 * under the License.
43 */
44
45/** Include the Thrift base */
46/** Include the protocols */
47use Thrift\Protocol\TBinaryProtocol;
48use Thrift\Protocol\TBinaryProtocolAccelerated;
49use Thrift\Protocol\TCompactProtocol;
50use Thrift\Protocol\TJSONProtocol;
51
52/** Include the socket layer */
53use Thrift\Transport\TSocket;
54use Thrift\Transport\TSocketPool;
55
56/** Include the socket layer */
57use Thrift\Transport\TFramedTransport;
58use Thrift\Transport\TBufferedTransport;
59
60function makeProtocol($transport, $PROTO)
61{
62  if ($PROTO == 'binary') {
63    return new TBinaryProtocol($transport);
64  } else if ($PROTO == 'compact') {
65    return new TCompactProtocol($transport);
66  } else if ($PROTO == 'json') {
67    return new TJSONProtocol($transport);
68  } else if ($PROTO == 'accel') {
69    if (!function_exists('thrift_protocol_write_binary')) {
70      echo "Acceleration extension is not loaded\n";
71      exit(1);
72    }
73    return new TBinaryProtocolAccelerated($transport);
74  }
75
76  echo "--protocol must be one of {binary|compact|json|accel}\n";
77  exit(1);
78}
79
80$host = 'localhost';
81$port = 9090;
82
83if ($argc > 1) {
84  $host = $argv[0];
85}
86
87if ($argc > 2) {
88  $host = $argv[1];
89}
90
91foreach ($argv as $arg) {
92  if (substr($arg, 0, 7) == '--port=') {
93    $port = substr($arg, 7);
94  } else if (substr($arg, 0, 12) == '--transport=') {
95    $MODE = substr($arg, 12);
96  } else if (substr($arg, 0, 11) == '--protocol=') {
97    $PROTO = substr($arg, 11);
98  }
99}
100
101$hosts = array('localhost');
102
103$socket = new TSocket($host, $port);
104$socket = new TSocketPool($hosts, $port);
105$socket->setDebug(TRUE);
106
107if ($MODE == 'inline') {
108  $transport = $socket;
109  $testClient = new \ThriftTest\ThriftTestClient($transport);
110} else if ($MODE == 'framed') {
111  $framedSocket = new TFramedTransport($socket);
112  $transport = $framedSocket;
113  $protocol = makeProtocol($transport, $PROTO);
114  $testClient = new \ThriftTest\ThriftTestClient($protocol);
115} else {
116  $bufferedSocket = new TBufferedTransport($socket, 1024, 1024);
117  $transport = $bufferedSocket;
118  $protocol = makeProtocol($transport, $PROTO);
119  $testClient = new \ThriftTest\ThriftTestClient($protocol);
120}
121
122$transport->open();
123
124$start = microtime(true);
125
126define('ERR_BASETYPES', 1);
127define('ERR_STRUCTS', 2);
128define('ERR_CONTAINERS', 4);
129define('ERR_EXCEPTIONS', 8);
130define('ERR_UNKNOWN', 64);
131$exitcode = 0;
132/**
133 * VOID TEST
134 */
135print_r("testVoid()");
136$testClient->testVoid();
137print_r(" = void\n");
138
139function roundtrip($testClient, $method, $value) {
140  global $exitcode;
141  print_r("$method($value)");
142  $ret = $testClient->$method($value);
143  print_r(" = \"$ret\"\n");
144  if ($value !== $ret) {
145    print_r("*** FAILED ***\n");
146    $exitcode |= ERR_BASETYPES;
147  }
148}
149
150/**
151 * STRING TEST
152 */
153roundtrip($testClient, 'testString', "Test");
154
155/**
156 * BOOL TEST
157 */
158roundtrip($testClient, 'testBool', true);
159roundtrip($testClient, 'testBool', false);
160
161/**
162 * BYTE TEST
163 */
164roundtrip($testClient, 'testByte', 1);
165roundtrip($testClient, 'testByte', -1);
166roundtrip($testClient, 'testByte', 127);
167roundtrip($testClient, 'testByte', -128);
168
169/**
170 * I32 TEST
171 */
172roundtrip($testClient, 'testI32', -1);
173
174/**
175 * I64 TEST
176 */
177roundtrip($testClient, 'testI64', 0);
178roundtrip($testClient, 'testI64', 1);
179roundtrip($testClient, 'testI64', -1);
180roundtrip($testClient, 'testI64', -34359738368);
181
182/**
183 * DOUBLE TEST
184 */
185roundtrip($testClient, 'testDouble', -852.234234234);
186
187/**
188 * BINARY TEST  --  TODO
189 */
190
191/**
192 * STRUCT TEST
193 */
194print_r("testStruct({\"Zero\", 1, -3, -5})");
195$out = new \ThriftTest\Xtruct();
196$out->string_thing = "Zero";
197$out->byte_thing = 1;
198$out->i32_thing = -3;
199$out->i64_thing = -5;
200$in = $testClient->testStruct($out);
201print_r(" = {\"".$in->string_thing."\", ".
202        $in->byte_thing.", ".
203        $in->i32_thing.", ".
204        $in->i64_thing."}\n");
205
206if ($in != $out) {
207    echo "**FAILED**\n";
208    $exitcode |= ERR_STRUCTS;
209}
210
211/**
212 * NESTED STRUCT TEST
213 */
214print_r("testNest({1, {\"Zero\", 1, -3, -5}), 5}");
215$out2 = new \ThriftTest\Xtruct2();
216$out2->byte_thing = 1;
217$out2->struct_thing = $out;
218$out2->i32_thing = 5;
219$in2 = $testClient->testNest($out2);
220$in = $in2->struct_thing;
221print_r(" = {".$in2->byte_thing.", {\"".
222        $in->string_thing."\", ".
223        $in->byte_thing.", ".
224        $in->i32_thing.", ".
225        $in->i64_thing."}, ".
226        $in2->i32_thing."}\n");
227
228if ($in2 != $out2) {
229    echo "**FAILED**\n";
230    $exitcode |= ERR_STRUCTS;
231}
232
233/**
234 * MAP TEST
235 */
236$mapout = array();
237for ($i = 0; $i < 5; ++$i) {
238  $mapout[$i] = $i-10;
239}
240print_r("testMap({");
241$first = true;
242foreach ($mapout as $key => $val) {
243  if ($first) {
244    $first = false;
245  } else {
246    print_r(", ");
247  }
248  print_r("$key => $val");
249}
250print_r("})");
251
252$mapin = $testClient->testMap($mapout);
253print_r(" = {");
254$first = true;
255foreach ($mapin as $key => $val) {
256  if ($first) {
257    $first = false;
258  } else {
259    print_r(", ");
260  }
261  print_r("$key => $val");
262}
263print_r("}\n");
264
265if ($mapin != $mapout) {
266    echo "**FAILED**\n";
267    $exitcode |= ERR_CONTAINERS;
268}
269
270$mapout = array();
271for ($i = 0; $i < 10; $i++) {
272    $mapout["key$i"] = "val$i";
273}
274print_r('testStringMap({');
275$first = true;
276foreach ($mapout as $key => $val) {
277  if ($first) {
278    $first = false;
279  } else {
280    print_r(", ");
281  }
282  print_r("\"$key\" => \"$val\"");
283}
284print_r("})");
285$mapin = $testClient->testStringMap($mapout);
286print_r(" = {");
287$first = true;
288foreach ($mapin as $key => $val) {
289  if ($first) {
290    $first = false;
291  } else {
292    print_r(", ");
293  }
294  print_r("\"$key\" => \"$val\"");
295}
296print_r("}\n");
297ksort($mapin);
298if ($mapin != $mapout) {
299    echo "**FAILED**\n";
300    $exitcode |= ERR_CONTAINERS;
301}
302
303/**
304 * SET TEST
305 */
306$setout = array();;
307for ($i = -2; $i < 3; ++$i) {
308  $setout[$i]= true;
309}
310print_r("testSet({");
311echo implode(',', array_keys($setout));
312print_r("})");
313$setin = $testClient->testSet($setout);
314print_r(" = {");
315echo implode(', ', array_keys($setin));
316print_r("}\n");
317// Order of keys in set does not matter
318ksort($setin);
319if ($setout !== $setin) {
320    echo "**FAILED**\n";
321    $exitcode |= ERR_CONTAINERS;
322}
323// Regression test for corrupted array
324if ($setin[2] !== $setout[2] || is_int($setin[2])) {
325    echo "**FAILED**\n";
326    $exitcode |= ERR_CONTAINERS;
327}
328
329/**
330 * LIST TEST
331 */
332$listout = array();
333for ($i = -2; $i < 3; ++$i) {
334  $listout []= $i;
335}
336print_r("testList({");
337$first = true;
338foreach ($listout as $val) {
339  if ($first) {
340    $first = false;
341  } else {
342    print_r(", ");
343  }
344  print_r($val);
345}
346print_r("})");
347$listin = $testClient->testList($listout);
348print_r(" = {");
349$first = true;
350foreach ($listin as $val) {
351  if ($first) {
352    $first = false;
353  } else {
354    print_r(", ");
355  }
356  print_r($val);
357}
358print_r("}\n");
359if ($listin !== $listout) {
360    echo "**FAILED**\n";
361    $exitcode |= ERR_CONTAINERS;
362}
363
364/**
365 * ENUM TEST
366 */
367print_r("testEnum(ONE)");
368$ret = $testClient->testEnum(\ThriftTest\Numberz::ONE);
369print_r(" = $ret\n");
370if ($ret != \ThriftTest\Numberz::ONE) {
371    echo "**FAILED**\n";
372    $exitcode |= ERR_STRUCTS;
373}
374
375print_r("testEnum(TWO)");
376$ret = $testClient->testEnum(\ThriftTest\Numberz::TWO);
377print_r(" = $ret\n");
378if ($ret != \ThriftTest\Numberz::TWO) {
379    echo "**FAILED**\n";
380    $exitcode |= ERR_STRUCTS;
381}
382
383print_r("testEnum(THREE)");
384$ret = $testClient->testEnum(\ThriftTest\Numberz::THREE);
385print_r(" = $ret\n");
386if ($ret != \ThriftTest\Numberz::THREE) {
387    echo "**FAILED**\n";
388    $exitcode |= ERR_STRUCTS;
389}
390
391print_r("testEnum(FIVE)");
392$ret = $testClient->testEnum(\ThriftTest\Numberz::FIVE);
393print_r(" = $ret\n");
394if ($ret != \ThriftTest\Numberz::FIVE) {
395    echo "**FAILED**\n";
396    $exitcode |= ERR_STRUCTS;
397}
398
399print_r("testEnum(EIGHT)");
400$ret = $testClient->testEnum(\ThriftTest\Numberz::EIGHT);
401print_r(" = $ret\n");
402if ($ret != \ThriftTest\Numberz::EIGHT) {
403    echo "**FAILED**\n";
404    $exitcode |= ERR_STRUCTS;
405}
406
407/**
408 * TYPEDEF TEST
409 */
410print_r("testTypedef(309858235082523)");
411$uid = $testClient->testTypedef(309858235082523);
412print_r(" = $uid\n");
413if ($uid !== 309858235082523) {
414    echo "**FAILED**\n";
415    $exitcode |= ERR_STRUCTS;
416}
417
418/**
419 * NESTED MAP TEST
420 */
421print_r("testMapMap(1)");
422$mm = $testClient->testMapMap(1);
423print_r(" = {");
424foreach ($mm as $key => $val) {
425  print_r("$key => {");
426  foreach ($val as $k2 => $v2) {
427    print_r("$k2 => $v2, ");
428  }
429  print_r("}, ");
430}
431print_r("}\n");
432$expected_mm = [
433  -4 => [-4 => -4, -3 => -3, -2 => -2, -1 => -1],
434  4 => [4 => 4, 3 => 3, 2 => 2, 1 => 1],
435];
436if ($mm != $expected_mm) {
437    echo "**FAILED**\n";
438    $exitcode |= ERR_CONTAINERS;
439}
440
441/**
442 * INSANITY TEST
443 */
444$insane = new \ThriftTest\Insanity();
445$insane->userMap[\ThriftTest\Numberz::FIVE] = 5000;
446$truck = new \ThriftTest\Xtruct();
447$truck->string_thing = "Truck";
448$truck->byte_thing = 8;
449$truck->i32_thing = 8;
450$truck->i64_thing = 8;
451$insane->xtructs []= $truck;
452print_r("testInsanity()");
453$whoa = $testClient->testInsanity($insane);
454print_r(" = {");
455foreach ($whoa as $key => $val) {
456  print_r("$key => {");
457  foreach ($val as $k2 => $v2) {
458    print_r("$k2 => {");
459    $userMap = $v2->userMap;
460    print_r("{");
461    if (is_array($userMap)) {
462      foreach ($userMap as $k3 => $v3) {
463        print_r("$k3 => $v3, ");
464      }
465    }
466    print_r("}, ");
467
468    $xtructs = $v2->xtructs;
469    print_r("{");
470    if (is_array($xtructs)) {
471      foreach ($xtructs as $x) {
472        print_r("{\"".$x->string_thing."\", ".
473                $x->byte_thing.", ".$x->i32_thing.", ".$x->i64_thing."}, ");
474      }
475    }
476    print_r("}");
477
478    print_r("}, ");
479  }
480  print_r("}, ");
481}
482print_r("}\n");
483
484/**
485 * EXCEPTION TEST
486 */
487print_r("testException('Xception')");
488try {
489  $testClient->testException('Xception');
490  print_r("  void\nFAILURE\n");
491  $exitcode |= ERR_EXCEPTIONS;
492} catch (\ThriftTest\Xception $x) {
493  print_r(' caught xception '.$x->errorCode.': '.$x->message."\n");
494}
495
496// Regression test for THRIFT-4263
497print_r("testBinarySerializer_Deserialize('foo')");
498try {
499  \Thrift\Serializer\TBinarySerializer::deserialize(base64_decode('foo'), \ThriftTest\Xtruct2::class);
500  echo "**FAILED**\n";
501  $exitcode |= ERR_STRUCTS;
502} catch (\Thrift\Exception\TTransportException $happy_exception) {
503  // We expected this due to binary data of base64_decode('foo') is less then 4
504  // bytes and it tries to find thrift version number in the transport by
505  // reading i32() at the beginning.  Casting to string validates that
506  // exception is still accessible in memory and not corrupted.  Without patch,
507  // PHP will error log that the exception doesn't have any tostring method,
508  // which is a lie due to corrupted memory.
509  for($i=99; $i > 0; $i--) {
510    (string)$happy_exception;
511  }
512  print_r("  SUCCESS\n");
513}
514
515/**
516 * Normal tests done.
517 */
518
519$stop = microtime(true);
520$elp = round(1000*($stop - $start), 0);
521print_r("Total time: $elp ms\n");
522
523/**
524 * Extraneous "I don't trust PHP to pack/unpack integer" tests
525 */
526
527if ($protocol instanceof TBinaryProtocolAccelerated) {
528    // Regression check: check that method name is not double-freed
529    // Method name should not be an interned string.
530    $method_name = "Void";
531    $method_name = "test$method_name";
532
533    $seqid = 0;
534    $args = new \ThriftTest\ThriftTest_testVoid_args();
535    thrift_protocol_write_binary($protocol, $method_name, \Thrift\Type\TMessageType::CALL, $args, $seqid, $protocol->isStrictWrite());
536    $testClient->recv_testVoid();
537
538}
539
540// Max I32
541$num = pow(2, 30) + (pow(2, 30) - 1);
542roundtrip($testClient, 'testI32', $num);
543
544// Min I32
545$num = 0 - pow(2, 31);
546roundtrip($testClient, 'testI32', $num);
547
548// Max I64
549$num = pow(2, 62) + (pow(2, 62) - 1);
550roundtrip($testClient, 'testI64', $num);
551
552// Min I64
553$num = 0 - pow(2, 62) - pow(2, 62);
554roundtrip($testClient, 'testI64', $num);
555
556$transport->close();
557exit($exitcode);
558