Subversion Repositories SmartDukaan

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
30 ashish 1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements. See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership. The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License. You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied. See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
 
20
#ifdef HAVE_CONFIG_H
21
#include "config.h"
22
#endif
23
 
24
#include <sys/types.h>
25
#include <netinet/in.h>
26
#include <unistd.h>
27
#include <endian.h>
28
#include <byteswap.h>
29
#include <stdexcept>
30
 
31
#if __BYTE_ORDER == __LITTLE_ENDIAN
32
#define htonll(x) bswap_64(x)
33
#define ntohll(x) bswap_64(x)
34
#else
35
#define htonll(x) x
36
#define ntohll(x) x
37
#endif
38
 
39
enum TType {
40
  T_STOP       = 0,
41
  T_VOID       = 1,
42
  T_BOOL       = 2,
43
  T_BYTE       = 3,
44
  T_I08        = 3,
45
  T_I16        = 6,
46
  T_I32        = 8,
47
  T_U64        = 9,
48
  T_I64        = 10,
49
  T_DOUBLE     = 4,
50
  T_STRING     = 11,
51
  T_UTF7       = 11,
52
  T_STRUCT     = 12,
53
  T_MAP        = 13,
54
  T_SET        = 14,
55
  T_LIST       = 15,
56
  T_UTF8       = 16,
57
  T_UTF16      = 17
58
};
59
 
60
const int32_t VERSION_MASK = 0xffff0000;
61
const int32_t VERSION_1 = 0x80010000;
62
const int8_t T_CALL = 1;
63
const int8_t T_REPLY = 2;
64
const int8_t T_EXCEPTION = 3;
65
// tprotocolexception
66
const int INVALID_DATA = 1;
67
const int BAD_VERSION = 4;
68
 
69
#include "php.h"
70
#include "zend_interfaces.h"
71
#include "zend_exceptions.h"
72
#include "php_thrift_protocol.h"
73
 
74
static function_entry thrift_protocol_functions[] = {
75
  PHP_FE(thrift_protocol_write_binary, NULL)
76
  PHP_FE(thrift_protocol_read_binary, NULL)
77
  {NULL, NULL, NULL}
78
} ;
79
 
80
zend_module_entry thrift_protocol_module_entry = {
81
  STANDARD_MODULE_HEADER,
82
  "thrift_protocol",
83
  thrift_protocol_functions,
84
  NULL,
85
  NULL,
86
  NULL,
87
  NULL,
88
  NULL,
89
  "1.0",
90
  STANDARD_MODULE_PROPERTIES
91
};
92
 
93
#ifdef COMPILE_DL_THRIFT_PROTOCOL
94
ZEND_GET_MODULE(thrift_protocol)
95
#endif
96
 
97
class PHPExceptionWrapper : public std::exception {
98
public:
99
  PHPExceptionWrapper(zval* _ex) throw() : ex(_ex) {
100
    snprintf(_what, 40, "PHP exception zval=%p", ex);
101
  }
102
  const char* what() const throw() { return _what; }
103
  ~PHPExceptionWrapper() throw() {}
104
  operator zval*() const throw() { return const_cast<zval*>(ex); } // Zend API doesn't do 'const'...
105
protected:
106
  zval* ex;
107
  char _what[40];
108
} ;
109
 
110
class PHPTransport {
111
public:
112
  zval* protocol() { return p; }
113
  zval* transport() { return t; }
114
protected:
115
  PHPTransport() {}
116
 
117
  void construct_with_zval(zval* _p, size_t _buffer_size) {
118
    buffer = reinterpret_cast<char*>(emalloc(_buffer_size));
119
    buffer_ptr = buffer;
120
    buffer_used = 0;
121
    buffer_size = _buffer_size;
122
    p = _p;
123
 
124
    // Get the transport for the passed protocol
125
    zval gettransport;
126
    ZVAL_STRING(&gettransport, "getTransport", 0);
127
    MAKE_STD_ZVAL(t);
128
    ZVAL_NULL(t);
129
    TSRMLS_FETCH();
130
    call_user_function(EG(function_table), &p, &gettransport, t, 0, NULL TSRMLS_CC);
131
  }
132
  ~PHPTransport() {
133
    efree(buffer);
134
    zval_ptr_dtor(&t);
135
  }
136
 
137
  char* buffer;
138
  char* buffer_ptr;
139
  size_t buffer_used;
140
  size_t buffer_size;
141
 
142
  zval* p;
143
  zval* t;
144
};
145
 
146
 
147
class PHPOutputTransport : public PHPTransport {
148
public:
149
  PHPOutputTransport(zval* _p, size_t _buffer_size = 8192) {
150
    construct_with_zval(_p, _buffer_size);
151
  }
152
 
153
  ~PHPOutputTransport() {
154
    flush();
155
    directFlush();
156
  }
157
 
158
  void write(const char* data, size_t len) {
159
    if ((len + buffer_used) > buffer_size) {
160
      flush();
161
    }
162
    if (len > buffer_size) {
163
      directWrite(data, len);
164
    } else {
165
      memcpy(buffer_ptr, data, len);
166
      buffer_used += len;
167
      buffer_ptr += len;
168
    }
169
  }
170
 
171
  void writeI64(int64_t i) {
172
    i = htonll(i);
173
    write((const char*)&i, 8);
174
  }
175
 
176
  void writeU32(uint32_t i) {
177
    i = htonl(i);
178
    write((const char*)&i, 4);
179
  }
180
 
181
  void writeI32(int32_t i) {
182
    i = htonl(i);
183
    write((const char*)&i, 4);
184
  }
185
 
186
  void writeI16(int16_t i) {
187
    i = htons(i);
188
    write((const char*)&i, 2);
189
  }
190
 
191
  void writeI8(int8_t i) {
192
    write((const char*)&i, 1);
193
  }
194
 
195
  void writeString(const char* str, size_t len) {
196
    writeU32(len);
197
    write(str, len);
198
  }
199
 
200
  void flush() {
201
    if (buffer_used) {
202
      directWrite(buffer, buffer_used);
203
      buffer_ptr = buffer;
204
      buffer_used = 0;
205
    }
206
  }
207
 
208
protected:
209
  void directFlush() {
210
    zval ret;
211
    ZVAL_NULL(&ret);
212
    zval flushfn;
213
    ZVAL_STRING(&flushfn, "flush", 0);
214
    TSRMLS_FETCH();
215
    call_user_function(EG(function_table), &t, &flushfn, &ret, 0, NULL TSRMLS_CC);
216
    zval_dtor(&ret);
217
  }
218
  void directWrite(const char* data, size_t len) {
219
    zval writefn;
220
    ZVAL_STRING(&writefn, "write", 0);
221
    char* newbuf = (char*)emalloc(buffer_used + 1);
222
    memcpy(newbuf, buffer, buffer_used);
223
    newbuf[buffer_used] = '\0';
224
    zval *args[1];
225
    MAKE_STD_ZVAL(args[0]);
226
    ZVAL_STRINGL(args[0], newbuf, buffer_used, 0);
227
    TSRMLS_FETCH();
228
    zval ret;
229
    ZVAL_NULL(&ret);
230
    call_user_function(EG(function_table), &t, &writefn, &ret, 1, args TSRMLS_CC);
231
    zval_ptr_dtor(args);
232
    zval_dtor(&ret);
233
    if (EG(exception)) {
234
      zval* ex = EG(exception);
235
      EG(exception) = NULL;
236
      throw PHPExceptionWrapper(ex);
237
    }
238
  }
239
};
240
 
241
class PHPInputTransport : public PHPTransport {
242
public:
243
  PHPInputTransport(zval* _p, size_t _buffer_size = 8192) {
244
    construct_with_zval(_p, _buffer_size);
245
  }
246
 
247
  ~PHPInputTransport() {
248
    put_back();
249
  }
250
 
251
  void put_back() {
252
    if (buffer_used) {
253
      zval putbackfn;
254
      ZVAL_STRING(&putbackfn, "putBack", 0);
255
 
256
      char* newbuf = (char*)emalloc(buffer_used + 1);
257
      memcpy(newbuf, buffer_ptr, buffer_used);
258
      newbuf[buffer_used] = '\0';
259
 
260
      zval *args[1];
261
      MAKE_STD_ZVAL(args[0]);
262
      ZVAL_STRINGL(args[0], newbuf, buffer_used, 0);
263
 
264
      TSRMLS_FETCH();
265
 
266
      zval ret;
267
      ZVAL_NULL(&ret);
268
      call_user_function(EG(function_table), &t, &putbackfn, &ret, 1, args TSRMLS_CC);
269
      zval_ptr_dtor(args);
270
      zval_dtor(&ret);
271
    }
272
    buffer_used = 0;
273
    buffer_ptr = buffer;
274
  }
275
 
276
  void skip(size_t len) {
277
    while (len) {
278
      size_t chunk_size = MIN(len, buffer_used);
279
      if (chunk_size) {
280
        buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
281
        buffer_used -= chunk_size;
282
        len -= chunk_size;
283
      }
284
      if (! len) break;
285
      refill();
286
    }
287
  }
288
 
289
  void readBytes(void* buf, size_t len) {
290
    while (len) {
291
      size_t chunk_size = MIN(len, buffer_used);
292
      if (chunk_size) {
293
        memcpy(buf, buffer_ptr, chunk_size);
294
        buffer_ptr = reinterpret_cast<char*>(buffer_ptr) + chunk_size;
295
        buffer_used -= chunk_size;
296
        buf = reinterpret_cast<char*>(buf) + chunk_size;
297
        len -= chunk_size;
298
      }
299
      if (! len) break;
300
      refill();
301
    }
302
  }
303
 
304
  int8_t readI8() {
305
    int8_t c;
306
    readBytes(&c, 1);
307
    return c;
308
  }
309
 
310
  int16_t readI16() {
311
    int16_t c;
312
    readBytes(&c, 2);
313
    return (int16_t)ntohs(c);
314
  }
315
 
316
  uint32_t readU32() {
317
    uint32_t c;
318
    readBytes(&c, 4);
319
    return (uint32_t)ntohl(c);
320
  }
321
 
322
  int32_t readI32() {
323
    int32_t c;
324
    readBytes(&c, 4);
325
    return (int32_t)ntohl(c);
326
  }
327
 
328
protected:
329
  void refill() {
330
    assert(buffer_used == 0);
331
    zval retval;
332
    ZVAL_NULL(&retval);
333
 
334
    zval *args[1];
335
    MAKE_STD_ZVAL(args[0]);
336
    ZVAL_LONG(args[0], buffer_size);
337
 
338
    TSRMLS_FETCH();
339
 
340
    zval funcname;
341
    ZVAL_STRING(&funcname, "read", 0);
342
 
343
    call_user_function(EG(function_table), &t, &funcname, &retval, 1, args TSRMLS_CC);
344
    zval_ptr_dtor(args);
345
 
346
    if (EG(exception)) {
347
      zval_dtor(&retval);
348
      zval* ex = EG(exception);
349
      EG(exception) = NULL;
350
      throw PHPExceptionWrapper(ex);
351
    }
352
 
353
    buffer_used = Z_STRLEN(retval);
354
    memcpy(buffer, Z_STRVAL(retval), buffer_used);
355
    zval_dtor(&retval);
356
 
357
    buffer_ptr = buffer;
358
  }
359
 
360
};
361
 
362
void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec);
363
void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec);
364
void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec);
365
void skip_element(long thrift_typeID, PHPInputTransport& transport);
366
 
367
// Create a PHP object given a typename and call the ctor, optionally passing up to 2 arguments
368
void createObject(char* obj_typename, zval* return_value, int nargs = 0, zval* arg1 = NULL, zval* arg2 = NULL) {
369
  TSRMLS_FETCH();
370
  size_t obj_typename_len = strlen(obj_typename);
371
  zend_class_entry* ce = zend_fetch_class(obj_typename, obj_typename_len, ZEND_FETCH_CLASS_DEFAULT TSRMLS_CC);
372
  if (! ce) {
373
    php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s does not exist", obj_typename);
374
    RETURN_NULL();
375
  }
376
 
377
  object_and_properties_init(return_value, ce, NULL);
378
  zend_function* constructor = zend_std_get_constructor(return_value TSRMLS_CC);
379
  zval* ctor_rv = NULL;
380
  zend_call_method(&return_value, ce, &constructor, NULL, 0, &ctor_rv, nargs, arg1, arg2 TSRMLS_CC);
381
  zval_ptr_dtor(&ctor_rv);
382
}
383
 
384
void throw_tprotocolexception(char* what, long errorcode) {
385
  TSRMLS_FETCH();
386
 
387
  zval *zwhat, *zerrorcode;
388
  MAKE_STD_ZVAL(zwhat);
389
  MAKE_STD_ZVAL(zerrorcode);
390
 
391
  ZVAL_STRING(zwhat, what, 1);
392
  ZVAL_LONG(zerrorcode, errorcode);
393
 
394
  zval* ex;
395
  MAKE_STD_ZVAL(ex);
396
  createObject("TProtocolException", ex, 2, zwhat, zerrorcode);
397
  zval_ptr_dtor(&zwhat);
398
  zval_ptr_dtor(&zerrorcode);
399
  throw PHPExceptionWrapper(ex);
400
}
401
 
402
void binary_deserialize(int8_t thrift_typeID, PHPInputTransport& transport, zval* return_value, HashTable* fieldspec) {
403
  zval** val_ptr;
404
  Z_TYPE_P(return_value) = IS_NULL; // just in case
405
 
406
  switch (thrift_typeID) {
407
    case T_STOP:
408
    case T_VOID:
409
      RETURN_NULL();
410
      return;
411
    case T_STRUCT: {
412
      if (zend_hash_find(fieldspec, "class", 6, (void**)&val_ptr) != SUCCESS) {
413
        throw_tprotocolexception("no class type in spec", INVALID_DATA);
414
        skip_element(T_STRUCT, transport);
415
        RETURN_NULL();
416
      }
417
      char* structType = Z_STRVAL_PP(val_ptr);
418
      createObject(structType, return_value);
419
      if (Z_TYPE_P(return_value) == IS_NULL) {
420
        // unable to create class entry
421
        skip_element(T_STRUCT, transport);
422
        RETURN_NULL();
423
      }
424
      TSRMLS_FETCH();
425
      zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
426
      if (Z_TYPE_P(spec) != IS_ARRAY) {
427
        char errbuf[128];
428
        snprintf(errbuf, 128, "spec for %s is wrong type: %d\n", structType, Z_TYPE_P(spec));
429
        throw_tprotocolexception(errbuf, INVALID_DATA);
430
        RETURN_NULL();
431
      }
432
      binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
433
      return;
434
    } break;
435
    case T_BOOL: {
436
      uint8_t c;
437
      transport.readBytes(&c, 1);
438
      RETURN_BOOL(c != 0);
439
    }
440
  //case T_I08: // same numeric value as T_BYTE
441
    case T_BYTE: {
442
      uint8_t c;
443
      transport.readBytes(&c, 1);
444
      RETURN_LONG((int8_t)c);
445
    }
446
    case T_I16: {
447
      uint16_t c;
448
      transport.readBytes(&c, 2);
449
      RETURN_LONG((int16_t)ntohs(c));
450
    }
451
    case T_I32: {
452
      uint32_t c;
453
      transport.readBytes(&c, 4);
454
      RETURN_LONG((int32_t)ntohl(c));
455
    }
456
    case T_U64:
457
    case T_I64: {
458
      uint64_t c;
459
      transport.readBytes(&c, 8);
460
      RETURN_LONG((int64_t)ntohll(c));
461
    }
462
    case T_DOUBLE: {
463
      union {
464
        uint64_t c;
465
        double d;
466
      } a;
467
      transport.readBytes(&(a.c), 8);
468
      a.c = ntohll(a.c);
469
      RETURN_DOUBLE(a.d);
470
    }
471
    //case T_UTF7: // aliases T_STRING
472
    case T_UTF8:
473
    case T_UTF16:
474
    case T_STRING: {
475
      uint32_t size = transport.readU32();
476
      if (size) {
477
        char* strbuf = (char*) emalloc(size + 1);
478
        transport.readBytes(strbuf, size);
479
        strbuf[size] = '\0';
480
        ZVAL_STRINGL(return_value, strbuf, size, 0);
481
      } else {
482
        ZVAL_EMPTY_STRING(return_value);
483
      }
484
      return;
485
    }
486
    case T_MAP: { // array of key -> value
487
      uint8_t types[2];
488
      transport.readBytes(types, 2);
489
      uint32_t size = transport.readU32();
490
      array_init(return_value);
491
 
492
      zend_hash_find(fieldspec, "key", 4, (void**)&val_ptr);
493
      HashTable* keyspec = Z_ARRVAL_PP(val_ptr);
494
      zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr);
495
      HashTable* valspec = Z_ARRVAL_PP(val_ptr);
496
 
497
      for (uint32_t s = 0; s < size; ++s) {
498
        zval *value;
499
        MAKE_STD_ZVAL(value);
500
 
501
        zval* key;
502
        MAKE_STD_ZVAL(key);
503
 
504
        binary_deserialize(types[0], transport, key, keyspec);
505
        binary_deserialize(types[1], transport, value, valspec);
506
        if (Z_TYPE_P(key) == IS_LONG) {
507
          zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
508
        }
509
        else {
510
          if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key);
511
          zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
512
        }
513
        zval_ptr_dtor(&key);
514
      }
515
      return; // return_value already populated
516
    }
517
    case T_LIST: { // array with autogenerated numeric keys
518
      int8_t type = transport.readI8();
519
      uint32_t size = transport.readU32();
520
      zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
521
      HashTable* elemspec = Z_ARRVAL_PP(val_ptr);
522
 
523
      array_init(return_value);
524
      for (uint32_t s = 0; s < size; ++s) {
525
        zval *value;
526
        MAKE_STD_ZVAL(value);
527
        binary_deserialize(type, transport, value, elemspec);
528
        zend_hash_next_index_insert(return_value->value.ht, &value, sizeof(zval *), NULL);
529
      }
530
      return;
531
    }
532
    case T_SET: { // array of key -> TRUE
533
      uint8_t type;
534
      uint32_t size;
535
      transport.readBytes(&type, 1);
536
      transport.readBytes(&size, 4);
537
      size = ntohl(size);
538
      zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
539
      HashTable* elemspec = Z_ARRVAL_PP(val_ptr);
540
 
541
      array_init(return_value);
542
 
543
      for (uint32_t s = 0; s < size; ++s) {
544
        zval* key;
545
        zval* value;
546
        MAKE_STD_ZVAL(key);
547
        MAKE_STD_ZVAL(value);
548
        ZVAL_TRUE(value);
549
 
550
        binary_deserialize(type, transport, key, elemspec);
551
 
552
        if (Z_TYPE_P(key) == IS_LONG) {
553
          zend_hash_index_update(return_value->value.ht, Z_LVAL_P(key), &value, sizeof(zval *), NULL);
554
        }
555
        else {
556
          if (Z_TYPE_P(key) != IS_STRING) convert_to_string(key);
557
          zend_hash_update(return_value->value.ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &value, sizeof(zval *), NULL);
558
        }
559
        zval_ptr_dtor(&key);
560
      }
561
      return;
562
    }
563
  };
564
 
565
  char errbuf[128];
566
  sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
567
  throw_tprotocolexception(errbuf, INVALID_DATA);
568
}
569
 
570
void skip_element(long thrift_typeID, PHPInputTransport& transport) {
571
  switch (thrift_typeID) {
572
    case T_STOP:
573
    case T_VOID:
574
      return;
575
    case T_STRUCT:
576
      while (true) {
577
        int8_t ttype = transport.readI8(); // get field type
578
        if (ttype == T_STOP) break;
579
        transport.skip(2); // skip field number, I16
580
        skip_element(ttype, transport); // skip field payload
581
      }
582
      return;
583
    case T_BOOL:
584
    case T_BYTE:
585
      transport.skip(1);
586
      return;
587
    case T_I16:
588
      transport.skip(2);
589
      return;
590
    case T_I32:
591
      transport.skip(4);
592
      return;
593
    case T_U64:
594
    case T_I64:
595
    case T_DOUBLE:
596
      transport.skip(8);
597
      return;
598
    //case T_UTF7: // aliases T_STRING
599
    case T_UTF8:
600
    case T_UTF16:
601
    case T_STRING: {
602
      uint32_t len = transport.readU32();
603
      transport.skip(len);
604
      } return;
605
    case T_MAP: {
606
      int8_t keytype = transport.readI8();
607
      int8_t valtype = transport.readI8();
608
      uint32_t size = transport.readU32();
609
      for (uint32_t i = 0; i < size; ++i) {
610
        skip_element(keytype, transport);
611
        skip_element(valtype, transport);
612
      }
613
    } return;
614
    case T_LIST:
615
    case T_SET: {
616
      int8_t valtype = transport.readI8();
617
      uint32_t size = transport.readU32();
618
      for (uint32_t i = 0; i < size; ++i) {
619
        skip_element(valtype, transport);
620
      }
621
    } return;
622
  };
623
 
624
  char errbuf[128];
625
  sprintf(errbuf, "Unknown thrift typeID %ld", thrift_typeID);
626
  throw_tprotocolexception(errbuf, INVALID_DATA);
627
}
628
 
629
void binary_serialize_hashtable_key(int8_t keytype, PHPOutputTransport& transport, HashTable* ht, HashPosition& ht_pos) {
630
  bool keytype_is_numeric = (!((keytype == T_STRING) || (keytype == T_UTF8) || (keytype == T_UTF16)));
631
 
632
  char* key;
633
  uint key_len;
634
  long index = 0;
635
 
636
  zval* z;
637
  MAKE_STD_ZVAL(z);
638
 
639
  int res = zend_hash_get_current_key_ex(ht, &key, &key_len, (ulong*)&index, 0, &ht_pos);
640
  if (keytype_is_numeric) {
641
    if (res == HASH_KEY_IS_STRING) {
642
      index = strtol(key, NULL, 10);
643
    }
644
    ZVAL_LONG(z, index);
645
  } else {
646
    char buf[64];
647
    if (res == HASH_KEY_IS_STRING) {
648
      key_len -= 1; // skip the null terminator
649
    } else {
650
      sprintf(buf, "%ld", index);
651
      key = buf; key_len = strlen(buf);
652
    }
653
    ZVAL_STRINGL(z, key, key_len, 1);
654
  }
655
  binary_serialize(keytype, transport, &z, NULL);
656
  zval_ptr_dtor(&z);
657
}
658
 
659
inline bool ttype_is_int(int8_t t) {
660
  return ((t == T_BYTE) || ((t >= T_I16)  && (t <= T_I64)));
661
}
662
 
663
inline bool ttypes_are_compatible(int8_t t1, int8_t t2) {
664
  // Integer types of different widths are considered compatible;
665
  // otherwise the typeID must match.
666
  return ((t1 == t2) || (ttype_is_int(t1) && ttype_is_int(t2)));
667
}
668
 
669
void binary_deserialize_spec(zval* zthis, PHPInputTransport& transport, HashTable* spec) {
670
  // SET and LIST have 'elem' => array('type', [optional] 'class')
671
  // MAP has 'val' => array('type', [optiona] 'class')
672
  TSRMLS_FETCH();
673
  zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC);
674
  while (true) {
675
    zval** val_ptr = NULL;
676
 
677
    int8_t ttype = transport.readI8();
678
    if (ttype == T_STOP) return;
679
    int16_t fieldno = transport.readI16();
680
    if (zend_hash_index_find(spec, fieldno, (void**)&val_ptr) == SUCCESS) {
681
      HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
682
      // pull the field name
683
      // zend hash tables use the null at the end in the length... so strlen(hash key) + 1.
684
      zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr);
685
      char* varname = Z_STRVAL_PP(val_ptr);
686
 
687
      // and the type
688
      zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr);
689
      if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
690
      int8_t expected_ttype = Z_LVAL_PP(val_ptr);
691
 
692
      if (ttypes_are_compatible(ttype, expected_ttype)) {
693
        zval* rv = NULL;
694
        MAKE_STD_ZVAL(rv);
695
        binary_deserialize(ttype, transport, rv, fieldspec);
696
        zend_update_property(ce, zthis, varname, strlen(varname), rv TSRMLS_CC);
697
        zval_ptr_dtor(&rv);
698
      } else {
699
        skip_element(ttype, transport);
700
      }
701
    } else {
702
      skip_element(ttype, transport);
703
    }
704
  }
705
}
706
 
707
void binary_serialize(int8_t thrift_typeID, PHPOutputTransport& transport, zval** value, HashTable* fieldspec) {
708
  // At this point the typeID (and field num, if applicable) should've already been written to the output so all we need to do is write the payload.
709
  switch (thrift_typeID) {
710
    case T_STOP:
711
    case T_VOID:
712
      return;
713
    case T_STRUCT: {
714
      TSRMLS_FETCH();
715
      if (Z_TYPE_PP(value) != IS_OBJECT) {
716
        throw_tprotocolexception("Attempt to send non-object type as a T_STRUCT", INVALID_DATA);
717
      }
718
      zval* spec = zend_read_static_property(zend_get_class_entry(*value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
719
      if (Z_TYPE_P(spec) != IS_ARRAY) {
720
        throw_tprotocolexception("Attempt to send non-Thrift object as a T_STRUCT", INVALID_DATA);
721
      }
722
      binary_serialize_spec(*value, transport, Z_ARRVAL_P(spec));
723
    } return;
724
    case T_BOOL:
725
      if (Z_TYPE_PP(value) != IS_BOOL) convert_to_boolean(*value);
726
      transport.writeI8(Z_BVAL_PP(value) ? 1 : 0);
727
      return;
728
    case T_BYTE:
729
      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
730
      transport.writeI8(Z_LVAL_PP(value));
731
      return;
732
    case T_I16:
733
      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
734
      transport.writeI16(Z_LVAL_PP(value));
735
      return;
736
    case T_I32:
737
      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
738
      transport.writeI32(Z_LVAL_PP(value));
739
      return;
740
    case T_I64:
741
    case T_U64:
742
      if (Z_TYPE_PP(value) != IS_LONG) convert_to_long(*value);
743
      transport.writeI64(Z_LVAL_PP(value));
744
      return;
745
    case T_DOUBLE: {
746
      union {
747
        int64_t c;
748
        double d;
749
      } a;
750
      if (Z_TYPE_PP(value) != IS_DOUBLE) convert_to_double(*value);
751
      a.d = Z_DVAL_PP(value);
752
      transport.writeI64(a.c);
753
    } return;
754
    //case T_UTF7:
755
    case T_UTF8:
756
    case T_UTF16:
757
    case T_STRING:
758
      if (Z_TYPE_PP(value) != IS_STRING) convert_to_string(*value);
759
      transport.writeString(Z_STRVAL_PP(value), Z_STRLEN_PP(value));
760
      return;
761
    case T_MAP: {
762
      if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value);
763
      if (Z_TYPE_PP(value) != IS_ARRAY) {
764
        throw_tprotocolexception("Attempt to send an incompatible type as an array (T_MAP)", INVALID_DATA);
765
      }
766
      HashTable* ht = Z_ARRVAL_PP(value);
767
      zval** val_ptr;
768
 
769
      zend_hash_find(fieldspec, "ktype", 6, (void**)&val_ptr);
770
      if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
771
      uint8_t keytype = Z_LVAL_PP(val_ptr);
772
      transport.writeI8(keytype);
773
      zend_hash_find(fieldspec, "vtype", 6, (void**)&val_ptr);
774
      if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
775
      uint8_t valtype = Z_LVAL_PP(val_ptr);
776
      transport.writeI8(valtype);
777
 
778
      zend_hash_find(fieldspec, "val", 4, (void**)&val_ptr);
779
      HashTable* valspec = Z_ARRVAL_PP(val_ptr);
780
 
781
      transport.writeI32(zend_hash_num_elements(ht));
782
      HashPosition key_ptr;
783
      for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) {
784
        binary_serialize_hashtable_key(keytype, transport, ht, key_ptr);
785
        binary_serialize(valtype, transport, val_ptr, valspec);
786
      }
787
    } return;
788
    case T_LIST: {
789
      if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value);
790
      if (Z_TYPE_PP(value) != IS_ARRAY) {
791
        throw_tprotocolexception("Attempt to send an incompatible type as an array (T_LIST)", INVALID_DATA);
792
      }
793
      HashTable* ht = Z_ARRVAL_PP(value);
794
      zval** val_ptr;
795
 
796
      zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr);
797
      if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
798
      uint8_t valtype = Z_LVAL_PP(val_ptr);
799
      transport.writeI8(valtype);
800
 
801
      zend_hash_find(fieldspec, "elem", 5, (void**)&val_ptr);
802
      HashTable* valspec = Z_ARRVAL_PP(val_ptr);
803
 
804
      transport.writeI32(zend_hash_num_elements(ht));
805
      HashPosition key_ptr;
806
      for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) {
807
        binary_serialize(valtype, transport, val_ptr, valspec);
808
      }
809
    } return;
810
    case T_SET: {
811
      if (Z_TYPE_PP(value) != IS_ARRAY) convert_to_array(*value);
812
      if (Z_TYPE_PP(value) != IS_ARRAY) {
813
        throw_tprotocolexception("Attempt to send an incompatible type as an array (T_SET)", INVALID_DATA);
814
      }
815
      HashTable* ht = Z_ARRVAL_PP(value);
816
      zval** val_ptr;
817
 
818
      zend_hash_find(fieldspec, "etype", 6, (void**)&val_ptr);
819
      if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
820
      uint8_t keytype = Z_LVAL_PP(val_ptr);
821
      transport.writeI8(keytype);
822
 
823
      transport.writeI32(zend_hash_num_elements(ht));
824
      HashPosition key_ptr;
825
      for (zend_hash_internal_pointer_reset_ex(ht, &key_ptr); zend_hash_get_current_data_ex(ht, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(ht, &key_ptr)) {
826
        binary_serialize_hashtable_key(keytype, transport, ht, key_ptr);
827
      }
828
    } return;
829
  };
830
  char errbuf[128];
831
  sprintf(errbuf, "Unknown thrift typeID %d", thrift_typeID);
832
  throw_tprotocolexception(errbuf, INVALID_DATA);
833
}
834
 
835
 
836
void binary_serialize_spec(zval* zthis, PHPOutputTransport& transport, HashTable* spec) {
837
  HashPosition key_ptr;
838
  zval** val_ptr;
839
 
840
  TSRMLS_FETCH();
841
  zend_class_entry* ce = zend_get_class_entry(zthis TSRMLS_CC);
842
 
843
  for (zend_hash_internal_pointer_reset_ex(spec, &key_ptr); zend_hash_get_current_data_ex(spec, (void**)&val_ptr, &key_ptr) == SUCCESS; zend_hash_move_forward_ex(spec, &key_ptr)) {
844
    ulong fieldno;
845
    if (zend_hash_get_current_key_ex(spec, NULL, NULL, &fieldno, 0, &key_ptr) != HASH_KEY_IS_LONG) {
846
      throw_tprotocolexception("Bad keytype in TSPEC (expected 'long')", INVALID_DATA);
847
      return;
848
    }
849
    HashTable* fieldspec = Z_ARRVAL_PP(val_ptr);
850
 
851
    // field name
852
    zend_hash_find(fieldspec, "var", 4, (void**)&val_ptr);
853
    char* varname = Z_STRVAL_PP(val_ptr);
854
 
855
    // thrift type
856
    zend_hash_find(fieldspec, "type", 5, (void**)&val_ptr);
857
    if (Z_TYPE_PP(val_ptr) != IS_LONG) convert_to_long(*val_ptr);
858
    int8_t ttype = Z_LVAL_PP(val_ptr);
859
 
860
    zval* prop = zend_read_property(ce, zthis, varname, strlen(varname), false TSRMLS_CC);
861
    if (Z_TYPE_P(prop) != IS_NULL) {
862
      transport.writeI8(ttype);
863
      transport.writeI16(fieldno);
864
      binary_serialize(ttype, transport, &prop, fieldspec);
865
    }
866
  }
867
  transport.writeI8(T_STOP); // struct end
868
}
869
 
870
// 6 params: $transport $method_name $ttype $request_struct $seqID $strict_write
871
PHP_FUNCTION(thrift_protocol_write_binary) {
872
  int argc = ZEND_NUM_ARGS();
873
  if (argc < 6) {
874
    WRONG_PARAM_COUNT;
875
  }
876
 
877
  zval ***args = (zval***) emalloc(argc * sizeof(zval**));
878
  zend_get_parameters_array_ex(argc, args);
879
 
880
  if (Z_TYPE_PP(args[0]) != IS_OBJECT) {
881
    php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)");
882
    efree(args);
883
    RETURN_NULL();
884
  }
885
 
886
  if (Z_TYPE_PP(args[1]) != IS_STRING) {
887
    php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (method name)");
888
    efree(args);
889
    RETURN_NULL();
890
  }
891
 
892
  if (Z_TYPE_PP(args[3]) != IS_OBJECT) {
893
    php_error_docref(NULL TSRMLS_CC, E_ERROR, "4th parameter is not an object (request struct)");
894
    efree(args);
895
    RETURN_NULL();
896
  }
897
 
898
  PHPOutputTransport transport(*args[0]);
899
  const char* method_name = Z_STRVAL_PP(args[1]);
900
  convert_to_long(*args[2]);
901
  int32_t msgtype = Z_LVAL_PP(args[2]);
902
  zval* request_struct = *args[3];
903
  convert_to_long(*args[4]);
904
  int32_t seqID = Z_LVAL_PP(args[4]);
905
  convert_to_boolean(*args[5]);
906
  bool strictWrite = Z_BVAL_PP(args[5]);
907
  efree(args);
908
  args = NULL;
909
 
910
  try {
911
    if (strictWrite) {
912
      int32_t version = VERSION_1 | msgtype;
913
      transport.writeI32(version);
914
      transport.writeString(method_name, strlen(method_name));
915
      transport.writeI32(seqID);
916
    } else {
917
      transport.writeString(method_name, strlen(method_name));
918
      transport.writeI8(msgtype);
919
      transport.writeI32(seqID);
920
    }
921
 
922
    zval* spec = zend_read_static_property(zend_get_class_entry(request_struct TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
923
    if (Z_TYPE_P(spec) != IS_ARRAY) {
924
        throw_tprotocolexception("Attempt to send non-Thrift object", INVALID_DATA);
925
    }
926
    binary_serialize_spec(request_struct, transport, Z_ARRVAL_P(spec));
927
  } catch (const PHPExceptionWrapper& ex) {
928
    zend_throw_exception_object(ex TSRMLS_CC);
929
    RETURN_NULL();
930
  }
931
}
932
 
933
// 3 params: $transport $response_Typename $strict_read
934
PHP_FUNCTION(thrift_protocol_read_binary) {
935
  int argc = ZEND_NUM_ARGS();
936
 
937
  if (argc < 3) {
938
    WRONG_PARAM_COUNT;
939
  }
940
 
941
  zval ***args = (zval***) emalloc(argc * sizeof(zval**));
942
  zend_get_parameters_array_ex(argc, args);
943
 
944
  if (Z_TYPE_PP(args[0]) != IS_OBJECT) {
945
    php_error_docref(NULL TSRMLS_CC, E_ERROR, "1st parameter is not an object (transport)");
946
    efree(args);
947
    RETURN_NULL();
948
  }
949
 
950
  if (Z_TYPE_PP(args[1]) != IS_STRING) {
951
    php_error_docref(NULL TSRMLS_CC, E_ERROR, "2nd parameter is not a string (typename of expected response struct)");
952
    efree(args);
953
    RETURN_NULL();
954
  }
955
 
956
  PHPInputTransport transport(*args[0]);
957
  char* obj_typename = Z_STRVAL_PP(args[1]);
958
  convert_to_boolean(*args[2]);
959
  bool strict_read = Z_BVAL_PP(args[2]);
960
  efree(args);
961
  args = NULL;
962
 
963
  try {
964
    int8_t messageType = 0;
965
    int32_t sz = transport.readI32();
966
 
967
    if (sz < 0) {
968
      // Check for correct version number
969
      int32_t version = sz & VERSION_MASK;
970
      if (version != VERSION_1) {
971
        throw_tprotocolexception("Bad version identifier", BAD_VERSION);
972
      }
973
      messageType = (sz & 0x000000ff);
974
      int32_t namelen = transport.readI32();
975
      // skip the name string and the sequence ID, we don't care about those
976
      transport.skip(namelen + 4);
977
    } else {
978
      if (strict_read) {
979
        throw_tprotocolexception("No version identifier... old protocol client in strict mode?", BAD_VERSION);
980
      } else {
981
        // Handle pre-versioned input
982
        transport.skip(sz); // skip string body
983
        messageType = transport.readI8();
984
        transport.skip(4); // skip sequence number
985
      }
986
    }
987
 
988
    if (messageType == T_EXCEPTION) {
989
      zval* ex;
990
      MAKE_STD_ZVAL(ex);
991
      createObject("TApplicationException", ex);
992
      zval* spec = zend_read_static_property(zend_get_class_entry(ex TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
993
      binary_deserialize_spec(ex, transport, Z_ARRVAL_P(spec));
994
      throw PHPExceptionWrapper(ex);
995
    }
996
 
997
    createObject(obj_typename, return_value);
998
    zval* spec = zend_read_static_property(zend_get_class_entry(return_value TSRMLS_CC), "_TSPEC", 6, false TSRMLS_CC);
999
    binary_deserialize_spec(return_value, transport, Z_ARRVAL_P(spec));
1000
  } catch (const PHPExceptionWrapper& ex) {
1001
    zend_throw_exception_object(ex TSRMLS_CC);
1002
    RETURN_NULL();
1003
  }
1004
}
1005