Check in php implementation. (#2052)

This pull request includes two implementation: C extension and PHP
package. Both implementations support encode/decode of singular,
repeated and map fields.
pull/2122/merge
Paul Yang 9 years ago committed by GitHub
parent 86fcd879b3
commit e0e54661f7
  1. 15
      .gitignore
  2. 2
      README.md
  3. 1
      cmake/extract_includes.bat.in
  4. 1
      cmake/libprotoc.cmake
  5. 24
      composer.json
  6. 99
      php/README.md
  7. 388
      php/ext/google/protobuf/array.c
  8. 8
      php/ext/google/protobuf/config.m4
  9. 609
      php/ext/google/protobuf/def.c
  10. 1245
      php/ext/google/protobuf/encode_decode.c
  11. 470
      php/ext/google/protobuf/map.c
  12. 326
      php/ext/google/protobuf/message.c
  13. 74
      php/ext/google/protobuf/package.xml
  14. 150
      php/ext/google/protobuf/protobuf.c
  15. 505
      php/ext/google/protobuf/protobuf.h
  16. 644
      php/ext/google/protobuf/storage.c
  17. 15
      php/ext/google/protobuf/test.php
  18. 310
      php/ext/google/protobuf/type_check.c
  19. 4005
      php/ext/google/protobuf/upb.c
  20. 3128
      php/ext/google/protobuf/upb.h
  21. 68
      php/ext/google/protobuf/utf8.c
  22. 36
      php/ext/google/protobuf/utf8.h
  23. 162
      php/src/Google/Protobuf/Internal/DescriptorPool.php
  24. 63
      php/src/Google/Protobuf/Internal/EnumBuilderContext.php
  25. 40
      php/src/Google/Protobuf/Internal/GPBLabel.php
  26. 55
      php/src/Google/Protobuf/Internal/GPBType.php
  27. 161
      php/src/Google/Protobuf/Internal/GPBUtil.php
  28. 583
      php/src/Google/Protobuf/Internal/GPBWire.php
  29. 323
      php/src/Google/Protobuf/Internal/InputStream.php
  30. 57
      php/src/Google/Protobuf/Internal/MapEntry.php
  31. 321
      php/src/Google/Protobuf/Internal/MapField.php
  32. 671
      php/src/Google/Protobuf/Internal/Message.php
  33. 120
      php/src/Google/Protobuf/Internal/MessageBuilderContext.php
  34. 77
      php/src/Google/Protobuf/Internal/OneofField.php
  35. 143
      php/src/Google/Protobuf/Internal/OutputStream.php
  36. 303
      php/src/Google/Protobuf/Internal/RepeatedField.php
  37. 175
      php/src/Google/Protobuf/Internal/Type.php
  38. 541
      php/src/Google/Protobuf/descriptor.php
  39. 2532
      php/src/Google/Protobuf/descriptor_internal.pb.php
  40. 15
      php/src/phpdoc.dist.xml
  41. 888
      php/tests/array_test.php
  42. 4
      php/tests/autoload.php
  43. 136
      php/tests/encode_decode_test.php
  44. 557
      php/tests/generated_class_test.php
  45. 648
      php/tests/map_field_test.php
  46. 73
      php/tests/memory_leak_test.php
  47. 443
      php/tests/php_implementation_test.php
  48. 1385
      php/tests/test.pb.php
  49. 136
      php/tests/test.proto
  50. 23
      php/tests/test.sh
  51. 92
      php/tests/test_base.php
  52. 36
      php/tests/test_include.pb.php
  53. 393
      php/tests/test_util.php
  54. 13
      phpunit.xml
  55. 2
      src/Makefile.am
  56. 10
      src/google/protobuf/compiler/main.cc
  57. 781
      src/google/protobuf/compiler/php/php_generator.cc
  58. 57
      src/google/protobuf/compiler/php/php_generator.h

15
.gitignore vendored

@ -118,3 +118,18 @@ conformance/lite/
conformance/nonexistent_tests.txt
conformance/protoc_middleman
conformance/succeeding_tests.txt
# php test output
composer.lock
php/ext/google/protobuf/.libs/
php/ext/google/protobuf/Makefile.fragments
php/ext/google/protobuf/Makefile.global
php/ext/google/protobuf/Makefile.objects
php/ext/google/protobuf/acinclude.m4
php/ext/google/protobuf/build/
php/ext/google/protobuf/config.h
php/ext/google/protobuf/config.nice
php/ext/google/protobuf/configure.in
php/ext/google/protobuf/mkinstalldirs
php/ext/google/protobuf/run-tests.php
vendor/

@ -65,7 +65,7 @@ how to install protobuf runtime for that specific language:
| JavaScript | [js](js) |
| Ruby | [ruby](ruby) |
| Go | [golang/protobuf](https://github.com/golang/protobuf) |
| PHP | TBD |
| PHP | [php](php) |
Usage

@ -32,6 +32,7 @@ copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\js\js_generat
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\objectivec\objectivec_generator.h include\google\protobuf\compiler\objectivec\objectivec_generator.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\objectivec\objectivec_helpers.h include\google\protobuf\compiler\objectivec\objectivec_helpers.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\parser.h include\google\protobuf\compiler\parser.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\php\php_generator.h include\google\protobuf\compiler\php\php_generator.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\plugin.h include\google\protobuf\compiler\plugin.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\plugin.pb.h include\google\protobuf\compiler\plugin.pb.h
copy ${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\python\python_generator.h include\google\protobuf\compiler\python\python_generator.h

@ -84,6 +84,7 @@ set(libprotoc_files
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc
${protobuf_source_dir}/src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/php/php_generator.cc
${protobuf_source_dir}/src/google/protobuf/compiler/plugin.cc
${protobuf_source_dir}/src/google/protobuf/compiler/plugin.pb.cc
${protobuf_source_dir}/src/google/protobuf/compiler/python/python_generator.cc

@ -0,0 +1,24 @@
{
"name": "google/protobuf",
"type": "library",
"description": "proto library for PHP",
"keywords": ["proto"],
"homepage": "https://developers.google.com/protocol-buffers/",
"license": "BSD-3-Clause",
"require": {
"php": ">=5.5.0"
},
"require-dev": {
"phpunit/phpunit": ">=4.8.0"
},
"autoload": {
"psr-4": {
"Google\\": "php/src/Google"
},
"files": [
"php/src/Google/Protobuf/descriptor.php",
"php/src/Google/Protobuf/descriptor_internal.pb.php",
"php/src/Google/Protobuf/Internal/Type.php"
]
}
}

@ -0,0 +1,99 @@
This directory contains the Protocol Buffers runtime implementation via both a
pure PHP package and a native c extension. The pure PHP package is intended to
provide usability to wider range of PHP platforms, while the c extension is
intended to provide higher performance. Both implementations provide the same
runtime APIs and share the same generated code. Users don’t need to re-generate
code for the same proto definition when they want to switch the implementation
later.
Both implementations make use of generated PHP code that defines message and
enum types in PHP. We strongly recommend using protoc's PHP generation support
with .proto files. The build process in this directory only installs the
extension/package; you need to install protoc as well to have PHP code
generation functionality.
## Requirements
To use PHP runtime library requires:
- PHP 5.5 or above.
## Installation
### C Extension
#### Prerequirements
To install the c extension, the following tools are needed:
* autoconf
* automake
* libtool
* make
* gcc
* pear
* pecl
On Ubuntu, you can install them with:
```
sudo apt-get install php-pear php5-dev autoconf automake libtool make gcc
```
On other platforms, please use the corresponding package managing tool to
install them before proceeding.
#### Installation from Source (Building extension)
To build the c extension, run the following command:
```
cd ext/google/protobuf
pear package
sudo pecl install protobuf-{VERSION}.tgz
```
#### Installation from PECL
When we release a version of Protocol Buffers, we will upload the extension to
[PECL](https://pecl.php.net/). To use this pre-packaged extension, simply
install it as you would any other extension:
```
sudo pecl install protobuf-{VERSION}
```
### PHP Package
#### Installation from composer
Simply add "google/protobuf" to the 'require' section of composer.json in your
project.
### Protoc
Once the extension or package is installed, if you wish to generate PHP code
from a `.proto` file, you will also want to install the Protocol Buffers
compiler (protoc), as described in this repository's main `README` file. The
version of `protoc` included in the latest release supports the `--php_out`
option to generate PHP code:
```
protoc --php_out=out_dir test.proto
```
## Usage
For general guide:
https://developers.google.com/protocol-buffers/phptutorial/
For generated code:
https://developers.google.com/protocol-buffers/docs/reference/php-generated
Known Issues
------------
* Missing native support for well known types.
* Missing support for proto2.
* No API provided for clear/copy messages.
* No API provided for encoding/decoding with stream.
* Map fields may not be garbage-collected if there is cycle reference.
* No debug information for messages in c extension.
* HHVM not tested.
* PHP 7.0 not tested.
* C extension not tested on windows.
* Message name cannot be Empty.

@ -0,0 +1,388 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <ext/spl/spl_iterators.h>
#include <Zend/zend_API.h>
#include <Zend/zend_interfaces.h>
#include "protobuf.h"
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
ZEND_ARG_INFO(0, index)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
ZEND_ARG_INFO(0, index)
ZEND_ARG_INFO(0, newval)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_void, 0)
ZEND_END_ARG_INFO()
static zend_function_entry repeated_field_methods[] = {
PHP_ME(RepeatedField, __construct, NULL, ZEND_ACC_PUBLIC)
PHP_ME(RepeatedField, append, NULL, ZEND_ACC_PUBLIC)
PHP_ME(RepeatedField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC)
PHP_ME(RepeatedField, offsetGet, arginfo_offsetGet, ZEND_ACC_PUBLIC)
PHP_ME(RepeatedField, offsetSet, arginfo_offsetSet, ZEND_ACC_PUBLIC)
PHP_ME(RepeatedField, offsetUnset, arginfo_offsetGet, ZEND_ACC_PUBLIC)
PHP_ME(RepeatedField, count, arginfo_void, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
// Forward declare static functions.
static zend_object_value repeated_field_create(zend_class_entry *ce TSRMLS_DC);
static void repeated_field_free(void *object TSRMLS_DC);
static int repeated_field_array_init(zval *array, upb_fieldtype_t type,
uint size ZEND_FILE_LINE_DC);
static void repeated_field_free_element(void *object);
static void repeated_field_write_dimension(zval *object, zval *offset,
zval *value TSRMLS_DC);
static int repeated_field_has_dimension(zval *object, zval *offset TSRMLS_DC);
static HashTable *repeated_field_get_gc(zval *object, zval ***table,
int *n TSRMLS_DC);
// -----------------------------------------------------------------------------
// RepeatedField creation/desctruction
// -----------------------------------------------------------------------------
zend_class_entry* repeated_field_type;
zend_object_handlers* repeated_field_handlers;
void repeated_field_init(TSRMLS_D) {
zend_class_entry class_type;
const char* class_name = "Google\\Protobuf\\Internal\\RepeatedField";
INIT_CLASS_ENTRY_EX(class_type, class_name, strlen(class_name),
repeated_field_methods);
repeated_field_type = zend_register_internal_class(&class_type TSRMLS_CC);
repeated_field_type->create_object = repeated_field_create;
zend_class_implements(repeated_field_type TSRMLS_CC, 2, spl_ce_ArrayAccess,
spl_ce_Countable);
repeated_field_handlers = PEMALLOC(zend_object_handlers);
memcpy(repeated_field_handlers, zend_get_std_object_handlers(),
sizeof(zend_object_handlers));
repeated_field_handlers->get_gc = repeated_field_get_gc;
}
static zend_object_value repeated_field_create(zend_class_entry *ce TSRMLS_DC) {
zend_object_value retval = {0};
RepeatedField *intern;
intern = emalloc(sizeof(RepeatedField));
memset(intern, 0, sizeof(RepeatedField));
zend_object_std_init(&intern->std, ce TSRMLS_CC);
object_properties_init(&intern->std, ce);
intern->array = NULL;
intern->type = 0;
intern->msg_ce = NULL;
retval.handle = zend_objects_store_put(
intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
(zend_objects_free_object_storage_t)repeated_field_free, NULL TSRMLS_CC);
retval.handlers = repeated_field_handlers;
return retval;
}
static void repeated_field_free(void *object TSRMLS_DC) {
RepeatedField *intern = object;
zend_object_std_dtor(&intern->std TSRMLS_CC);
zval_ptr_dtor(&intern->array);
efree(object);
}
static int repeated_field_array_init(zval *array, upb_fieldtype_t type,
uint size ZEND_FILE_LINE_DC) {
ALLOC_HASHTABLE(Z_ARRVAL_P(array));
switch (type) {
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
case UPB_TYPE_MESSAGE:
zend_hash_init(Z_ARRVAL_P(array), size, NULL, ZVAL_PTR_DTOR, 0);
break;
default:
zend_hash_init(Z_ARRVAL_P(array), size, NULL, repeated_field_free_element,
0);
}
Z_TYPE_P(array) = IS_ARRAY;
return SUCCESS;
}
static void repeated_field_free_element(void *object) {
}
// -----------------------------------------------------------------------------
// RepeatedField Handlers
// -----------------------------------------------------------------------------
static void repeated_field_write_dimension(zval *object, zval *offset,
zval *value TSRMLS_DC) {
uint64_t index;
RepeatedField *intern = zend_object_store_get_object(object TSRMLS_CC);
HashTable *ht = HASH_OF(intern->array);
int size = native_slot_size(intern->type);
unsigned char memory[NATIVE_SLOT_MAX_SIZE];
memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
if (!native_slot_set(intern->type, intern->msg_ce, memory, value)) {
return;
}
if (!offset || Z_TYPE_P(offset) == IS_NULL) {
index = zend_hash_num_elements(HASH_OF(intern->array));
} else {
if (protobuf_convert_to_uint64(offset, &index)) {
if (!zend_hash_index_exists(ht, index)) {
zend_error(E_USER_ERROR, "Element at %d doesn't exist.\n", index);
return;
}
} else {
return;
}
}
zend_hash_index_update(ht, index, memory, size, NULL);
}
static HashTable *repeated_field_get_gc(zval *object, zval ***table,
int *n TSRMLS_DC) {
*table = NULL;
*n = 0;
RepeatedField *intern = zend_object_store_get_object(object TSRMLS_CC);
return HASH_OF(intern->array);
}
// -----------------------------------------------------------------------------
// C RepeatedField Utilities
// -----------------------------------------------------------------------------
void *repeated_field_index_native(RepeatedField *intern, int index) {
HashTable *ht = HASH_OF(intern->array);
void *value;
if (zend_hash_index_find(ht, index, (void **)&value) == FAILURE) {
zend_error(E_USER_ERROR, "Element at %d doesn't exist.\n", index);
return NULL;
}
return value;
}
void repeated_field_push_native(RepeatedField *intern, void *value TSRMLS_DC) {
HashTable *ht = HASH_OF(intern->array);
int size = native_slot_size(intern->type);
zend_hash_next_index_insert(ht, (void **)value, size, NULL);
}
void repeated_field_create_with_type(zend_class_entry *ce,
const upb_fielddef *field,
zval **repeated_field TSRMLS_DC) {
MAKE_STD_ZVAL(*repeated_field);
Z_TYPE_PP(repeated_field) = IS_OBJECT;
Z_OBJVAL_PP(repeated_field) =
repeated_field_type->create_object(repeated_field_type TSRMLS_CC);
RepeatedField *intern =
zend_object_store_get_object(*repeated_field TSRMLS_CC);
intern->type = upb_fielddef_type(field);
if (intern->type == UPB_TYPE_MESSAGE) {
upb_msgdef *msg = upb_fielddef_msgsubdef(field);
zval *desc_php = get_def_obj(msg);
Descriptor *desc = zend_object_store_get_object(desc_php TSRMLS_CC);
intern->msg_ce = desc->klass;
}
MAKE_STD_ZVAL(intern->array);
repeated_field_array_init(intern->array, intern->type, 0 ZEND_FILE_LINE_CC);
// TODO(teboring): Link class entry for message and enum
}
// -----------------------------------------------------------------------------
// PHP RepeatedField Methods
// -----------------------------------------------------------------------------
/**
* Constructs an instance of RepeatedField.
* @param long Type of the stored element.
* @param string Message/Enum class name (message/enum fields only).
*/
PHP_METHOD(RepeatedField, __construct) {
long type;
zend_class_entry* klass = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|C", &type, &klass) ==
FAILURE) {
return;
}
RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
intern->type = to_fieldtype(type);
intern->msg_ce = klass;
MAKE_STD_ZVAL(intern->array);
repeated_field_array_init(intern->array, intern->type, 0 ZEND_FILE_LINE_CC);
if (intern->type == UPB_TYPE_MESSAGE && klass == NULL) {
zend_error(E_USER_ERROR, "Message type must have concrete class.");
return;
}
// TODO(teboring): Consider enum.
}
/**
* Append element to the end of the repeated field.
* @param object The element to be added.
*/
PHP_METHOD(RepeatedField, append) {
zval *value;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) ==
FAILURE) {
return;
}
repeated_field_write_dimension(getThis(), NULL, value TSRMLS_CC);
}
/**
* Check whether the element at given index exists.
* @param long The index to be checked.
* @return bool True if the element at the given index exists.
*/
PHP_METHOD(RepeatedField, offsetExists) {
long index;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
FAILURE) {
return;
}
RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
RETURN_BOOL(index >= 0 &&
index < zend_hash_num_elements(HASH_OF(intern->array)));
}
/**
* Return the element at the given index.
* This will also be called for: $ele = $arr[0]
* @param long The index of the element to be fetched.
* @return object The stored element at given index.
* @exception Invalid type for index.
* @exception Non-existing index.
*/
PHP_METHOD(RepeatedField, offsetGet) {
long index;
void *memory;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
FAILURE) {
return;
}
RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
HashTable *table = HASH_OF(intern->array);
if (zend_hash_index_find(table, index, (void **)&memory) == FAILURE) {
zend_error(E_USER_ERROR, "Element at %d doesn't exist.\n", index);
return;
}
native_slot_get(intern->type, memory, return_value_ptr TSRMLS_CC);
}
/**
* Assign the element at the given index.
* This will also be called for: $arr []= $ele and $arr[0] = ele
* @param long The index of the element to be assigned.
* @param object The element to be assigned.
* @exception Invalid type for index.
* @exception Non-existing index.
* @exception Incorrect type of the element.
*/
PHP_METHOD(RepeatedField, offsetSet) {
zval *index, *value;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) ==
FAILURE) {
return;
}
repeated_field_write_dimension(getThis(), index, value TSRMLS_CC);
}
/**
* Remove the element at the given index.
* This will also be called for: unset($arr)
* @param long The index of the element to be removed.
* @exception Invalid type for index.
* @exception The element to be removed is not at the end of the RepeatedField.
*/
PHP_METHOD(RepeatedField, offsetUnset) {
long index;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
FAILURE) {
return;
}
RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
// Only the element at the end of the array can be removed.
if (index == -1 ||
index != (zend_hash_num_elements(HASH_OF(intern->array)) - 1)) {
zend_error(E_USER_ERROR, "Cannot remove element at %d.\n", index);
return;
}
zend_hash_index_del(HASH_OF(intern->array), index);
}
/**
* Return the number of stored elements.
* This will also be called for: count($arr)
* @return long The number of stored elements.
*/
PHP_METHOD(RepeatedField, count) {
RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC);
if (zend_parse_parameters_none() == FAILURE) {
return;
}
RETURN_LONG(zend_hash_num_elements(HASH_OF(intern->array)));
}

@ -1,10 +1,10 @@
dnl lines starting with "dnl" are comments
PHP_ARG_ENABLE(protobuf, whether to enable Protobuf extension, [ --enable-protobuf Enable Protobuf extension])
if test "$PHP_PROTOBUF" != "no"; then
dnl this defines the extension
PHP_NEW_EXTENSION(protobuf, upb.c protobuf.c def.c message.c storage.c, $ext_shared)
PHP_NEW_EXTENSION(
protobuf,
array.c def.c encode_decode.c map.c message.c protobuf.c storage.c type_check.c upb.c utf8.c,
$ext_shared)
fi

@ -1,115 +1,213 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protobuf.h"
// Forward declare.
static zend_object_value descriptor_create(zend_class_entry *ce TSRMLS_DC);
static void descriptor_init_c_instance(Descriptor* intern TSRMLS_DC);
static void descriptor_free_c(Descriptor* object TSRMLS_DC);
static void descriptor_free(void* object TSRMLS_DC);
static zend_object_value enum_descriptor_create(zend_class_entry *ce TSRMLS_DC);
static void enum_descriptor_init_c_instance(EnumDescriptor* intern TSRMLS_DC);
static void enum_descriptor_free_c(EnumDescriptor* object TSRMLS_DC);
static void enum_descriptor_free(void* object TSRMLS_DC);
static zend_object_value descriptor_pool_create(zend_class_entry *ce TSRMLS_DC);
static void descriptor_pool_free_c(DescriptorPool* object TSRMLS_DC);
static void descriptor_pool_free(void* object TSRMLS_DC);
static void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC);
// -----------------------------------------------------------------------------
// Common Utilities
// -----------------------------------------------------------------------------
void check_upb_status(const upb_status* status, const char* msg) {
static void check_upb_status(const upb_status* status, const char* msg) {
if (!upb_ok(status)) {
zend_error("%s: %s\n", msg, upb_status_errmsg(status));
zend_error(E_ERROR, "%s: %s\n", msg, upb_status_errmsg(status));
}
}
static void upb_filedef_free(void *r) {
upb_filedef *f = *(upb_filedef **)r;
size_t i;
static upb_def *check_notfrozen(const upb_def *def) {
if (upb_def_isfrozen(def)) {
zend_error(E_ERROR,
"Attempt to modify a frozen descriptor. Once descriptors are "
"added to the descriptor pool, they may not be modified.");
for (i = 0; i < upb_filedef_depcount(f); i++) {
upb_filedef_unref(upb_filedef_dep(f, i), f);
}
return (upb_def *)def;
}
static upb_msgdef *check_msgdef_notfrozen(const upb_msgdef *def) {
return upb_downcast_msgdef_mutable(check_notfrozen((const upb_def *)def));
upb_inttable_uninit(&f->defs);
upb_inttable_uninit(&f->deps);
upb_gfree((void *)f->name);
upb_gfree((void *)f->package);
upb_gfree(f);
}
static upb_fielddef *check_fielddef_notfrozen(const upb_fielddef *def) {
return upb_downcast_fielddef_mutable(check_notfrozen((const upb_def *)def));
// Camel-case the field name and append "Entry" for generated map entry name.
// e.g. map<KeyType, ValueType> foo_map => FooMapEntry
static void append_map_entry_name(char *result, const char *field_name,
int pos) {
bool cap_next = true;
int i;
for (i = 0; i < strlen(field_name); ++i) {
if (field_name[i] == '_') {
cap_next = true;
} else if (cap_next) {
// Note: Do not use ctype.h due to locales.
if ('a' <= field_name[i] && field_name[i] <= 'z') {
result[pos++] = field_name[i] - 'a' + 'A';
} else {
result[pos++] = field_name[i];
}
cap_next = false;
} else {
result[pos++] = field_name[i];
}
}
strcat(result, "Entry");
}
#define PROTOBUF_WRAP_INTERN(wrapper, intern, intern_dtor) \
Z_TYPE_P(wrapper) = IS_OBJECT; \
Z_OBJVAL_P(wrapper) \
.handle = zend_objects_store_put( \
intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \
intern_dtor, NULL TSRMLS_CC); \
Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers();
#define PROTOBUF_SETUP_ZEND_WRAPPER(class_name, class_name_lower, wrapper, \
intern) \
Z_TYPE_P(wrapper) = IS_OBJECT; \
class_name *intern = ALLOC(class_name); \
memset(intern, 0, sizeof(class_name)); \
class_name_lower##_init_c_instance(intern TSRMLS_CC); \
Z_OBJVAL_P(wrapper) \
.handle = zend_objects_store_put(intern, NULL, class_name_lower##_free, \
NULL TSRMLS_CC); \
Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers();
#define PROTOBUF_CREATE_ZEND_WRAPPER(class_name, class_name_lower, wrapper, \
intern) \
MAKE_STD_ZVAL(wrapper); \
PROTOBUF_SETUP_ZEND_WRAPPER(class_name, class_name_lower, wrapper, intern);
#define DEFINE_CLASS(name, name_lower, string_name) \
zend_class_entry *name_lower##_type; \
#define CHECK_UPB(code, msg) \
do { \
upb_status status = UPB_STATUS_INIT; \
code; \
check_upb_status(&status, msg); \
} while (0)
// Define PHP class
#define DEFINE_PROTOBUF_INIT_CLASS(name_lower, string_name) \
void name_lower##_init(TSRMLS_D) { \
zend_class_entry class_type; \
INIT_CLASS_ENTRY(class_type, string_name, name_lower##_methods); \
name_lower##_type = zend_register_internal_class(&class_type TSRMLS_CC); \
name_lower##_type->create_object = name_lower##_create; \
} \
name *php_to_##name_lower(zval *val TSRMLS_DC) { \
return (name *)zend_object_store_get_object(val TSRMLS_CC); \
} \
void name_lower##_free(void *object TSRMLS_DC) { \
name *intern = (name *)object; \
name_lower##_free_c(intern TSRMLS_CC); \
efree(object); \
} \
zend_object_value name_lower##_create(zend_class_entry *ce TSRMLS_DC) { \
zend_object_value return_value; \
name *intern = (name *)emalloc(sizeof(name)); \
memset(intern, 0, sizeof(name)); \
name_lower##_init_c_instance(intern TSRMLS_CC); \
return_value.handle = zend_objects_store_put( \
intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \
name_lower##_free, NULL TSRMLS_CC); \
return_value.handlers = zend_get_std_object_handlers(); \
return return_value; \
}
#define DEFINE_PROTOBUF_CREATE(name, name_lower) \
static zend_object_value name_lower##_create( \
zend_class_entry* ce TSRMLS_DC) { \
zend_object_value return_value; \
name* intern = (name*)emalloc(sizeof(name)); \
memset(intern, 0, sizeof(name)); \
name_lower##_init_c_instance(intern TSRMLS_CC); \
return_value.handle = zend_objects_store_put( \
intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \
name_lower##_free, NULL TSRMLS_CC); \
return_value.handlers = zend_get_std_object_handlers(); \
return return_value; \
}
#define DEFINE_PROTOBUF_FREE(name, name_lower) \
static void name_lower##_free(void* object TSRMLS_DC) { \
name* intern = (name*)object; \
name_lower##_free_c(intern TSRMLS_CC); \
efree(object); \
}
#define DEFINE_CLASS(name, name_lower, string_name) \
zend_class_entry* name_lower##_type; \
DEFINE_PROTOBUF_FREE(name, name_lower) \
DEFINE_PROTOBUF_CREATE(name, name_lower) \
DEFINE_PROTOBUF_INIT_CLASS(name_lower, string_name)
// -----------------------------------------------------------------------------
// GPBType
// -----------------------------------------------------------------------------
zend_class_entry* gpb_type_type;
static zend_function_entry gpb_type_methods[] = {
ZEND_FE_END
};
void gpb_type_init(TSRMLS_D) {
zend_class_entry class_type;
INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBType",
gpb_type_methods);
gpb_type_type = zend_register_internal_class(&class_type TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("DOUBLE"), 1 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("FLOAT"), 2 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("INT64"), 3 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("UINT64"), 4 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("INT32"), 5 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("FIXED64"), 6 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("FIXED32"), 7 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("BOOL"), 8 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("STRING"), 9 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("GROUP"), 10 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("MESSAGE"), 11 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("BYTES"), 12 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("UINT32"), 13 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("ENUM"), 14 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("SFIXED32"),
15 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("SFIXED64"),
16 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("SINT32"), 17 TSRMLS_CC);
zend_declare_class_constant_long(gpb_type_type, STR("SINT64"), 18 TSRMLS_CC);
}
// -----------------------------------------------------------------------------
// DescriptorPool
// -----------------------------------------------------------------------------
static zend_function_entry descriptor_pool_methods[] = {
PHP_ME(DescriptorPool, addMessage, NULL, ZEND_ACC_PUBLIC)
PHP_ME(DescriptorPool, finalize, NULL, ZEND_ACC_PUBLIC)
PHP_ME(DescriptorPool, getGeneratedPool, NULL,
ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(DescriptorPool, internalAddGeneratedFile, NULL, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
DEFINE_CLASS(DescriptorPool, descriptor_pool,
"Google\\Protobuf\\DescriptorPool");
"Google\\Protobuf\\Internal\\DescriptorPool");
zval* generated_pool_php; // wrapper of generated pool
DescriptorPool *generated_pool; // The actual generated pool
ZEND_FUNCTION(get_generated_pool) {
if (PROTOBUF_G(generated_pool) == NULL) {
MAKE_STD_ZVAL(PROTOBUF_G(generated_pool));
Z_TYPE_P(PROTOBUF_G(generated_pool)) = IS_OBJECT;
static void init_generated_pool_once(TSRMLS_D) {
if (generated_pool_php == NULL) {
MAKE_STD_ZVAL(generated_pool_php);
Z_TYPE_P(generated_pool_php) = IS_OBJECT;
generated_pool = ALLOC(DescriptorPool);
descriptor_pool_init_c_instance(generated_pool TSRMLS_CC);
Z_OBJ_HANDLE_P(PROTOBUF_G(generated_pool)) = zend_objects_store_put(
Z_OBJ_HANDLE_P(generated_pool_php) = zend_objects_store_put(
generated_pool, NULL,
(zend_objects_free_object_storage_t)descriptor_pool_free, NULL TSRMLS_CC);
Z_OBJ_HT_P(PROTOBUF_G(generated_pool)) = zend_get_std_object_handlers();
(zend_objects_free_object_storage_t)descriptor_pool_free,
NULL TSRMLS_CC);
Z_OBJ_HT_P(generated_pool_php) = zend_get_std_object_handlers();
}
RETURN_ZVAL(PROTOBUF_G(generated_pool), 1, 0);
}
void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC) {
static void descriptor_pool_init_c_instance(DescriptorPool *pool TSRMLS_DC) {
zend_object_std_init(&pool->std, descriptor_pool_type TSRMLS_CC);
pool->symtab = upb_symtab_new(&pool->symtab);
@ -117,31 +215,21 @@ void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC) {
zend_hash_init(pool->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0);
}
void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) {
static void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) {
upb_symtab_unref(pool->symtab, &pool->symtab);
zend_hash_destroy(pool->pending_list);
FREE_HASHTABLE(pool->pending_list);
}
PHP_METHOD(DescriptorPool, addMessage) {
char *name = NULL;
int str_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &str_len) ==
FAILURE) {
return;
static void validate_enumdef(const upb_enumdef *enumdef) {
// Verify that an entry exists with integer value 0. (This is the default
// value.)
const char *lookup = upb_enumdef_iton(enumdef, 0);
if (lookup == NULL) {
zend_error(E_USER_ERROR,
"Enum definition does not contain a value for '0'.");
}
zval* retval = NULL;
PROTOBUF_CREATE_ZEND_WRAPPER(MessageBuilderContext, message_builder_context,
retval, context);
MAKE_STD_ZVAL(context->pool);
ZVAL_ZVAL(context->pool, getThis(), 1, 0);
Descriptor *desc = php_to_descriptor(context->descriptor TSRMLS_CC);
Descriptor_name_set(desc, name);
RETURN_ZVAL(retval, 0, 1);
}
static void validate_msgdef(const upb_msgdef* msgdef) {
@ -157,35 +245,123 @@ static void validate_msgdef(const upb_msgdef* msgdef) {
}
}
PHP_METHOD(DescriptorPool, finalize) {
DescriptorPool *self = php_to_descriptor_pool(getThis() TSRMLS_CC);
Bucket *temp;
int i, num;
PHP_METHOD(DescriptorPool, getGeneratedPool) {
init_generated_pool_once(TSRMLS_C);
RETURN_ZVAL(generated_pool_php, 1, 0);
}
static void convert_to_class_name_inplace(char *proto_name,
size_t pkg_name_len) {
size_t i;
bool first_char = false;
num = zend_hash_num_elements(self->pending_list);
upb_def **defs = emalloc(sizeof(upb_def *) * num);
for (i = 0; i <= pkg_name_len + 1; i++) {
// PHP package uses camel case.
if (!first_char && proto_name[i] != '.') {
first_char = true;
proto_name[i] += 'A' - 'a';
}
// php packages are divided by '\'.
if (proto_name[i] == '.') {
first_char = false;
proto_name[i] = '\\';
}
}
// Submessage is concatenated with its containing messages by '_'.
for (i = pkg_name_len; i < strlen(proto_name); i++) {
if (proto_name[i] == '.') {
proto_name[i] = '_';
}
}
}
PHP_METHOD(DescriptorPool, internalAddGeneratedFile) {
char *data = NULL;
int data_len;
upb_filedef **files;
size_t i;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) ==
FAILURE) {
return;
}
for (i = 0, temp = self->pending_list->pListHead; temp != NULL;
temp = temp->pListNext) {
zval *def_php = *(zval **)temp->pData;
Descriptor* desc = php_to_descriptor(def_php TSRMLS_CC);
defs[i] = (upb_def *)desc->msgdef;
validate_msgdef((const upb_msgdef *)defs[i++]);
DescriptorPool *pool = UNBOX(DescriptorPool, getThis());
CHECK_UPB(files = upb_loaddescriptor(data, data_len, &pool, &status),
"Parse binary descriptors to internal descriptors failed");
// This method is called only once in each file.
assert(files[0] != NULL);
assert(files[1] == NULL);
CHECK_UPB(upb_symtab_addfile(pool->symtab, files[0], &status),
"Unable to add file to DescriptorPool");
// For each enum/message, we need its PHP class, upb descriptor and its PHP
// wrapper. These information are needed later for encoding, decoding and type
// checking. However, sometimes we just have one of them. In order to find
// them quickly, here, we store the mapping for them.
for (i = 0; i < upb_filedef_defcount(files[0]); i++) {
const upb_def *def = upb_filedef_def(files[0], i);
switch (upb_def_type(def)) {
#define CASE_TYPE(def_type, def_type_lower, desc_type, desc_type_lower) \
case UPB_DEF_##def_type: { \
desc_type *desc; \
zval *desc_php; \
CREATE(desc_type, desc, desc_type_lower##_init_c_instance); \
BOX(desc_type, desc_php, desc, desc_type_lower##_free); \
Z_DELREF_P(desc_php); \
const upb_##def_type_lower *def_type_lower = \
upb_downcast_##def_type_lower(def); \
desc->def_type_lower = def_type_lower; \
add_def_obj(desc->def_type_lower, desc_php); \
/* Unlike other messages, MapEntry is shared by all map fields and doesn't \
* have generated PHP class.*/ \
if (upb_def_type(def) == UPB_DEF_MSG && upb_msgdef_mapentry(def)) { \
break; \
} \
/* Prepend '.' to package name to make it absolute. */ \
const char *fullname = upb_##def_type_lower##_fullname(def_type_lower); \
char *klass_name = ecalloc(sizeof(char), 2 + strlen(fullname)); \
klass_name[0] = '.'; \
strcpy(&klass_name[1], fullname); \
size_t pkg_name_len = strlen(upb_filedef_package(files[0])); \
convert_to_class_name_inplace(klass_name, pkg_name_len); \
zend_class_entry **pce; \
if (zend_lookup_class(klass_name, strlen(klass_name), &pce TSRMLS_CC) == \
FAILURE) { \
zend_error(E_ERROR, "Generated message class %s hasn't been defined", \
klass_name); \
return; \
} else { \
desc->klass = *pce; \
} \
add_ce_obj(desc->klass, desc_php); \
efree(klass_name); \
break; \
}
CHECK_UPB(upb_symtab_add(self->symtab, (upb_def **)defs, num, NULL, &status),
"Unable to add defs to DescriptorPool");
CASE_TYPE(MSG, msgdef, Descriptor, descriptor)
CASE_TYPE(ENUM, enumdef, EnumDescriptor, enum_descriptor)
#undef CASE_TYPE
for (temp = self->pending_list->pListHead; temp != NULL;
temp = temp->pListNext) {
// zval *def_php = *(zval **)temp->pData;
// Descriptor* desc = php_to_descriptor(def_php TSRMLS_CC);
build_class_from_descriptor((zval *)temp->pDataPtr TSRMLS_CC);
default:
break;
}
}
for (i = 0; i < upb_filedef_defcount(files[0]); i++) {
const upb_def *def = upb_filedef_def(files[0], i);
if (upb_def_type(def) == UPB_DEF_MSG) {
const upb_msgdef *msgdef = upb_downcast_msgdef(def);
zval *desc_php = get_def_obj(msgdef);
build_class_from_descriptor(desc_php TSRMLS_CC);
}
}
FREE(defs);
zend_hash_destroy(self->pending_list);
zend_hash_init(self->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0);
upb_filedef_unref(files[0], &pool);
upb_gfree(files);
}
// -----------------------------------------------------------------------------
@ -196,186 +372,87 @@ static zend_function_entry descriptor_methods[] = {
ZEND_FE_END
};
DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Descriptor");
DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Internal\\Descriptor");
void descriptor_free_c(Descriptor *self TSRMLS_DC) {
upb_msg_field_iter iter;
upb_msg_field_begin(&iter, self->msgdef);
while (!upb_msg_field_done(&iter)) {
upb_fielddef *fielddef = upb_msg_iter_field(&iter);
upb_fielddef_unref(fielddef, &fielddef);
upb_msg_field_next(&iter);
}
upb_msgdef_unref(self->msgdef, &self->msgdef);
static void descriptor_free_c(Descriptor *self TSRMLS_DC) {
if (self->layout) {
free_layout(self->layout);
}
if (self->fill_handlers) {
upb_handlers_unref(self->fill_handlers, &self->fill_handlers);
}
if (self->fill_method) {
upb_pbdecodermethod_unref(self->fill_method, &self->fill_method);
}
if (self->pb_serialize_handlers) {
upb_handlers_unref(self->pb_serialize_handlers,
&self->pb_serialize_handlers);
}
}
static void descriptor_add_field(Descriptor *desc,
const upb_fielddef *fielddef) {
upb_msgdef *mut_def = check_msgdef_notfrozen(desc->msgdef);
upb_fielddef *mut_field_def = check_fielddef_notfrozen(fielddef);
CHECK_UPB(upb_msgdef_addfield(mut_def, mut_field_def, NULL, &status),
"Adding field to Descriptor failed");
// add_def_obj(fielddef, obj);
}
void descriptor_init_c_instance(Descriptor* desc TSRMLS_DC) {
static void descriptor_init_c_instance(Descriptor *desc TSRMLS_DC) {
zend_object_std_init(&desc->std, descriptor_type TSRMLS_CC);
desc->msgdef = upb_msgdef_new(&desc->msgdef);
desc->msgdef = NULL;
desc->layout = NULL;
// MAKE_STD_ZVAL(intern->klass);
// ZVAL_NULL(intern->klass);
desc->klass = NULL;
desc->fill_handlers = NULL;
desc->fill_method = NULL;
desc->pb_serialize_handlers = NULL;
}
void Descriptor_name_set(Descriptor *desc, const char *name) {
upb_msgdef *mut_def = check_msgdef_notfrozen(desc->msgdef);
CHECK_UPB(upb_msgdef_setfullname(mut_def, name, &status),
"Error setting Descriptor name");
}
// -----------------------------------------------------------------------------
// FieldDescriptor
// EnumDescriptor
// -----------------------------------------------------------------------------
static void field_descriptor_name_set(const upb_fielddef* fielddef,
const char *name) {
upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
CHECK_UPB(upb_fielddef_setname(mut_def, name, &status),
"Error setting FieldDescriptor name");
}
static void field_descriptor_label_set(const upb_fielddef* fielddef,
upb_label_t upb_label) {
upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
upb_fielddef_setlabel(mut_def, upb_label);
}
upb_fieldtype_t string_to_descriptortype(const char *type) {
#define CONVERT(upb, str) \
if (!strcmp(type, str)) { \
return UPB_DESCRIPTOR_TYPE_##upb; \
}
CONVERT(FLOAT, "float");
CONVERT(DOUBLE, "double");
CONVERT(BOOL, "bool");
CONVERT(STRING, "string");
CONVERT(BYTES, "bytes");
CONVERT(MESSAGE, "message");
CONVERT(GROUP, "group");
CONVERT(ENUM, "enum");
CONVERT(INT32, "int32");
CONVERT(INT64, "int64");
CONVERT(UINT32, "uint32");
CONVERT(UINT64, "uint64");
CONVERT(SINT32, "sint32");
CONVERT(SINT64, "sint64");
CONVERT(FIXED32, "fixed32");
CONVERT(FIXED64, "fixed64");
CONVERT(SFIXED32, "sfixed32");
CONVERT(SFIXED64, "sfixed64");
#undef CONVERT
static zend_function_entry enum_descriptor_methods[] = {
ZEND_FE_END
};
zend_error(E_ERROR, "Unknown field type.");
return 0;
}
DEFINE_CLASS(EnumDescriptor, enum_descriptor,
"Google\\Protobuf\\Internal\\EnumDescriptor");
static void field_descriptor_type_set(const upb_fielddef* fielddef,
const char *type) {
upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
upb_fielddef_setdescriptortype(mut_def, string_to_descriptortype(type));
static void enum_descriptor_free_c(EnumDescriptor *self TSRMLS_DC) {
}
static void field_descriptor_number_set(const upb_fielddef* fielddef,
int number) {
upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
CHECK_UPB(upb_fielddef_setnumber(mut_def, number, &status),
"Error setting field number");
static void enum_descriptor_init_c_instance(EnumDescriptor *self TSRMLS_DC) {
zend_object_std_init(&self->std, enum_descriptor_type TSRMLS_CC);
self->enumdef = NULL;
self->klass = NULL;
}
// -----------------------------------------------------------------------------
// MessageBuilderContext
// FieldDescriptor
// -----------------------------------------------------------------------------
static zend_function_entry message_builder_context_methods[] = {
PHP_ME(MessageBuilderContext, finalizeToPool, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MessageBuilderContext, optional, NULL, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
DEFINE_CLASS(MessageBuilderContext, message_builder_context,
"Google\\Protobuf\\Internal\\MessageBuilderContext");
void message_builder_context_free_c(MessageBuilderContext *context TSRMLS_DC) {
zval_ptr_dtor(&context->descriptor);
zval_ptr_dtor(&context->pool);
}
void message_builder_context_init_c_instance(
MessageBuilderContext *context TSRMLS_DC) {
zend_object_std_init(&context->std, message_builder_context_type TSRMLS_CC);
PROTOBUF_CREATE_ZEND_WRAPPER(Descriptor, descriptor, context->descriptor,
desc);
}
upb_fieldtype_t to_fieldtype(upb_descriptortype_t type) {
switch (type) {
#define CASE(descriptor_type, type) \
case UPB_DESCRIPTOR_TYPE_##descriptor_type: \
return UPB_TYPE_##type;
CASE(FLOAT, FLOAT);
CASE(DOUBLE, DOUBLE);
CASE(BOOL, BOOL);
CASE(STRING, STRING);
CASE(BYTES, BYTES);
CASE(MESSAGE, MESSAGE);
CASE(GROUP, MESSAGE);
CASE(ENUM, ENUM);
CASE(INT32, INT32);
CASE(INT64, INT64);
CASE(UINT32, UINT32);
CASE(UINT64, UINT64);
CASE(SINT32, INT32);
CASE(SINT64, INT64);
CASE(FIXED32, UINT32);
CASE(FIXED64, UINT64);
CASE(SFIXED32, INT32);
CASE(SFIXED64, INT64);
static void msgdef_add_field(Descriptor *desc, upb_label_t upb_label,
const char *name, const char *type, int number,
const char *type_class) {
upb_fielddef *fielddef = upb_fielddef_new(&fielddef);
upb_fielddef_setpacked(fielddef, false);
field_descriptor_label_set(fielddef, upb_label);
field_descriptor_name_set(fielddef, name);
field_descriptor_type_set(fielddef, type);
field_descriptor_number_set(fielddef, number);
// // if (type_class != Qnil) {
// // if (TYPE(type_class) != T_STRING) {
// // rb_raise(rb_eArgError, "Expected string for type class");
// // }
// // // Make it an absolute type name by prepending a dot.
// // type_class = rb_str_append(rb_str_new2("."), type_class);
// // rb_funcall(fielddef, rb_intern("submsg_name="), 1, type_class);
// // }
descriptor_add_field(desc, fielddef);
}
#undef CONVERT
PHP_METHOD(MessageBuilderContext, optional) {
MessageBuilderContext *self = php_to_message_builder_context(getThis() TSRMLS_CC);
Descriptor *desc = php_to_descriptor(self->descriptor TSRMLS_CC);
// VALUE name, type, number, type_class;
const char *name, *type, *type_class;
int number, name_str_len, type_str_len, type_class_str_len;
if (ZEND_NUM_ARGS() == 3) {
if (zend_parse_parameters(3 TSRMLS_CC, "ssl", &name,
&name_str_len, &type, &type_str_len, &number) == FAILURE) {
return;
}
} else {
if (zend_parse_parameters(4 TSRMLS_CC, "ssls", &name,
&name_str_len, &type, &type_str_len, &number, &type_class,
&type_class_str_len) == FAILURE) {
return;
}
}
msgdef_add_field(desc, UPB_LABEL_OPTIONAL, name, type, number, type_class);
zval_copy_ctor(getThis());
RETURN_ZVAL(getThis(), 1, 0);
}
PHP_METHOD(MessageBuilderContext, finalizeToPool) {
MessageBuilderContext *self = php_to_message_builder_context(getThis() TSRMLS_CC);
DescriptorPool *pool = php_to_descriptor_pool(self->pool TSRMLS_CC);
Descriptor* desc = php_to_descriptor(self->descriptor TSRMLS_CC);
Z_ADDREF_P(self->descriptor);
zend_hash_next_index_insert(pool->pending_list, &self->descriptor,
sizeof(zval *), NULL);
RETURN_ZVAL(self->pool, 1, 0);
zend_error(E_ERROR, "Unknown field type.");
return 0;
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,470 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <ext/spl/spl_iterators.h>
#include <Zend/zend_API.h>
#include <Zend/zend_interfaces.h>
#include "protobuf.h"
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetGet, 0, 0, 1)
ZEND_ARG_INFO(0, index)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_offsetSet, 0, 0, 2)
ZEND_ARG_INFO(0, index)
ZEND_ARG_INFO(0, newval)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_void, 0)
ZEND_END_ARG_INFO()
// Utilities
void* upb_value_memory(upb_value* v) {
return (void*)(&v->val);
}
// -----------------------------------------------------------------------------
// Basic map operations on top of upb's strtable.
//
// Note that we roll our own `Map` container here because, as for
// `RepeatedField`, we want a strongly-typed container. This is so that any user
// errors due to incorrect map key or value types are raised as close as
// possible to the error site, rather than at some deferred point (e.g.,
// serialization).
//
// We build our `Map` on top of upb_strtable so that we're able to take
// advantage of the native_slot storage abstraction, as RepeatedField does.
// (This is not quite a perfect mapping -- see the key conversions below -- but
// gives us full support and error-checking for all value types for free.)
// -----------------------------------------------------------------------------
// Map values are stored using the native_slot abstraction (as with repeated
// field values), but keys are a bit special. Since we use a strtable, we need
// to store keys as sequences of bytes such that equality of those bytes maps
// one-to-one to equality of keys. We store strings directly (i.e., they map to
// their own bytes) and integers as native integers (using the native_slot
// abstraction).
// Note that there is another tradeoff here in keeping string keys as native
// strings rather than PHP strings: traversing the Map requires conversion to
// PHP string values on every traversal, potentially creating more garbage. We
// should consider ways to cache a PHP version of the key if this becomes an
// issue later.
// Forms a key to use with the underlying strtable from a PHP key value. |buf|
// must point to TABLE_KEY_BUF_LENGTH bytes of temporary space, used to
// construct a key byte sequence if needed. |out_key| and |out_length| provide
// the resulting key data/length.
#define TABLE_KEY_BUF_LENGTH 8 // sizeof(uint64_t)
static bool table_key(Map* self, zval* key,
char* buf,
const char** out_key,
size_t* out_length) {
switch (self->key_type) {
case UPB_TYPE_STRING:
if (!protobuf_convert_to_string(key)) {
return false;
}
if (!is_structurally_valid_utf8(Z_STRVAL_P(key), Z_STRLEN_P(key))) {
zend_error(E_USER_ERROR, "Given key is not UTF8 encoded.");
return false;
}
*out_key = Z_STRVAL_P(key);
*out_length = Z_STRLEN_P(key);
break;
#define CASE_TYPE(upb_type, type, c_type, php_type) \
case UPB_TYPE_##upb_type: { \
c_type type##_value; \
if (!protobuf_convert_to_##type(key, &type##_value)) { \
return false; \
} \
native_slot_set(self->key_type, NULL, buf, key); \
*out_key = buf; \
*out_length = native_slot_size(self->key_type); \
break; \
}
CASE_TYPE(BOOL, bool, int8_t, BOOL)
CASE_TYPE(INT32, int32, int32_t, LONG)
CASE_TYPE(INT64, int64, int64_t, LONG)
CASE_TYPE(UINT32, uint32, uint32_t, LONG)
CASE_TYPE(UINT64, uint64, uint64_t, LONG)
#undef CASE_TYPE
default:
// Map constructor should not allow a Map with another key type to be
// constructed.
assert(false);
break;
}
return true;
}
// -----------------------------------------------------------------------------
// MapField methods
// -----------------------------------------------------------------------------
static zend_function_entry map_field_methods[] = {
PHP_ME(MapField, __construct, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MapField, offsetExists, arginfo_offsetGet, ZEND_ACC_PUBLIC)
PHP_ME(MapField, offsetGet, arginfo_offsetGet, ZEND_ACC_PUBLIC)
PHP_ME(MapField, offsetSet, arginfo_offsetSet, ZEND_ACC_PUBLIC)
PHP_ME(MapField, offsetUnset, arginfo_offsetGet, ZEND_ACC_PUBLIC)
PHP_ME(MapField, count, arginfo_void, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
// -----------------------------------------------------------------------------
// MapField creation/desctruction
// -----------------------------------------------------------------------------
zend_class_entry* map_field_type;
zend_object_handlers* map_field_handlers;
static map_begin_internal(Map *map, MapIter *iter) {
iter->self = map;
upb_strtable_begin(&iter->it, &map->table);
}
static HashTable *map_field_get_gc(zval *object, zval ***table,
int *n TSRMLS_DC) {
// TODO(teboring): Unfortunately, zend engine does not support garbage
// collection for custom array. We have to use zend engine's native array
// instead.
*table = NULL;
*n = 0;
return NULL;
}
void map_field_init(TSRMLS_D) {
zend_class_entry class_type;
const char* class_name = "Google\\Protobuf\\Internal\\MapField";
INIT_CLASS_ENTRY_EX(class_type, class_name, strlen(class_name),
map_field_methods);
map_field_type = zend_register_internal_class(&class_type TSRMLS_CC);
map_field_type->create_object = map_field_create;
zend_class_implements(map_field_type TSRMLS_CC, 2, spl_ce_ArrayAccess,
spl_ce_Countable);
map_field_handlers = PEMALLOC(zend_object_handlers);
memcpy(map_field_handlers, zend_get_std_object_handlers(),
sizeof(zend_object_handlers));
map_field_handlers->get_gc = map_field_get_gc;
}
zend_object_value map_field_create(zend_class_entry *ce TSRMLS_DC) {
zend_object_value retval = {0};
Map *intern;
intern = emalloc(sizeof(Map));
memset(intern, 0, sizeof(Map));
zend_object_std_init(&intern->std, ce TSRMLS_CC);
object_properties_init(&intern->std, ce);
// Table value type is always UINT64: this ensures enough space to store the
// native_slot value.
if (!upb_strtable_init(&intern->table, UPB_CTYPE_UINT64)) {
zend_error(E_USER_ERROR, "Could not allocate table.");
}
retval.handle = zend_objects_store_put(
intern, (zend_objects_store_dtor_t)zend_objects_destroy_object,
(zend_objects_free_object_storage_t)map_field_free, NULL TSRMLS_CC);
retval.handlers = map_field_handlers;
return retval;
}
void map_field_free(void *object TSRMLS_DC) {
Map *map = (Map *)object;
switch (map->value_type) {
case UPB_TYPE_MESSAGE:
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
MapIter it;
int len;
for (map_begin_internal(map, &it); !map_done(&it); map_next(&it)) {
upb_value value = map_iter_value(&it, &len);
void *mem = upb_value_memory(&value);
zval_ptr_dtor(mem);
}
break;
}
default:
break;
}
upb_strtable_uninit(&map->table);
zend_object_std_dtor(&map->std TSRMLS_CC);
efree(object);
}
void map_field_create_with_type(zend_class_entry *ce, const upb_fielddef *field,
zval **map_field TSRMLS_DC) {
MAKE_STD_ZVAL(*map_field);
Z_TYPE_PP(map_field) = IS_OBJECT;
Z_OBJVAL_PP(map_field) =
map_field_type->create_object(map_field_type TSRMLS_CC);
Map* intern =
(Map*)zend_object_store_get_object(*map_field TSRMLS_CC);
const upb_fielddef *key_field = map_field_key(field);
const upb_fielddef *value_field = map_field_value(field);
intern->key_type = upb_fielddef_type(key_field);
intern->value_type = upb_fielddef_type(value_field);
intern->msg_ce = field_type_class(value_field);
}
static void map_field_free_element(void *object) {
}
// -----------------------------------------------------------------------------
// MapField Handlers
// -----------------------------------------------------------------------------
static bool *map_field_read_dimension(zval *object, zval *key, int type,
zval **retval TSRMLS_DC) {
Map *intern =
(Map *)zend_object_store_get_object(object TSRMLS_CC);
char keybuf[TABLE_KEY_BUF_LENGTH];
const char* keyval = NULL;
size_t length = 0;
upb_value v;
#ifndef NDEBUG
v.ctype = UPB_CTYPE_UINT64;
#endif
if (!table_key(intern, key, keybuf, &keyval, &length)) {
return false;
}
if (upb_strtable_lookup2(&intern->table, keyval, length, &v)) {
void* mem = upb_value_memory(&v);
native_slot_get(intern->value_type, mem, retval TSRMLS_CC);
return true;
} else {
zend_error(E_USER_ERROR, "Given key doesn't exist.");
return false;
}
}
bool map_index_set(Map *intern, const char* keyval, int length, upb_value v) {
// Replace any existing value by issuing a 'remove' operation first.
upb_strtable_remove2(&intern->table, keyval, length, NULL);
if (!upb_strtable_insert2(&intern->table, keyval, length, v)) {
zend_error(E_USER_ERROR, "Could not insert into table");
return false;
}
return true;
}
static bool map_field_write_dimension(zval *object, zval *key,
zval *value TSRMLS_DC) {
Map *intern = (Map *)zend_object_store_get_object(object TSRMLS_CC);
char keybuf[TABLE_KEY_BUF_LENGTH];
const char* keyval = NULL;
size_t length = 0;
upb_value v;
void* mem;
if (!table_key(intern, key, keybuf, &keyval, &length)) {
return false;
}
mem = upb_value_memory(&v);
memset(mem, 0, native_slot_size(intern->value_type));
if(!native_slot_set(intern->value_type, intern->msg_ce, mem, value)) {
return false;
}
#ifndef NDEBUG
v.ctype = UPB_CTYPE_UINT64;
#endif
// Replace any existing value by issuing a 'remove' operation first.
upb_strtable_remove2(&intern->table, keyval, length, NULL);
if (!upb_strtable_insert2(&intern->table, keyval, length, v)) {
zend_error(E_USER_ERROR, "Could not insert into table");
return false;
}
return true;
}
static bool map_field_unset_dimension(zval *object, zval *key TSRMLS_DC) {
Map *intern = (Map *)zend_object_store_get_object(object TSRMLS_CC);
char keybuf[TABLE_KEY_BUF_LENGTH];
const char* keyval = NULL;
size_t length = 0;
upb_value v;
if (!table_key(intern, key, keybuf, &keyval, &length)) {
return false;
}
#ifndef NDEBUG
v.ctype = UPB_CTYPE_UINT64;
#endif
upb_strtable_remove2(&intern->table, keyval, length, &v);
return true;
}
// -----------------------------------------------------------------------------
// PHP MapField Methods
// -----------------------------------------------------------------------------
PHP_METHOD(MapField, __construct) {
long key_type, value_type;
zend_class_entry* klass = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|C", &key_type,
&value_type, &klass) == FAILURE) {
return;
}
Map* intern =
(Map*)zend_object_store_get_object(getThis() TSRMLS_CC);
intern->key_type = to_fieldtype(key_type);
intern->value_type = to_fieldtype(value_type);
intern->msg_ce = klass;
// Check that the key type is an allowed type.
switch (intern->key_type) {
case UPB_TYPE_INT32:
case UPB_TYPE_INT64:
case UPB_TYPE_UINT32:
case UPB_TYPE_UINT64:
case UPB_TYPE_BOOL:
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
// These are OK.
break;
default:
zend_error(E_USER_ERROR, "Invalid key type for map.");
}
}
PHP_METHOD(MapField, offsetExists) {
zval *key;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &key) ==
FAILURE) {
return;
}
Map *intern = (Map *)zend_object_store_get_object(getThis() TSRMLS_CC);
char keybuf[TABLE_KEY_BUF_LENGTH];
const char* keyval = NULL;
size_t length = 0;
upb_value v;
#ifndef NDEBUG
v.ctype = UPB_CTYPE_UINT64;
#endif
if (!table_key(intern, key, keybuf, &keyval, &length)) {
return false;
}
RETURN_BOOL(upb_strtable_lookup2(&intern->table, keyval, length, &v));
}
PHP_METHOD(MapField, offsetGet) {
zval *index, *value;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) ==
FAILURE) {
return;
}
map_field_read_dimension(getThis(), index, BP_VAR_R,
return_value_ptr TSRMLS_CC);
}
PHP_METHOD(MapField, offsetSet) {
zval *index, *value;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &index, &value) ==
FAILURE) {
return;
}
map_field_write_dimension(getThis(), index, value TSRMLS_CC);
}
PHP_METHOD(MapField, offsetUnset) {
zval *index;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &index) ==
FAILURE) {
return;
}
map_field_unset_dimension(getThis(), index TSRMLS_CC);
}
PHP_METHOD(MapField, count) {
Map *intern =
(MapField *)zend_object_store_get_object(getThis() TSRMLS_CC);
if (zend_parse_parameters_none() == FAILURE) {
return;
}
RETURN_LONG(upb_strtable_count(&intern->table));
}
// -----------------------------------------------------------------------------
// Map Iterator
// -----------------------------------------------------------------------------
void map_begin(zval *map_php, MapIter *iter) {
Map *self = UNBOX(Map, map_php);
map_begin_internal(self, iter);
}
void map_next(MapIter *iter) {
upb_strtable_next(&iter->it);
}
bool map_done(MapIter *iter) {
return upb_strtable_done(&iter->it);
}
const char *map_iter_key(MapIter *iter, int *len) {
*len = upb_strtable_iter_keylength(&iter->it);
return upb_strtable_iter_key(&iter->it);
}
upb_value map_iter_value(MapIter *iter, int *len) {
*len = native_slot_size(iter->self->value_type);
return upb_strtable_iter_value(&iter->it);
}

@ -29,245 +29,219 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <php.h>
#include <stdlib.h>
#include "protobuf.h"
// -----------------------------------------------------------------------------
// Class/module creation from msgdefs and enumdefs, respectively.
// -----------------------------------------------------------------------------
void* message_data(void* msg) {
return ((uint8_t *)msg) + sizeof(MessageHeader);
}
void message_set_property(zval* object, zval* field_name, zval* value,
const zend_literal* key TSRMLS_DC) {
const upb_fielddef* field;
MessageHeader* self = zend_object_store_get_object(object TSRMLS_CC);
CHECK_TYPE(field_name, IS_STRING);
field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(field_name));
if (field == NULL) {
zend_error(E_ERROR, "Unknown field: %s", Z_STRVAL_P(field_name));
}
layout_set(self->descriptor->layout, message_data(self), field, value);
}
zval* message_get_property(zval* object, zval* member, int type,
const zend_literal* key TSRMLS_DC) {
MessageHeader* self =
(MessageHeader*)zend_object_store_get_object(object TSRMLS_CC);
CHECK_TYPE(member, IS_STRING);
const upb_fielddef* field;
field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member));
if (field == NULL) {
return EG(uninitialized_zval_ptr);
}
zval* retval = layout_get(self->descriptor->layout, message_data(self), field TSRMLS_CC);
return retval;
}
static zend_class_entry* message_type;
zend_object_handlers* message_handlers;
static zend_function_entry message_methods[] = {
PHP_ME(Message, encode, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Message, decode, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED)
PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED)
{NULL, NULL, NULL}
};
/* stringsink *****************************************************************/
// Forward declare static functions.
// This should probably be factored into a common upb component.
static void message_set_property(zval* object, zval* member, zval* value,
const zend_literal* key TSRMLS_DC);
static zval* message_get_property(zval* object, zval* member, int type,
const zend_literal* key TSRMLS_DC);
static zval** message_get_property_ptr_ptr(zval* object, zval* member, int type,
const zend_literal* key TSRMLS_DC);
typedef struct {
upb_byteshandler handler;
upb_bytessink sink;
char *ptr;
size_t len, size;
} stringsink;
static void *stringsink_start(void *_sink, const void *hd, size_t size_hint) {
stringsink *sink = _sink;
sink->len = 0;
return sink;
}
static zend_object_value message_create(zend_class_entry* ce TSRMLS_DC);
static void message_free(void* object TSRMLS_DC);
static size_t stringsink_string(void *_sink, const void *hd, const char *ptr,
size_t len, const upb_bufhandle *handle) {
stringsink *sink = _sink;
size_t new_size = sink->size;
// -----------------------------------------------------------------------------
// PHP Message Handlers
// -----------------------------------------------------------------------------
UPB_UNUSED(hd);
UPB_UNUSED(handle);
void message_init(TSRMLS_D) {
zend_class_entry class_type;
INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\Message",
message_methods);
message_type = zend_register_internal_class(&class_type TSRMLS_CC);
message_handlers = PEMALLOC(zend_object_handlers);
memcpy(message_handlers, zend_get_std_object_handlers(),
sizeof(zend_object_handlers));
message_handlers->write_property = message_set_property;
message_handlers->read_property = message_get_property;
message_handlers->get_property_ptr_ptr = message_get_property_ptr_ptr;
}
while (sink->len + len > new_size) {
new_size *= 2;
static void message_set_property(zval* object, zval* member, zval* value,
const zend_literal* key TSRMLS_DC) {
if (Z_TYPE_P(member) != IS_STRING) {
zend_error(E_USER_ERROR, "Unexpected type for field name");
return;
}
if (new_size != sink->size) {
sink->ptr = realloc(sink->ptr, new_size);
sink->size = new_size;
if (Z_OBJCE_P(object) != EG(scope)) {
// User cannot set property directly (e.g., $m->a = 1)
zend_error(E_USER_ERROR, "Cannot access private property.");
return;
}
memcpy(sink->ptr + sink->len, ptr, len);
sink->len += len;
const upb_fielddef* field;
MessageHeader* self = zend_object_store_get_object(object TSRMLS_CC);
field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member));
if (field == NULL) {
zend_error(E_USER_ERROR, "Unknown field: %s", Z_STRVAL_P(member));
}
return len;
layout_set(self->descriptor->layout, self, field, value);
}
void stringsink_init(stringsink *sink) {
upb_byteshandler_init(&sink->handler);
upb_byteshandler_setstartstr(&sink->handler, stringsink_start, NULL);
upb_byteshandler_setstring(&sink->handler, stringsink_string, NULL);
static zval* message_get_property(zval* object, zval* member, int type,
const zend_literal* key TSRMLS_DC) {
if (Z_TYPE_P(member) != IS_STRING) {
zend_error(E_USER_ERROR, "Property name has to be a string.");
return EG(uninitialized_zval_ptr);
}
upb_bytessink_reset(&sink->sink, &sink->handler, sink);
if (Z_OBJCE_P(object) != EG(scope)) {
// User cannot get property directly (e.g., $a = $m->a)
zend_error(E_USER_ERROR, "Cannot access private property.");
return EG(uninitialized_zval_ptr);
}
sink->size = 32;
sink->ptr = malloc(sink->size);
sink->len = 0;
}
zend_property_info* property_info = NULL;
void stringsink_uninit(stringsink *sink) { free(sink->ptr); }
// Stack-allocated context during an encode/decode operation. Contains the upb
// environment and its stack-based allocator, an initial buffer for allocations
// to avoid malloc() when possible, and a template for PHP exception messages
// if any error occurs.
#define STACK_ENV_STACKBYTES 4096
typedef struct {
upb_env env;
upb_seededalloc alloc;
const char *php_error_template;
char allocbuf[STACK_ENV_STACKBYTES];
} stackenv;
static void stackenv_init(stackenv* se, const char* errmsg);
static void stackenv_uninit(stackenv* se);
// Callback invoked by upb if any error occurs during parsing or serialization.
static bool env_error_func(void* ud, const upb_status* status) {
stackenv* se = ud;
// Free the env -- rb_raise will longjmp up the stack past the encode/decode
// function so it would not otherwise have been freed.
stackenv_uninit(se);
// TODO(teboring): have a way to verify that this is actually a parse error,
// instead of just throwing "parse error" unconditionally.
zend_error(E_ERROR, se->php_error_template);
// Never reached.
return false;
}
// All properties should have been declared in the generated code and have
// corresponding zvals in properties_table.
ulong h = zend_get_hash_value(Z_STRVAL_P(member), Z_STRLEN_P(member) + 1);
if (zend_hash_quick_find(&Z_OBJCE_P(object)->properties_info,
Z_STRVAL_P(member), Z_STRLEN_P(member) + 1, h,
(void**)&property_info) != SUCCESS) {
zend_error(E_USER_ERROR, "Property does not exist.");
return EG(uninitialized_zval_ptr);
}
MessageHeader* self =
(MessageHeader*)zend_object_store_get_object(object TSRMLS_CC);
static void stackenv_init(stackenv* se, const char* errmsg) {
se->php_error_template = errmsg;
upb_env_init(&se->env);
upb_seededalloc_init(&se->alloc, &se->allocbuf, STACK_ENV_STACKBYTES);
upb_env_setallocfunc(&se->env, upb_seededalloc_getallocfunc(&se->alloc),
&se->alloc);
upb_env_seterrorfunc(&se->env, env_error_func, se);
const upb_fielddef* field;
field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member));
if (field == NULL) {
return EG(uninitialized_zval_ptr);
}
return layout_get(
self->descriptor->layout, message_data(self), field,
&Z_OBJ_P(object)->properties_table[property_info->offset] TSRMLS_CC);
}
static void stackenv_uninit(stackenv* se) {
upb_env_uninit(&se->env);
upb_seededalloc_uninit(&se->alloc);
static zval** message_get_property_ptr_ptr(zval* object, zval* member, int type,
const zend_literal* key TSRMLS_DC) {
return NULL;
}
// -----------------------------------------------------------------------------
// Message
// C Message Utilities
// -----------------------------------------------------------------------------
static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) {
if (desc->pb_serialize_handlers == NULL) {
desc->pb_serialize_handlers =
upb_pb_encoder_newhandlers(desc->msgdef, &desc->pb_serialize_handlers);
}
return desc->pb_serialize_handlers;
void* message_data(void* msg) {
return ((uint8_t*)msg) + sizeof(MessageHeader);
}
PHP_METHOD(Message, encode) {
Descriptor* desc = (Descriptor*)zend_object_store_get_object(
CE_STATIC_MEMBERS(Z_OBJCE_P(getThis()))[0] TSRMLS_CC);
stringsink sink;
stringsink_init(&sink);
{
const upb_handlers* serialize_handlers = msgdef_pb_serialize_handlers(desc);
static void message_free(void* object TSRMLS_DC) {
MessageHeader* msg = (MessageHeader*)object;
int i;
stackenv se;
upb_pb_encoder* encoder;
stackenv_init(&se, "Error occurred during encoding: %s");
encoder = upb_pb_encoder_create(&se.env, serialize_handlers, &sink.sink);
putmsg(getThis(), desc, upb_pb_encoder_input(encoder), 0);
RETVAL_STRINGL(sink.ptr, sink.len, 1);
stackenv_uninit(&se);
stringsink_uninit(&sink);
for (i = 0; i < msg->std.ce->default_properties_count; i++) {
zval_ptr_dtor(&msg->std.properties_table[i]);
}
efree(msg->std.properties_table);
efree(msg);
}
void message_free(void * object TSRMLS_DC) {
FREE(object);
}
zend_object_value message_create(zend_class_entry* ce TSRMLS_DC) {
static zend_object_value message_create(zend_class_entry* ce TSRMLS_DC) {
zend_object_value return_value;
zval* php_descriptor = get_def_obj(ce);
zval* php_descriptor = get_ce_obj(ce);
Descriptor* desc = zend_object_store_get_object(php_descriptor TSRMLS_CC);
MessageHeader* msg = (MessageHeader*)ALLOC_N(
uint8_t, sizeof(MessageHeader) + desc->layout->size);
memset(message_data(msg), 0, desc->layout->size);
// We wrap first so that everything in the message object is GC-rooted in case
// a collection happens during object creation in layout_init().
// We wrap first so that everything in the message object is GC-rooted in
// case a collection happens during object creation in layout_init().
msg->descriptor = desc;
layout_init(desc->layout, message_data(msg));
zend_object_std_init(&msg->std, ce TSRMLS_CC);
object_properties_init(&msg->std, ce);
layout_init(desc->layout, message_data(msg), msg->std.properties_table);
return_value.handle = zend_objects_store_put(
msg, (zend_objects_store_dtor_t)zend_objects_destroy_object,
message_free, NULL TSRMLS_CC);
msg, (zend_objects_store_dtor_t)zend_objects_destroy_object, message_free,
NULL TSRMLS_CC);
return_value.handlers = PROTOBUF_G(message_handlers);
return_value.handlers = message_handlers;
return return_value;
}
const zend_class_entry* build_class_from_descriptor(
zval* php_descriptor TSRMLS_DC) {
Descriptor* desc = php_to_descriptor(php_descriptor);
void build_class_from_descriptor(zval* php_descriptor TSRMLS_DC) {
Descriptor* desc = UNBOX(Descriptor, php_descriptor);
// Map entries don't have existing php class.
if (upb_msgdef_mapentry(desc->msgdef)) {
return;
}
zend_class_entry* registered_ce = desc->klass;
if (desc->layout == NULL) {
MessageLayout* layout = create_layout(desc->msgdef);
desc->layout = layout;
}
// TODO(teboring): Add it back.
// if (desc->fill_method == NULL) {
// desc->fill_method = new_fillmsg_decodermethod(desc, &desc->fill_method);
// }
const char* name = upb_msgdef_fullname(desc->msgdef);
if (name == NULL) {
zend_error(E_ERROR, "Descriptor does not have assigned name.");
registered_ce->create_object = message_create;
}
// -----------------------------------------------------------------------------
// PHP Methods
// -----------------------------------------------------------------------------
PHP_METHOD(Message, readOneof) {
long index;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) ==
FAILURE) {
return;
}
zend_class_entry class_entry;
INIT_CLASS_ENTRY_EX(class_entry, name, strlen(name), message_methods);
zend_class_entry* registered_ce =
zend_register_internal_class(&class_entry TSRMLS_CC);
MessageHeader* msg =
(MessageHeader*)zend_object_store_get_object(getThis() TSRMLS_CC);
add_def_obj(registered_ce, php_descriptor);
const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index);
if (PROTOBUF_G(message_handlers) == NULL) {
PROTOBUF_G(message_handlers) = ALLOC(zend_object_handlers);
memcpy(PROTOBUF_G(message_handlers), zend_get_std_object_handlers(),
sizeof(zend_object_handlers));
PROTOBUF_G(message_handlers)->write_property = message_set_property;
PROTOBUF_G(message_handlers)->read_property = message_get_property;
int property_cache_index =
msg->descriptor->layout->fields[upb_fielddef_index(field)].cache_index;
zval** cache_ptr = &(msg->std.properties_table)[property_cache_index];
layout_get(msg->descriptor->layout, message_data(msg), field,
return_value_ptr TSRMLS_CC);
}
PHP_METHOD(Message, writeOneof) {
long index;
zval* value;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz", &index, &value) ==
FAILURE) {
return;
}
registered_ce->create_object = message_create;
MessageHeader* msg =
(MessageHeader*)zend_object_store_get_object(getThis() TSRMLS_CC);
const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index);
layout_set(msg->descriptor->layout, msg, field, value TSRMLS_CC);
}

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.9.5" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
<name>protobuf</name>
<channel>pecl.php.net</channel>
<summary>Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data.</summary>
<description>https://developers.google.com/protocol-buffers/</description>
<lead>
<name>Bo Yang</name>
<user>teboring</user>
<email>protobuf-opensource@google.com</email>
<active>yes</active>
</lead>
<date>2016-09-02</date>
<time>16:06:07</time>
<version>
<release>3.1.0</release>
<api>3.1.0</api>
</version>
<stability>
<release>alpha</release>
<api>alpha</api>
</stability>
<license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
<notes>
First alpha release.
</notes>
<contents>
<dir baseinstalldir="/" name="/">
<file baseinstalldir="/" name="config.m4" role="src" />
<file baseinstalldir="/" name="array.c" role="src" />
<file baseinstalldir="/" name="def.c" role="src" />
<file baseinstalldir="/" name="encode_decode.c" role="src" />
<file baseinstalldir="/" name="map.c" role="src" />
<file baseinstalldir="/" name="message.c" role="src" />
<file baseinstalldir="/" name="protobuf.c" role="src" />
<file baseinstalldir="/" name="protobuf.h" role="src" />
<file baseinstalldir="/" name="storage.c" role="src" />
<file baseinstalldir="/" name="type_check.c" role="src" />
<file baseinstalldir="/" name="upb.c" role="src" />
<file baseinstalldir="/" name="upb.h" role="src" />
<file baseinstalldir="/" name="utf8.c" role="src" />
<file baseinstalldir="/" name="utf8.h" role="src" />
</dir>
</contents>
<dependencies>
<required>
<php>
<min>5.6.0</min>
</php>
<pearinstaller>
<min>1.4.0</min>
</pearinstaller>
</required>
</dependencies>
<providesextension>protobuf</providesextension>
<extsrcrelease />
<changelog>
<release>
<version>
<release>3.1.0</release>
<api>3.1.0</api>
</version>
<stability>
<release>alpha</release>
<api>alpha</api>
</stability>
<date>2016-09-02</date>
<license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
<notes>
First alpha release
</notes>
</release>
</changelog>
</package>

@ -1,3 +1,33 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protobuf.h"
#include <zend_hash.h>
@ -5,56 +35,81 @@
ZEND_DECLARE_MODULE_GLOBALS(protobuf)
static PHP_GINIT_FUNCTION(protobuf);
static PHP_GSHUTDOWN_FUNCTION(protobuf);
static PHP_RINIT_FUNCTION(protobuf);
static PHP_RSHUTDOWN_FUNCTION(protobuf);
static PHP_MINIT_FUNCTION(protobuf);
static PHP_MSHUTDOWN_FUNCTION(protobuf);
// -----------------------------------------------------------------------------
// Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor
// instances.
static HashTable* upb_def_to_php_obj_map;
// Global map from message/enum's php class entry to corresponding wrapper
// Descriptor/EnumDescriptor instances.
static HashTable* ce_to_php_obj_map;
// -----------------------------------------------------------------------------
// Global maps.
// -----------------------------------------------------------------------------
void add_def_obj(const void* def, zval* value) {
uint nIndex = (ulong)def & PROTOBUF_G(upb_def_to_php_obj_map).nTableMask;
static void add_to_table(HashTable* t, const void* def, void* value) {
uint nIndex = (ulong)def & t->nTableMask;
zval* pDest = NULL;
Z_ADDREF_P(value);
zend_hash_index_update(&PROTOBUF_G(upb_def_to_php_obj_map), (zend_ulong)def,
&value, sizeof(zval*), &pDest);
zend_hash_index_update(t, (zend_ulong)def, &value, sizeof(zval*), &pDest);
}
zval* get_def_obj(const void* def) {
zval** value;
if (zend_hash_index_find(&PROTOBUF_G(upb_def_to_php_obj_map), (zend_ulong)def,
&value) == FAILURE) {
static void* get_from_table(const HashTable* t, const void* def) {
void** value;
if (zend_hash_index_find(t, (zend_ulong)def, (void**)&value) == FAILURE) {
zend_error(E_ERROR, "PHP object not found for given definition.\n");
return NULL;
}
return *value;
}
static void add_to_list(HashTable* t, void* value) {
zval* pDest = NULL;
zend_hash_next_index_insert(t, &value, sizeof(void*), &pDest);
}
void add_def_obj(const void* def, zval* value) {
Z_ADDREF_P(value);
add_to_table(upb_def_to_php_obj_map, def, value);
}
zval* get_def_obj(const void* def) {
return (zval*)get_from_table(upb_def_to_php_obj_map, def);
}
void add_ce_obj(const void* ce, zval* value) {
Z_ADDREF_P(value);
add_to_table(ce_to_php_obj_map, ce, value);
}
zval* get_ce_obj(const void* ce) {
return (zval*)get_from_table(ce_to_php_obj_map, ce);
}
// -----------------------------------------------------------------------------
// Utilities.
// -----------------------------------------------------------------------------
// define the function(s) we want to add
zend_function_entry protobuf_functions[] = {
ZEND_FE(get_generated_pool, NULL)
ZEND_FE_END
};
// "protobuf_functions" refers to the struct defined above
// we'll be filling in more of this later: you can use this to specify
// globals, php.ini info, startup and teardown functions, etc.
zend_module_entry protobuf_module_entry = {
STANDARD_MODULE_HEADER,
PHP_PROTOBUF_EXTNAME, // extension name
protobuf_functions, // function list
PHP_MINIT(protobuf), // process startup
NULL, // process shutdown
NULL, // request startup
NULL, // request shutdown
NULL, // extension info
PHP_PROTOBUF_EXTNAME, // extension name
protobuf_functions, // function list
PHP_MINIT(protobuf), // process startup
PHP_MSHUTDOWN(protobuf), // process shutdown
PHP_RINIT(protobuf), // request shutdown
PHP_RSHUTDOWN(protobuf), // request shutdown
NULL, // extension info
PHP_PROTOBUF_VERSION, // extension version
PHP_MODULE_GLOBALS(protobuf), // globals descriptor
PHP_GINIT(protobuf), // globals ctor
PHP_GINIT(protobuf), // globals ctor
PHP_GSHUTDOWN(protobuf), // globals dtor
NULL, // post deactivate
STANDARD_MODULE_PROPERTIES_EX
@ -65,25 +120,48 @@ ZEND_GET_MODULE(protobuf)
// global variables
static PHP_GINIT_FUNCTION(protobuf) {
protobuf_globals->generated_pool = NULL;
generated_pool = NULL;
protobuf_globals->message_handlers = NULL;
zend_hash_init(&protobuf_globals->upb_def_to_php_obj_map, 16, NULL,
ZVAL_PTR_DTOR, 0);
}
static PHP_GSHUTDOWN_FUNCTION(protobuf) {
if (protobuf_globals->generated_pool != NULL) {
FREE_ZVAL(protobuf_globals->generated_pool);
}
if (protobuf_globals->message_handlers != NULL) {
FREE(protobuf_globals->message_handlers);
}
static PHP_RINIT_FUNCTION(protobuf) {
ALLOC_HASHTABLE(upb_def_to_php_obj_map);
zend_hash_init(upb_def_to_php_obj_map, 16, NULL, ZVAL_PTR_DTOR, 0);
ALLOC_HASHTABLE(ce_to_php_obj_map);
zend_hash_init(ce_to_php_obj_map, 16, NULL, ZVAL_PTR_DTOR, 0);
generated_pool = NULL;
generated_pool_php = NULL;
}
static PHP_RSHUTDOWN_FUNCTION(protobuf) {
zend_hash_destroy(upb_def_to_php_obj_map);
FREE_HASHTABLE(upb_def_to_php_obj_map);
zend_hash_destroy(ce_to_php_obj_map);
FREE_HASHTABLE(ce_to_php_obj_map);
if (generated_pool_php != NULL) {
zval_dtor(generated_pool_php);
FREE_ZVAL(generated_pool_php);
}
zend_hash_destroy(&protobuf_globals->upb_def_to_php_obj_map);
}
PHP_MINIT_FUNCTION(protobuf) {
static PHP_MINIT_FUNCTION(protobuf) {
map_field_init(TSRMLS_C);
repeated_field_init(TSRMLS_C);
gpb_type_init(TSRMLS_C);
message_init(TSRMLS_C);
descriptor_pool_init(TSRMLS_C);
descriptor_init(TSRMLS_C);
message_builder_context_init(TSRMLS_C);
enum_descriptor_init(TSRMLS_C);
util_init(TSRMLS_C);
}
static PHP_MSHUTDOWN_FUNCTION(protobuf) {
PEFREE(message_handlers);
PEFREE(repeated_field_handlers);
PEFREE(map_field_handlers);
}

@ -1,46 +1,73 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__
#define __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__
#include <php.h>
// ubp.h has to be placed after php.h. Othwise, php.h will introduce NDEBUG.
#include "upb.h"
#define PHP_PROTOBUF_EXTNAME "protobuf"
#define PHP_PROTOBUF_VERSION "0.01"
// Forward decls.
// -----------------------------------------------------------------------------
// Forward Declaration
// ----------------------------------------------------------------------------
struct DescriptorPool;
struct Descriptor;
struct FieldDescriptor;
struct EnumDescriptor;
struct MessageLayout;
struct FieldDescriptor;
struct MessageField;
struct MessageHeader;
struct MessageBuilderContext;
struct EnumBuilderContext;
struct MessageLayout;
struct RepeatedField;
struct MapField;
typedef struct DescriptorPool DescriptorPool;
typedef struct Descriptor Descriptor;
typedef struct FieldDescriptor FieldDescriptor;
typedef struct OneofDescriptor OneofDescriptor;
typedef struct EnumDescriptor EnumDescriptor;
typedef struct MessageLayout MessageLayout;
typedef struct FieldDescriptor FieldDescriptor;
typedef struct MessageField MessageField;
typedef struct MessageHeader MessageHeader;
typedef struct MessageBuilderContext MessageBuilderContext;
typedef struct OneofBuilderContext OneofBuilderContext;
typedef struct EnumBuilderContext EnumBuilderContext;
extern zend_class_entry* builder_type;
extern zend_class_entry* descriptor_type;
extern zend_class_entry* message_builder_context_type;
typedef struct MessageLayout MessageLayout;
typedef struct RepeatedField RepeatedField;
typedef struct MapField MapField;
extern DescriptorPool* generated_pool; // The actual generated pool
// -----------------------------------------------------------------------------
// Globals.
// -----------------------------------------------------------------------------
ZEND_BEGIN_MODULE_GLOBALS(protobuf)
zval* generated_pool;
zend_object_handlers* message_handlers;
HashTable upb_def_to_php_obj_map;
ZEND_END_MODULE_GLOBALS(protobuf)
ZEND_DECLARE_MODULE_GLOBALS(protobuf)
@ -51,14 +78,31 @@ ZEND_DECLARE_MODULE_GLOBALS(protobuf)
#define PROTOBUF_G(v) (protobuf_globals.v)
#endif
// -----------------------------------------------------------------------------
// PHP functions and global variables.
// -----------------------------------------------------------------------------
// Init module and PHP classes.
void descriptor_init(TSRMLS_D);
void enum_descriptor_init(TSRMLS_D);
void descriptor_pool_init(TSRMLS_D);
void gpb_type_init(TSRMLS_D);
void map_field_init(TSRMLS_D);
void repeated_field_init(TSRMLS_D);
void util_init(TSRMLS_D);
void message_init(TSRMLS_D);
// Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor
// instances.
void add_def_obj(const void* def, zval* value);
zval* get_def_obj(const void* def);
PHP_MINIT_FUNCTION(protobuf);
// Global map from PHP class entries to wrapper Descriptor/EnumDescriptor
// instances.
void add_ce_obj(const void* ce, zval* value);
zval* get_ce_obj(const void* ce);
extern zend_class_entry* map_field_type;
extern zend_class_entry* repeated_field_type;
// -----------------------------------------------------------------------------
// PHP class structure.
// Descriptor.
// -----------------------------------------------------------------------------
struct DescriptorPool {
@ -67,72 +111,112 @@ struct DescriptorPool {
HashTable* pending_list;
};
PHP_METHOD(DescriptorPool, getGeneratedPool);
PHP_METHOD(DescriptorPool, internalAddGeneratedFile);
extern zval* generated_pool_php; // wrapper of generated pool
extern DescriptorPool* generated_pool; // The actual generated pool
struct Descriptor {
zend_object std;
const upb_msgdef* msgdef;
MessageLayout* layout;
// zval* klass; // begins as NULL
// const upb_handlers* fill_handlers;
// const upb_pbdecodermethod* fill_method;
zend_class_entry* klass; // begins as NULL
const upb_handlers* fill_handlers;
const upb_pbdecodermethod* fill_method;
const upb_handlers* pb_serialize_handlers;
// const upb_handlers* json_serialize_handlers;
// Handlers hold type class references for sub-message fields directly in some
// cases. We need to keep these rooted because they might otherwise be
// collected.
// zval_array typeclass_references;
};
extern zend_class_entry* descriptor_type;
void descriptor_name_set(Descriptor *desc, const char *name);
struct FieldDescriptor {
zend_object std;
const upb_fielddef* fielddef;
};
struct OneofDescriptor {
zend_object std;
const upb_oneofdef* oneofdef;
};
struct EnumDescriptor {
zend_object std;
const upb_enumdef* enumdef;
// zval* module; // begins as NULL
zend_class_entry* klass; // begins as NULL
// VALUE module; // begins as nil
};
extern zend_class_entry* enum_descriptor_type;
// -----------------------------------------------------------------------------
// Native slot storage abstraction.
// Message class creation.
// -----------------------------------------------------------------------------
#define NATIVE_SLOT_MAX_SIZE sizeof(uint64_t)
size_t native_slot_size(upb_fieldtype_t type);
void* message_data(void* msg);
#define MAP_KEY_FIELD 1
#define MAP_VALUE_FIELD 2
// Build PHP class for given descriptor. Instead of building from scratch, this
// function modifies existing class which has been partially defined in PHP
// code.
void build_class_from_descriptor(zval* php_descriptor TSRMLS_DC);
// Oneof case slot value to indicate that no oneof case is set. The value `0` is
// safe because field numbers are used as case identifiers, and no field can
// have a number of 0.
#define ONEOF_CASE_NONE 0
// These operate on a map field (i.e., a repeated field of submessages whose
// submessage type is a map-entry msgdef).
bool is_map_field(const upb_fielddef* field);
const upb_fielddef* map_field_key(const upb_fielddef* field);
const upb_fielddef* map_field_value(const upb_fielddef* field);
// These operate on a map-entry msgdef.
const upb_fielddef* map_entry_key(const upb_msgdef* msgdef);
const upb_fielddef* map_entry_value(const upb_msgdef* msgdef);
extern zend_object_handlers* message_handlers;
// -----------------------------------------------------------------------------
// Message layout / storage.
// -----------------------------------------------------------------------------
/*
* In c extension, each protobuf message is a zval instance. The zval instance
* is like union, which can be used to store int, string, zend_object_value and
* etc. For protobuf message, the zval instance is used to store the
* zend_object_value.
*
* The zend_object_value is composed of handlers and a handle to look up the
* actual stored data. The handlers are pointers to functions, e.g., read,
* write, and etc, to access properties.
*
* The actual data of protobuf messages is stored as MessageHeader in zend
* engine's central repository. Each MessageHeader instance is composed of a
* zend_object, a Descriptor instance and the real message data.
*
* For the reason that PHP's native types may not be large enough to store
* protobuf message's field (e.g., int64), all message's data is stored in
* custom memory layout and is indexed by the Descriptor instance.
*
* The zend_object contains the zend class entry and the properties table. The
* zend class entry contains all information about protobuf message's
* corresponding PHP class. The most useful information is the offset table of
* properties. Because read access to properties requires returning zval
* instance, we need to convert data from the custom layout to zval instance.
* Instead of creating zval instance for every read access, we use the zval
* instances in the properties table in the zend_object as cache. When
* accessing properties, the offset is needed to find the zval property in
* zend_object's properties table. These properties will be updated using the
* data from custom memory layout only when reading these properties.
*
* zval
* |-zend_object_value obj
* |-zend_object_handlers* handlers -> |-read_property_handler
* | |-write_property_handler
* | ++++++++++++++++++++++
* |-zend_object_handle handle -> + central repository +
* ++++++++++++++++++++++
* MessageHeader <-----------------|
* |-zend_object std
* | |-class_entry* ce -> class_entry
* | | |-HashTable properties_table (name->offset)
* | |-zval** properties_table <------------------------------|
* | |------> zval* property(cache)
* |-Descriptor* desc (name->offset)
* |-void** data <-----------|
* |-----------------------> void* property(data)
*
*/
#define MESSAGE_FIELD_NO_CASE ((size_t)-1)
struct MessageField {
size_t offset;
size_t case_offset; // for oneofs, a uint32. Else, MESSAGE_FIELD_NO_CASE.
int cache_index; // Each field except oneof field has a zval cache to avoid
// multiple creation when being accessed.
size_t case_offset; // for oneofs, a uint32. Else, MESSAGE_FIELD_NO_CASE.
};
struct MessageLayout {
@ -141,141 +225,230 @@ struct MessageLayout {
size_t size;
};
void layout_init(MessageLayout* layout, void* storage);
zval* layout_get(MessageLayout* layout, const void* storage,
const upb_fielddef* field TSRMLS_DC);
struct MessageHeader {
zend_object std; // Stores properties table and class info of PHP instance.
// This is needed for MessageHeader to be accessed via PHP.
Descriptor* descriptor; // Kept alive by self.class.descriptor reference.
// The real message data is appended after MessageHeader.
};
MessageLayout* create_layout(const upb_msgdef* msgdef);
void layout_init(MessageLayout* layout, void* storage, zval** properties_table);
zval* layout_get(MessageLayout* layout, const void* storage,
const upb_fielddef* field, zval** cache TSRMLS_DC);
void layout_set(MessageLayout* layout, MessageHeader* header,
const upb_fielddef* field, zval* val);
void free_layout(MessageLayout* layout);
zval* native_slot_get(upb_fieldtype_t type, /*VALUE type_class,*/
const void* memory TSRMLS_DC);
PHP_METHOD(Message, readOneof);
PHP_METHOD(Message, writeOneof);
// -----------------------------------------------------------------------------
// Message class creation.
// Encode / Decode.
// -----------------------------------------------------------------------------
struct MessageHeader {
zend_object std;
Descriptor* descriptor; // kept alive by self.class.descriptor reference.
// Data comes after this.
};
// Maximum depth allowed during encoding, to avoid stack overflows due to
// cycles.
#define ENCODE_MAX_NESTING 63
struct MessageBuilderContext {
zend_object std;
zval* descriptor;
zval* pool;
};
// Constructs the upb decoder method for parsing messages of this type.
// This is called from the message class creation code.
const upb_pbdecodermethod *new_fillmsg_decodermethod(Descriptor *desc,
const void *owner);
PHP_METHOD(Message, encode);
PHP_METHOD(Message, decode);
// -----------------------------------------------------------------------------
// Type check / conversion.
// -----------------------------------------------------------------------------
bool protobuf_convert_to_int32(zval* from, int32_t* to);
bool protobuf_convert_to_uint32(zval* from, uint32_t* to);
bool protobuf_convert_to_int64(zval* from, int64_t* to);
bool protobuf_convert_to_uint64(zval* from, uint64_t* to);
bool protobuf_convert_to_float(zval* from, float* to);
bool protobuf_convert_to_double(zval* from, double* to);
bool protobuf_convert_to_bool(zval* from, int8_t* to);
bool protobuf_convert_to_string(zval* from);
PHP_METHOD(Util, checkInt32);
PHP_METHOD(Util, checkUint32);
PHP_METHOD(Util, checkInt64);
PHP_METHOD(Util, checkUint64);
PHP_METHOD(Util, checkEnum);
PHP_METHOD(Util, checkFloat);
PHP_METHOD(Util, checkDouble);
PHP_METHOD(Util, checkBool);
PHP_METHOD(Util, checkString);
PHP_METHOD(Util, checkBytes);
PHP_METHOD(Util, checkMessage);
PHP_METHOD(Util, checkRepeatedField);
// -----------------------------------------------------------------------------
// Native slot storage abstraction.
// -----------------------------------------------------------------------------
#define NATIVE_SLOT_MAX_SIZE sizeof(uint64_t)
size_t native_slot_size(upb_fieldtype_t type);
bool native_slot_set(upb_fieldtype_t type, const zend_class_entry* klass,
void* memory, zval* value);
void native_slot_init(upb_fieldtype_t type, void* memory, zval** cache);
// For each property, in order to avoid conversion between the zval object and
// the actual data type during parsing/serialization, the containing message
// object use the custom memory layout to store the actual data type for each
// property inside of it. To access a property from php code, the property
// needs to be converted to a zval object. The message object is not responsible
// for providing such a zval object. Instead the caller needs to provide one
// (cache) and update it with the actual data (memory).
void native_slot_get(upb_fieldtype_t type, const void* memory,
zval** cache TSRMLS_DC);
void native_slot_get_default(upb_fieldtype_t type, zval** cache TSRMLS_DC);
// -----------------------------------------------------------------------------
// Map Field.
// -----------------------------------------------------------------------------
struct OneofBuilderContext {
extern zend_object_handlers* map_field_handlers;
typedef struct {
zend_object std;
// VALUE descriptor;
// VALUE builder;
};
upb_fieldtype_t key_type;
upb_fieldtype_t value_type;
const zend_class_entry* msg_ce; // class entry for value message
upb_strtable table;
} Map;
typedef struct {
Map* self;
upb_strtable_iter it;
} MapIter;
void map_begin(zval* self, MapIter* iter);
void map_next(MapIter* iter);
bool map_done(MapIter* iter);
const char* map_iter_key(MapIter* iter, int* len);
upb_value map_iter_value(MapIter* iter, int* len);
// These operate on a map-entry msgdef.
const upb_fielddef* map_entry_key(const upb_msgdef* msgdef);
const upb_fielddef* map_entry_value(const upb_msgdef* msgdef);
struct EnumBuilderContext {
zend_object_value map_field_create(zend_class_entry *ce TSRMLS_DC);
void map_field_create_with_type(zend_class_entry *ce, const upb_fielddef *field,
zval **map_field TSRMLS_DC);
void map_field_free(void* object TSRMLS_DC);
void* upb_value_memory(upb_value* v);
#define MAP_KEY_FIELD 1
#define MAP_VALUE_FIELD 2
// These operate on a map field (i.e., a repeated field of submessages whose
// submessage type is a map-entry msgdef).
const upb_fielddef* map_field_key(const upb_fielddef* field);
const upb_fielddef* map_field_value(const upb_fielddef* field);
bool map_index_set(Map *intern, const char* keyval, int length, upb_value v);
PHP_METHOD(MapField, __construct);
PHP_METHOD(MapField, offsetExists);
PHP_METHOD(MapField, offsetGet);
PHP_METHOD(MapField, offsetSet);
PHP_METHOD(MapField, offsetUnset);
PHP_METHOD(MapField, count);
// -----------------------------------------------------------------------------
// Repeated Field.
// -----------------------------------------------------------------------------
extern zend_object_handlers* repeated_field_handlers;
struct RepeatedField {
zend_object std;
// VALUE enumdesc;
zval* array;
upb_fieldtype_t type;
const zend_class_entry* msg_ce; // class entry for containing message
// (for message field only).
};
// Forward-declare all of the PHP method implementations.
DescriptorPool* php_to_descriptor_pool(zval* value TSRMLS_DC);
zend_object_value descriptor_pool_create(zend_class_entry *ce TSRMLS_DC);
void descriptor_pool_free_c(DescriptorPool* object TSRMLS_DC);
void descriptor_pool_free(void* object TSRMLS_DC);
void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC);
PHP_METHOD(DescriptorPool, addMessage);
PHP_METHOD(DescriptorPool, finalize);
Descriptor* php_to_descriptor(zval* value TSRMLS_DC);
zend_object_value descriptor_create(zend_class_entry *ce TSRMLS_DC);
void descriptor_init_c_instance(Descriptor* intern TSRMLS_DC);
void descriptor_free_c(Descriptor* object TSRMLS_DC);
void descriptor_free(void* object TSRMLS_DC);
void descriptor_name_set(Descriptor *desc, const char *name);
void repeated_field_create_with_type(zend_class_entry* ce,
const upb_fielddef* field,
zval** repeated_field TSRMLS_DC);
// Return the element at the index position from the repeated field. There is
// not restriction on the type of stored elements.
void *repeated_field_index_native(RepeatedField *intern, int index);
// Add the element to the end of the repeated field. There is not restriction on
// the type of stored elements.
void repeated_field_push_native(RepeatedField *intern, void *value);
PHP_METHOD(RepeatedField, __construct);
PHP_METHOD(RepeatedField, append);
PHP_METHOD(RepeatedField, offsetExists);
PHP_METHOD(RepeatedField, offsetGet);
PHP_METHOD(RepeatedField, offsetSet);
PHP_METHOD(RepeatedField, offsetUnset);
PHP_METHOD(RepeatedField, count);
MessageBuilderContext* php_to_message_builder_context(zval* value TSRMLS_DC);
zend_object_value message_builder_context_create(
zend_class_entry* ce TSRMLS_DC);
void message_builder_context_init_c_instance(
MessageBuilderContext* intern TSRMLS_DC);
void message_builder_context_free_c(MessageBuilderContext* object TSRMLS_DC);
void message_builder_context_free(void* object TSRMLS_DC);
PHP_METHOD(MessageBuilderContext, optional);
PHP_METHOD(MessageBuilderContext, finalizeToPool);
// -----------------------------------------------------------------------------
// Oneof Field.
// -----------------------------------------------------------------------------
PHP_METHOD(Message, encode);
const zend_class_entry* build_class_from_descriptor(
zval* php_descriptor TSRMLS_DC);
typedef struct {
zend_object std;
upb_oneofdef* oneofdef;
int index; // Index of field in oneof. -1 if not set.
char value[NATIVE_SLOT_MAX_SIZE];
} Oneof;
PHP_FUNCTION(get_generated_pool);
// Oneof case slot value to indicate that no oneof case is set. The value `0` is
// safe because field numbers are used as case identifiers, and no field can
// have a number of 0.
#define ONEOF_CASE_NONE 0
// -----------------------------------------------------------------------------
// Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor
// instances.
// ----------------------------------------------------------------------------
// Upb.
// -----------------------------------------------------------------------------
void add_def_obj(const void* def, zval* value);
zval* get_def_obj(const void* def);
upb_fieldtype_t to_fieldtype(upb_descriptortype_t type);
const zend_class_entry *field_type_class(const upb_fielddef *field);
// -----------------------------------------------------------------------------
// Utilities.
// -----------------------------------------------------------------------------
// PHP Array utils.
#define Z_ARRVAL_SIZE_P(zval_p) zend_hash_num_elements(Z_ARRVAL_P(zval_p))
#define Z_ARRVAL_BEGIN_P(zval_p) Z_ARRVAL_P(zval_p)->pListHead
#define Z_BUCKET_NEXT_PP(bucket_pp) *bucket_pp = (*bucket_pp)->pListNext
#define DEFINE_PHP_OBJECT(class_name, class_name_lower, name) \
do { \
zval* name; \
MAKE_STD_ZVAL(name); \
object_init_ex(name, class_name_lower##_type); \
} while (0)
#define DEFINE_PHP_WRAPPER(class_name, class_name_lower, name, intern) \
zval* name; \
MAKE_STD_ZVAL(name); \
object_init_ex(name, class_name_lower##_type); \
Z_OBJVAL_P(name) \
.handle = zend_objects_store_put( \
intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \
class_name_lower##_free, NULL TSRMLS_CC);
#define DEFINE_PHP_ZVAL(name) \
do { \
zval* name; \
MAKE_STD_ZVAL(name); \
} while (0)
#define DEFINE_PHP_STRING(name, value) \
do { \
zval* name; \
MAKE_STD_ZVAL(name); \
ZVAL_STRING(name, value, 1); \
} while (0)
// Upb Utilities
void check_upb_status(const upb_status* status, const char* msg);
#define CHECK_UPB(code, msg) \
do { \
upb_status status = UPB_STATUS_INIT; \
code; \
check_upb_status(&status, msg); \
} while (0)
// PHP <-> C conversion.
#define UNBOX(class_name, val) \
(class_name*)zend_object_store_get_object(val TSRMLS_CC);
// Memory management
#define BOX(class_name, wrapper, intern, free_func) \
MAKE_STD_ZVAL(wrapper); \
Z_TYPE_P(wrapper) = IS_OBJECT; \
Z_OBJVAL_P(wrapper) \
.handle = \
zend_objects_store_put(intern, NULL, free_func, NULL TSRMLS_CC); \
Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers();
// Memory management
#define ALLOC(class_name) (class_name*) emalloc(sizeof(class_name))
#define PEMALLOC(class_name) (class_name*) pemalloc(sizeof(class_name), 1)
#define ALLOC_N(class_name, n) (class_name*) emalloc(sizeof(class_name) * n)
#define FREE(object) efree(object)
// Type Checking
#define CHECK_TYPE(field, type) \
if (Z_TYPE_P(field) != type) { \
zend_error(E_ERROR, "Unexpected type"); \
}
#define PEFREE(object) pefree(object, 1)
// Create PHP internal instance.
#define CREATE(class_name, intern, init_func) \
intern = ALLOC(class_name); \
memset(intern, 0, sizeof(class_name)); \
init_func(intern TSRMLS_CC);
// String argument.
#define STR(str) (str), strlen(str)
// Zend Value
#define Z_OBJ_P(zval_p) \
((zend_object*)(EG(objects_store) \
.object_buckets[Z_OBJ_HANDLE_P(zval_p)] \
.bucket.obj.object))
#endif // __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__

@ -1,19 +1,41 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdint.h>
#include <protobuf.h>
#include <Zend/zend.h>
// -----------------------------------------------------------------------------
// PHP <-> native slot management.
// Native slot storage.
// -----------------------------------------------------------------------------
static zval* int32_to_zval(int32_t value) {
zval* tmp;
MAKE_STD_ZVAL(tmp);
ZVAL_LONG(tmp, value);
php_printf("int32 to zval\n");
// ZVAL_LONG(tmp, 1);
return tmp;
}
#define DEREF(memory, type) *(type*)(memory)
size_t native_slot_size(upb_fieldtype_t type) {
@ -21,9 +43,9 @@ size_t native_slot_size(upb_fieldtype_t type) {
case UPB_TYPE_FLOAT: return 4;
case UPB_TYPE_DOUBLE: return 8;
case UPB_TYPE_BOOL: return 1;
case UPB_TYPE_STRING: return sizeof(zval*);
case UPB_TYPE_BYTES: return sizeof(zval*);
case UPB_TYPE_MESSAGE: return sizeof(zval*);
case UPB_TYPE_STRING: return sizeof(void*);
case UPB_TYPE_BYTES: return sizeof(void*);
case UPB_TYPE_MESSAGE: return sizeof(void*);
case UPB_TYPE_ENUM: return 4;
case UPB_TYPE_INT32: return 4;
case UPB_TYPE_INT64: return 8;
@ -33,72 +55,77 @@ size_t native_slot_size(upb_fieldtype_t type) {
}
}
static bool is_php_num(zval* value) {
// Is numerial string also valid?
return (Z_TYPE_P(value) == IS_LONG ||
Z_TYPE_P(value) == IS_DOUBLE);
}
bool native_slot_set(upb_fieldtype_t type, const zend_class_entry* klass,
void* memory, zval* value) {
switch (type) {
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
if (!protobuf_convert_to_string(value)) {
return false;
}
if (type == UPB_TYPE_STRING &&
!is_structurally_valid_utf8(Z_STRVAL_P(value), Z_STRLEN_P(value))) {
zend_error(E_USER_ERROR, "Given string is not UTF8 encoded.");
return false;
}
if (*(zval**)memory != NULL) {
REPLACE_ZVAL_VALUE((zval**)memory, value, 1);
} else {
// Handles repeated/map string field. Memory provided by
// RepeatedField/Map is not initialized.
MAKE_STD_ZVAL(DEREF(memory, zval*));
ZVAL_STRINGL(DEREF(memory, zval*), Z_STRVAL_P(value), Z_STRLEN_P(value),
1);
}
break;
}
case UPB_TYPE_MESSAGE: {
if (Z_TYPE_P(value) != IS_OBJECT && Z_TYPE_P(value) != IS_NULL) {
zend_error(E_USER_ERROR, "Given value is not message.");
return false;
}
if (Z_TYPE_P(value) == IS_OBJECT && klass != Z_OBJCE_P(value)) {
zend_error(E_USER_ERROR, "Given message does not have correct class.");
return false;
}
if (EXPECTED(DEREF(memory, zval*) != value)) {
if (DEREF(memory, zval*) != NULL) {
zval_ptr_dtor((zval**)memory);
}
DEREF(memory, zval*) = value;
Z_ADDREF_P(value);
}
break;
}
void native_slot_check_int_range_precision(upb_fieldtype_t type, zval* val) {
// TODO(teboring): Add it back.
// if (!is_php_num(val)) {
// zend_error(E_ERROR, "Expected number type for integral field.");
// }
// if (Z_TYPE_P(val) == IS_DOUBLE) {
// double dbl_val = NUM2DBL(val);
// if (floor(dbl_val) != dbl_val) {
// zend_error(E_ERROR,
// "Non-integral floating point value assigned to integer field.");
// }
// }
// if (type == UPB_TYPE_UINT32 || type == UPB_TYPE_UINT64) {
// if (NUM2DBL(val) < 0) {
// zend_error(E_ERROR,
// "Assigning negative value to unsigned integer field.");
// }
// }
}
#define CASE_TYPE(upb_type, type, c_type, php_type) \
case UPB_TYPE_##upb_type: { \
c_type type##_value; \
if (protobuf_convert_to_##type(value, &type##_value)) { \
DEREF(memory, c_type) = type##_value; \
} \
break; \
}
CASE_TYPE(INT32, int32, int32_t, LONG)
CASE_TYPE(UINT32, uint32, uint32_t, LONG)
CASE_TYPE(ENUM, int32, int32_t, LONG)
CASE_TYPE(INT64, int64, int64_t, LONG)
CASE_TYPE(UINT64, uint64, uint64_t, LONG)
CASE_TYPE(FLOAT, float, float, DOUBLE)
CASE_TYPE(DOUBLE, double, double, DOUBLE)
CASE_TYPE(BOOL, bool, int8_t, BOOL)
#undef CASE_TYPE
zval* native_slot_get(upb_fieldtype_t type, /*VALUE type_class,*/
const void* memory TSRMLS_DC) {
zval* retval = NULL;
switch (type) {
// TODO(teboring): Add it back.
// case UPB_TYPE_FLOAT:
// return DBL2NUM(DEREF(memory, float));
// case UPB_TYPE_DOUBLE:
// return DBL2NUM(DEREF(memory, double));
// case UPB_TYPE_BOOL:
// return DEREF(memory, int8_t) ? Qtrue : Qfalse;
// case UPB_TYPE_STRING:
// case UPB_TYPE_BYTES:
// case UPB_TYPE_MESSAGE:
// return DEREF(memory, VALUE);
// case UPB_TYPE_ENUM: {
// int32_t val = DEREF(memory, int32_t);
// VALUE symbol = enum_lookup(type_class, INT2NUM(val));
// if (symbol == Qnil) {
// return INT2NUM(val);
// } else {
// return symbol;
// }
// }
case UPB_TYPE_INT32:
return int32_to_zval(DEREF(memory, int32_t));
// TODO(teboring): Add it back.
// case UPB_TYPE_INT64:
// return LL2NUM(DEREF(memory, int64_t));
// case UPB_TYPE_UINT32:
// return UINT2NUM(DEREF(memory, uint32_t));
// case UPB_TYPE_UINT64:
// return ULL2NUM(DEREF(memory, uint64_t));
default:
return EG(uninitialized_zval_ptr);
break;
}
return true;
}
void native_slot_init(upb_fieldtype_t type, void* memory) {
void native_slot_init(upb_fieldtype_t type, void* memory, zval** cache) {
zval* tmp = NULL;
switch (type) {
case UPB_TYPE_FLOAT:
DEREF(memory, float) = 0.0;
@ -109,17 +136,11 @@ void native_slot_init(upb_fieldtype_t type, void* memory) {
case UPB_TYPE_BOOL:
DEREF(memory, int8_t) = 0;
break;
// TODO(teboring): Add it back.
// case UPB_TYPE_STRING:
// case UPB_TYPE_BYTES:
// DEREF(memory, VALUE) = php_str_new2("");
// php_enc_associate(DEREF(memory, VALUE), (type == UPB_TYPE_BYTES)
// ? kRubyString8bitEncoding
// : kRubyStringUtf8Encoding);
// break;
// case UPB_TYPE_MESSAGE:
// DEREF(memory, VALUE) = Qnil;
// break;
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
case UPB_TYPE_MESSAGE:
DEREF(memory, zval**) = cache;
break;
case UPB_TYPE_ENUM:
case UPB_TYPE_INT32:
DEREF(memory, int32_t) = 0;
@ -138,122 +159,93 @@ void native_slot_init(upb_fieldtype_t type, void* memory) {
}
}
void native_slot_set(upb_fieldtype_t type, /*VALUE type_class,*/ void* memory,
zval* value) {
native_slot_set_value_and_case(type, /*type_class,*/ memory, value, NULL, 0);
}
void native_slot_set_value_and_case(upb_fieldtype_t type, /*VALUE type_class,*/
void* memory, zval* value,
uint32_t* case_memory,
uint32_t case_number) {
void native_slot_get(upb_fieldtype_t type, const void* memory,
zval** cache TSRMLS_DC) {
switch (type) {
case UPB_TYPE_FLOAT:
if (!Z_TYPE_P(value) == IS_LONG) {
zend_error(E_ERROR, "Expected number type for float field.");
#define CASE(upb_type, php_type, c_type) \
case UPB_TYPE_##upb_type: \
SEPARATE_ZVAL_IF_NOT_REF(cache); \
ZVAL_##php_type(*cache, DEREF(memory, c_type)); \
return;
CASE(FLOAT, DOUBLE, float)
CASE(DOUBLE, DOUBLE, double)
CASE(BOOL, BOOL, int8_t)
CASE(INT32, LONG, int32_t)
CASE(INT64, LONG, int64_t)
CASE(UINT64, LONG, uint64_t)
CASE(ENUM, LONG, uint32_t)
#undef CASE
case UPB_TYPE_UINT32: {
// Prepend bit-1 for negative numbers, so that uint32 value will be
// consistent on both 32-bit and 64-bit architectures.
SEPARATE_ZVAL_IF_NOT_REF(cache);
int value = DEREF(memory, int32_t);
if (sizeof(int) == 8) {
value |= (-((value >> 31) & 0x1) & 0xFFFFFFFF00000000);
}
DEREF(memory, float) = Z_DVAL_P(value);
break;
case UPB_TYPE_DOUBLE:
// TODO(teboring): Add it back.
// if (!is_php_num(value)) {
// zend_error(E_ERROR, "Expected number type for double field.");
// }
// DEREF(memory, double) = Z_DVAL_P(value);
break;
case UPB_TYPE_BOOL: {
int8_t val = -1;
if (zval_is_true(value)) {
val = 1;
} else {
val = 0;
}
// TODO(teboring): Add it back.
// else if (value == Qfalse) {
// val = 0;
// }
// else {
// php_raise(php_eTypeError, "Invalid argument for boolean field.");
// }
DEREF(memory, int8_t) = val;
break;
ZVAL_LONG(*cache, value);
return;
}
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
// TODO(teboring): Add it back.
// if (Z_TYPE_P(value) != IS_STRING) {
// zend_error(E_ERROR, "Invalid argument for string field.");
// }
// native_slot_validate_string_encoding(type, value);
// DEREF(memory, zval*) = value;
// For optional string/bytes fields, the cache is owned by the containing
// message and should have been updated during setting/decoding. However,
// for repeated string/bytes fields, the cache is provided by zend engine
// and has not been updated.
zval* value = DEREF(memory, zval*);
if (*cache != value) {
ZVAL_STRINGL(*cache, Z_STRVAL_P(value), Z_STRLEN_P(value), 1);
}
break;
}
case UPB_TYPE_MESSAGE: {
// TODO(teboring): Add it back.
// if (CLASS_OF(value) == CLASS_OF(Qnil)) {
// value = Qnil;
// } else if (CLASS_OF(value) != type_class) {
// php_raise(php_eTypeError,
// "Invalid type %s to assign to submessage field.",
// php_class2name(CLASS_OF(value)));
// }
// DEREF(memory, VALUE) = value;
break;
}
case UPB_TYPE_ENUM: {
// TODO(teboring): Add it back.
// int32_t int_val = 0;
// if (!is_php_num(value) && TYPE(value) != T_SYMBOL) {
// php_raise(php_eTypeError,
// "Expected number or symbol type for enum field.");
// }
// if (TYPE(value) == T_SYMBOL) {
// // Ensure that the given symbol exists in the enum module.
// VALUE lookup = php_funcall(type_class, php_intern("resolve"), 1, value);
// if (lookup == Qnil) {
// php_raise(php_eRangeError, "Unknown symbol value for enum field.");
// } else {
// int_val = NUM2INT(lookup);
// }
// } else {
// native_slot_check_int_range_precision(UPB_TYPE_INT32, value);
// int_val = NUM2INT(value);
// }
// DEREF(memory, int32_t) = int_val;
// break;
}
case UPB_TYPE_INT32:
case UPB_TYPE_INT64:
case UPB_TYPE_UINT32:
case UPB_TYPE_UINT64:
native_slot_check_int_range_precision(type, value);
switch (type) {
case UPB_TYPE_INT32:
php_printf("Setting INT32 field\n");
DEREF(memory, int32_t) = Z_LVAL_P(value);
break;
case UPB_TYPE_INT64:
// TODO(teboring): Add it back.
// DEREF(memory, int64_t) = NUM2LL(value);
break;
case UPB_TYPE_UINT32:
// TODO(teboring): Add it back.
// DEREF(memory, uint32_t) = NUM2UINT(value);
break;
case UPB_TYPE_UINT64:
// TODO(teboring): Add it back.
// DEREF(memory, uint64_t) = NUM2ULL(value);
break;
default:
break;
// Same as above for string/bytes fields.
zval* value = DEREF(memory, zval*);
if (*cache != value) {
ZVAL_ZVAL(*cache, value, 1, 0);
}
break;
return;
}
default:
break;
return EG(uninitialized_zval_ptr);
}
}
if (case_memory != NULL) {
*case_memory = case_number;
void native_slot_get_default(upb_fieldtype_t type, zval** cache TSRMLS_DC) {
switch (type) {
#define CASE(upb_type, php_type) \
case UPB_TYPE_##upb_type: \
SEPARATE_ZVAL_IF_NOT_REF(cache); \
ZVAL_##php_type(*cache, 0); \
return;
CASE(FLOAT, DOUBLE)
CASE(DOUBLE, DOUBLE)
CASE(BOOL, BOOL)
CASE(INT32, LONG)
CASE(INT64, LONG)
CASE(UINT32, LONG)
CASE(UINT64, LONG)
CASE(ENUM, LONG)
#undef CASE
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
SEPARATE_ZVAL_IF_NOT_REF(cache);
ZVAL_STRINGL(*cache, "", 0, 1);
break;
}
case UPB_TYPE_MESSAGE: {
SEPARATE_ZVAL_IF_NOT_REF(cache);
ZVAL_NULL(*cache);
return;
}
default:
return EG(uninitialized_zval_ptr);
}
}
@ -281,6 +273,40 @@ bool is_map_field(const upb_fielddef* field) {
return tryget_map_entry_msgdef(field) != NULL;
}
const upb_fielddef* map_field_key(const upb_fielddef* field) {
const upb_msgdef* subdef = map_entry_msgdef(field);
return map_entry_key(subdef);
}
const upb_fielddef* map_field_value(const upb_fielddef* field) {
const upb_msgdef* subdef = map_entry_msgdef(field);
return map_entry_value(subdef);
}
const upb_fielddef* map_entry_key(const upb_msgdef* msgdef) {
const upb_fielddef* key_field = upb_msgdef_itof(msgdef, MAP_KEY_FIELD);
assert(key_field != NULL);
return key_field;
}
const upb_fielddef* map_entry_value(const upb_msgdef* msgdef) {
const upb_fielddef* value_field = upb_msgdef_itof(msgdef, MAP_VALUE_FIELD);
assert(value_field != NULL);
return value_field;
}
const zend_class_entry* field_type_class(const upb_fielddef* field) {
if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) {
zval* desc_php = get_def_obj(upb_fielddef_subdef(field));
Descriptor* desc = zend_object_store_get_object(desc_php TSRMLS_CC);
return desc->klass;
} else if (upb_fielddef_type(field) == UPB_TYPE_ENUM) {
zval* desc_php = get_def_obj(upb_fielddef_subdef(field));
EnumDescriptor* desc = zend_object_store_get_object(desc_php TSRMLS_CC);
return desc->klass;
}
}
// -----------------------------------------------------------------------------
// Memory layout management.
// -----------------------------------------------------------------------------
@ -290,12 +316,29 @@ static size_t align_up_to(size_t offset, size_t granularity) {
return (offset + granularity - 1) & ~(granularity - 1);
}
static void* slot_memory(MessageLayout* layout, const void* storage,
const upb_fielddef* field) {
return ((uint8_t*)storage) + layout->fields[upb_fielddef_index(field)].offset;
}
static uint32_t* slot_oneof_case(MessageLayout* layout, const void* storage,
const upb_fielddef* field) {
return (uint32_t*)(((uint8_t*)storage) +
layout->fields[upb_fielddef_index(field)].case_offset);
}
static int slot_property_cache(MessageLayout* layout, const void* storage,
const upb_fielddef* field) {
return layout->fields[upb_fielddef_index(field)].cache_index;
}
MessageLayout* create_layout(const upb_msgdef* msgdef) {
MessageLayout* layout = ALLOC(MessageLayout);
int nfields = upb_msgdef_numfields(msgdef);
upb_msg_field_iter it;
upb_msg_oneof_iter oit;
size_t off = 0;
int i = 0;
layout->fields = ALLOC_N(MessageField, nfields);
@ -322,6 +365,7 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) {
layout->fields[upb_fielddef_index(field)].offset = off;
layout->fields[upb_fielddef_index(field)].case_offset =
MESSAGE_FIELD_NO_CASE;
layout->fields[upb_fielddef_index(field)].cache_index = i++;
off += field_size;
}
@ -353,11 +397,13 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) {
upb_oneof_next(&fit)) {
const upb_fielddef* field = upb_oneof_iter_field(&fit);
layout->fields[upb_fielddef_index(field)].offset = off;
layout->fields[upb_fielddef_index(field)].cache_index = i;
}
i++;
off += field_size;
}
// Now the case fields.
// Now the case offset.
for (upb_msg_oneof_begin(&oit, msgdef); !upb_msg_oneof_done(&oit);
upb_msg_oneof_next(&oit)) {
const upb_oneofdef* oneof = upb_msg_iter_oneof(&oit);
@ -389,151 +435,125 @@ void free_layout(MessageLayout* layout) {
FREE(layout);
}
// TODO(teboring): Add it back.
// VALUE field_type_class(const upb_fielddef* field) {
// VALUE type_class = Qnil;
// if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) {
// VALUE submsgdesc = get_def_obj(upb_fielddef_subdef(field));
// type_class = Descriptor_msgclass(submsgdesc);
// } else if (upb_fielddef_type(field) == UPB_TYPE_ENUM) {
// VALUE subenumdesc = get_def_obj(upb_fielddef_subdef(field));
// type_class = EnumDescriptor_enummodule(subenumdesc);
// }
// return type_class;
// }
void layout_init(MessageLayout* layout, void* storage, zval** properties_table) {
int i;
upb_msg_field_iter it;
for (upb_msg_field_begin(&it, layout->msgdef), i = 0; !upb_msg_field_done(&it);
upb_msg_field_next(&it), i++) {
const upb_fielddef* field = upb_msg_iter_field(&it);
void* memory = slot_memory(layout, storage, field);
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
int cache_index = slot_property_cache(layout, storage, field);
zval** property_ptr = &properties_table[cache_index];
static void* slot_memory(MessageLayout* layout, const void* storage,
const upb_fielddef* field) {
return ((uint8_t*)storage) + layout->fields[upb_fielddef_index(field)].offset;
if (upb_fielddef_containingoneof(field)) {
memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
*oneof_case = ONEOF_CASE_NONE;
} else if (is_map_field(field)) {
zval_ptr_dtor(property_ptr);
map_field_create_with_type(map_field_type, field, property_ptr);
DEREF(memory, zval**) = property_ptr;
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
zval_ptr_dtor(property_ptr);
repeated_field_create_with_type(repeated_field_type, field, property_ptr);
DEREF(memory, zval**) = property_ptr;
} else {
native_slot_init(upb_fielddef_type(field), memory, property_ptr);
}
}
}
static uint32_t* slot_oneof_case(MessageLayout* layout, const void* storage,
const upb_fielddef* field) {
return (uint32_t*)(((uint8_t*)storage) +
layout->fields[upb_fielddef_index(field)].case_offset);
// For non-singular fields, the related memory needs to point to the actual
// zval in properties table first.
static void* value_memory(const upb_fielddef* field, void* memory) {
switch (upb_fielddef_type(field)) {
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
case UPB_TYPE_MESSAGE:
memory = DEREF(memory, zval**);
break;
default:
// No operation
break;
}
return memory;
}
void layout_set(MessageLayout* layout, void* storage, const upb_fielddef* field,
zval* val) {
zval* layout_get(MessageLayout* layout, const void* storage,
const upb_fielddef* field, zval** cache TSRMLS_DC) {
void* memory = slot_memory(layout, storage, field);
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
if (upb_fielddef_containingoneof(field)) {
if (Z_TYPE_P(val) == IS_NULL) {
// Assigning nil to a oneof field clears the oneof completely.
*oneof_case = ONEOF_CASE_NONE;
memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
if (*oneof_case != upb_fielddef_number(field)) {
native_slot_get_default(upb_fielddef_type(field), cache TSRMLS_CC);
} else {
// The transition between field types for a single oneof (union) slot is
// somewhat complex because we need to ensure that a GC triggered at any
// point by a call into the Ruby VM sees a valid state for this field and
// does not either go off into the weeds (following what it thinks is a
// VALUE but is actually a different field type) or miss an object (seeing
// what it thinks is a primitive field but is actually a VALUE for the new
// field type).
//
// In order for the transition to be safe, the oneof case slot must be in
// sync with the value slot whenever the Ruby VM has been called. Thus, we
// use native_slot_set_value_and_case(), which ensures that both the value
// and case number are altered atomically (w.r.t. the Ruby VM).
native_slot_set_value_and_case(upb_fielddef_type(field),
/*field_type_class(field),*/ memory, val,
oneof_case, upb_fielddef_number(field));
native_slot_get(upb_fielddef_type(field), value_memory(field, memory),
cache TSRMLS_CC);
}
} else if (is_map_field(field)) {
// TODO(teboring): Add it back.
// check_map_field_type(val, field);
// DEREF(memory, zval*) = val;
return *cache;
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
// TODO(teboring): Add it back.
// check_repeated_field_type(val, field);
// DEREF(memory, zval*) = val;
return *cache;
} else {
native_slot_set(upb_fielddef_type(field), /*field_type_class(field),*/ memory,
val);
}
}
void layout_init(MessageLayout* layout, void* storage) {
upb_msg_field_iter it;
for (upb_msg_field_begin(&it, layout->msgdef); !upb_msg_field_done(&it);
upb_msg_field_next(&it)) {
const upb_fielddef* field = upb_msg_iter_field(&it);
void* memory = slot_memory(layout, storage, field);
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
if (upb_fielddef_containingoneof(field)) {
// TODO(teboring): Add it back.
// memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
// *oneof_case = ONEOF_CASE_NONE;
} else if (is_map_field(field)) {
// TODO(teboring): Add it back.
// VALUE map = Qnil;
// const upb_fielddef* key_field = map_field_key(field);
// const upb_fielddef* value_field = map_field_value(field);
// VALUE type_class = field_type_class(value_field);
// if (type_class != Qnil) {
// VALUE args[3] = {
// fieldtype_to_php(upb_fielddef_type(key_field)),
// fieldtype_to_php(upb_fielddef_type(value_field)), type_class,
// };
// map = php_class_new_instance(3, args, cMap);
// } else {
// VALUE args[2] = {
// fieldtype_to_php(upb_fielddef_type(key_field)),
// fieldtype_to_php(upb_fielddef_type(value_field)),
// };
// map = php_class_new_instance(2, args, cMap);
// }
// DEREF(memory, VALUE) = map;
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
// TODO(teboring): Add it back.
// VALUE ary = Qnil;
// VALUE type_class = field_type_class(field);
// if (type_class != Qnil) {
// VALUE args[2] = {
// fieldtype_to_php(upb_fielddef_type(field)), type_class,
// };
// ary = php_class_new_instance(2, args, cRepeatedField);
// } else {
// VALUE args[1] = {fieldtype_to_php(upb_fielddef_type(field))};
// ary = php_class_new_instance(1, args, cRepeatedField);
// }
// DEREF(memory, VALUE) = ary;
} else {
native_slot_init(upb_fielddef_type(field), memory);
}
native_slot_get(upb_fielddef_type(field), value_memory(field, memory),
cache TSRMLS_CC);
return *cache;
}
}
zval* layout_get(MessageLayout* layout, const void* storage,
const upb_fielddef* field TSRMLS_DC) {
void layout_set(MessageLayout* layout, MessageHeader* header, const upb_fielddef* field,
zval* val) {
void* storage = message_data(header);
void* memory = slot_memory(layout, storage, field);
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
if (upb_fielddef_containingoneof(field)) {
if (*oneof_case != upb_fielddef_number(field)) {
return NULL;
// TODO(teboring): Add it back.
// return Qnil;
upb_fieldtype_t type = upb_fielddef_type(field);
zend_class_entry *ce = NULL;
// For non-singular fields, the related memory needs to point to the actual
// zval in properties table first.
switch (type) {
case UPB_TYPE_MESSAGE: {
upb_msgdef* msg = upb_fielddef_msgsubdef(field);
zval* desc_php = get_def_obj(msg);
Descriptor* desc = zend_object_store_get_object(desc_php TSRMLS_CC);
ce = desc->klass;
// Intentionally fall through.
}
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
int property_cache_index =
header->descriptor->layout->fields[upb_fielddef_index(field)]
.cache_index;
DEREF(memory, zval**) =
&(header->std.properties_table)[property_cache_index];
memory = DEREF(memory, zval**);
break;
}
default:
break;
}
return NULL;
// TODO(teboring): Add it back.
// return native_slot_get(upb_fielddef_type(field), field_type_class(field),
// memory);
native_slot_set(type, ce, memory, val);
*oneof_case = upb_fielddef_number(field);
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
return NULL;
// TODO(teboring): Add it back.
// return *((VALUE*)memory);
// Works for both repeated and map fields
memory = DEREF(memory, zval**);
if (EXPECTED(DEREF(memory, zval*) != val)) {
zval_ptr_dtor(memory);
DEREF(memory, zval*) = val;
Z_ADDREF_P(val);
}
} else {
return native_slot_get(
upb_fielddef_type(field), /*field_type_class(field), */
memory TSRMLS_CC);
upb_fieldtype_t type = upb_fielddef_type(field);
zend_class_entry *ce = NULL;
if (type == UPB_TYPE_MESSAGE) {
upb_msgdef* msg = upb_fielddef_msgsubdef(field);
zval* desc_php = get_def_obj(msg);
Descriptor* desc = zend_object_store_get_object(desc_php TSRMLS_CC);
ce = desc->klass;
}
native_slot_set(type, ce, value_memory(field, memory), val);
}
}

@ -1,15 +0,0 @@
<?php
namespace Google\Protobuf;
$pool = get_generated_pool();
$pool->addMessage("TestMessage")
->optional("optional_int32_a", "int32", 1)
->optional("optional_int32_b", "int32", 2)
->finalizeToPool()
->finalize();
$test_message = new \TestMessage();
?>

@ -0,0 +1,310 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <Zend/zend_operators.h>
#include "protobuf.h"
#include "utf8.h"
static zend_class_entry* util_type;
ZEND_BEGIN_ARG_INFO_EX(arg_check_optional, 0, 0, 1)
ZEND_ARG_INFO(1, val)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arg_check_message, 0, 0, 2)
ZEND_ARG_INFO(1, val)
ZEND_ARG_INFO(0, klass)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arg_check_repeated, 0, 0, 2)
ZEND_ARG_INFO(1, val)
ZEND_ARG_INFO(0, type)
ZEND_ARG_INFO(0, klass)
ZEND_END_ARG_INFO()
static zend_function_entry util_methods[] = {
PHP_ME(Util, checkInt32, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(Util, checkUint32, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(Util, checkInt64, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(Util, checkUint64, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(Util, checkEnum, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(Util, checkFloat, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(Util, checkDouble, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(Util, checkBool, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(Util, checkString, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(Util, checkBytes, arg_check_optional, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(Util, checkMessage, arg_check_message, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
PHP_ME(Util, checkRepeatedField, arg_check_repeated,
ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_FE_END
};
void util_init(TSRMLS_D) {
zend_class_entry class_type;
INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBUtil",
util_methods);
util_type = zend_register_internal_class(&class_type TSRMLS_CC);
}
// -----------------------------------------------------------------------------
// Type checking/conversion.
// -----------------------------------------------------------------------------
#define CONVERT_TO_INTEGER(type) \
static bool convert_long_to_##type(long val, type##_t* type##_value) { \
*type##_value = (type##_t)val; \
return true; \
} \
\
static bool convert_double_to_##type(double val, type##_t* type##_value) { \
*type##_value = (type##_t)zend_dval_to_lval(val); \
return true; \
} \
\
static bool convert_string_to_##type(const char* val, int len, \
type##_t* type##_value) { \
long lval; \
double dval; \
\
switch (is_numeric_string(val, len, &lval, &dval, 0)) { \
case IS_DOUBLE: { \
return convert_double_to_##type(dval, type##_value); \
} \
case IS_LONG: { \
return convert_long_to_##type(lval, type##_value); \
} \
default: \
zend_error(E_USER_ERROR, \
"Given string value cannot be converted to integer."); \
return false; \
} \
} \
\
bool protobuf_convert_to_##type(zval* from, type##_t* to) { \
switch (Z_TYPE_P(from)) { \
case IS_LONG: { \
return convert_long_to_##type(Z_LVAL_P(from), to); \
} \
case IS_DOUBLE: { \
return convert_double_to_##type(Z_DVAL_P(from), to); \
} \
case IS_STRING: { \
return convert_string_to_##type(Z_STRVAL_P(from), Z_STRLEN_P(from), \
to); \
} \
default: { \
zend_error(E_USER_ERROR, \
"Given value cannot be converted to integer."); \
return false; \
} \
} \
return false; \
}
CONVERT_TO_INTEGER(int32);
CONVERT_TO_INTEGER(uint32);
CONVERT_TO_INTEGER(int64);
CONVERT_TO_INTEGER(uint64);
#undef CONVERT_TO_INTEGER
#define CONVERT_TO_FLOAT(type) \
static bool convert_long_to_##type(long val, type* type##_value) { \
*type##_value = (type)val; \
return true; \
} \
\
static bool convert_double_to_##type(double val, type* type##_value) { \
*type##_value = (type)val; \
return true; \
} \
\
static bool convert_string_to_##type(const char* val, int len, \
type* type##_value) { \
long lval; \
double dval; \
\
switch (is_numeric_string(val, len, &lval, &dval, 0)) { \
case IS_DOUBLE: { \
*type##_value = (type)dval; \
return true; \
} \
case IS_LONG: { \
*type##_value = (type)lval; \
return true; \
} \
default: \
zend_error(E_USER_ERROR, \
"Given string value cannot be converted to integer."); \
return false; \
} \
} \
\
bool protobuf_convert_to_##type(zval* from, type* to) { \
switch (Z_TYPE_P(from)) { \
case IS_LONG: { \
return convert_long_to_##type(Z_LVAL_P(from), to); \
} \
case IS_DOUBLE: { \
return convert_double_to_##type(Z_DVAL_P(from), to); \
} \
case IS_STRING: { \
return convert_string_to_##type(Z_STRVAL_P(from), Z_STRLEN_P(from), \
to); \
} \
default: { \
zend_error(E_USER_ERROR, \
"Given value cannot be converted to integer."); \
return false; \
} \
} \
return false; \
}
CONVERT_TO_FLOAT(float);
CONVERT_TO_FLOAT(double);
#undef CONVERT_TO_FLOAT
bool protobuf_convert_to_bool(zval* from, int8_t* to) {
switch (Z_TYPE_P(from)) {
case IS_BOOL:
*to = (int8_t)Z_BVAL_P(from);
break;
case IS_LONG:
*to = (int8_t)(Z_LVAL_P(from) != 0);
break;
case IS_DOUBLE:
*to = (int8_t)(Z_LVAL_P(from) != 0);
break;
case IS_STRING: {
char* strval = Z_STRVAL_P(from);
if (Z_STRLEN_P(from) == 0 ||
(Z_STRLEN_P(from) == 1 && Z_STRVAL_P(from)[0] == '0')) {
*to = 0;
} else {
*to = 1;
}
STR_FREE(strval);
} break;
default: {
zend_error(E_USER_ERROR, "Given value cannot be converted to bool.");
return false;
}
}
return true;
}
bool protobuf_convert_to_string(zval* from) {
switch (Z_TYPE_P(from)) {
case IS_STRING: {
return true;
}
case IS_BOOL:
case IS_LONG:
case IS_DOUBLE: {
int use_copy;
zval tmp;
zend_make_printable_zval(from, &tmp, &use_copy);
ZVAL_COPY_VALUE(from, &tmp);
return true;
}
default:
zend_error(E_USER_ERROR, "Given value cannot be converted to string.");
return false;
}
}
// -----------------------------------------------------------------------------
// PHP Functions.
// -----------------------------------------------------------------------------
// The implementation of type checking for primitive fields is empty. This is
// because type checking is done when direct assigning message fields (e.g.,
// foo->a = 1). Functions defined here are place holders in generated code for
// pure PHP implementation (c extension and pure PHP share the same generated
// code).
#define PHP_TYPE_CHECK(type) \
PHP_METHOD(Util, check##type) {}
PHP_TYPE_CHECK(Int32)
PHP_TYPE_CHECK(Uint32)
PHP_TYPE_CHECK(Int64)
PHP_TYPE_CHECK(Uint64)
PHP_TYPE_CHECK(Enum)
PHP_TYPE_CHECK(Float)
PHP_TYPE_CHECK(Double)
PHP_TYPE_CHECK(Bool)
PHP_TYPE_CHECK(String)
PHP_TYPE_CHECK(Bytes)
#undef PHP_TYPE_CHECK
PHP_METHOD(Util, checkMessage) {
zval* val;
zend_class_entry* klass = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o!C", &val, &klass) ==
FAILURE) {
return;
}
if (val == NULL) {
RETURN_NULL();
}
if (!instanceof_function(Z_OBJCE_P(val), klass TSRMLS_CC)) {
zend_error(E_USER_ERROR, "Given value is not an instance of %s.",
klass->name);
return;
}
RETURN_ZVAL(val, 1, 0);
}
PHP_METHOD(Util, checkRepeatedField) {
zval* val;
long type;
const zend_class_entry* klass = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Ol|C", &val,
repeated_field_type, &type, &klass) == FAILURE) {
return;
}
RepeatedField *intern =
(RepeatedField *)zend_object_store_get_object(val TSRMLS_CC);
if (to_fieldtype(type) != intern->type) {
zend_error(E_USER_ERROR, "Incorrect repeated field type.");
return;
}
if (klass != NULL && intern->msg_ce != klass) {
zend_error(E_USER_ERROR, "Expect a repeated field of %s, but %s is given.",
klass->name, intern->msg_ce->name);
return;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,68 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdbool.h>
#include <stdint.h>
#include "utf8.h"
static const uint8_t utf8_offset[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0,
};
bool is_structurally_valid_utf8(const char* buf, int len) {
int i, j;
uint8_t offset;
i = 0;
while (i < len) {
offset = utf8_offset[(uint8_t)buf[i]];
if (offset == 0 || i + offset > len) {
return false;
}
for (j = i + 1; j < i + offset; j++) {
if (buf[j] & 0xc0 != 0x80) {
return false;
}
}
i += offset;
}
return i == len;
}

@ -0,0 +1,36 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_UTF8_H_
#define GOOGLE_PROTOBUF_UTF8_H_
bool is_structurally_valid_utf8(const char* buf, int len);
#endif // GOOGLE_PROTOBUF_UTF8_H_

@ -0,0 +1,162 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
use Google\Protobuf\Internal\Descriptor;
use Google\Protobuf\Internal\FileDescriptor;
use Google\Protobuf\Internal\FileDescriptorSet;
use Google\Protobuf\Internal\MessageBuilderContext;
use Google\Protobuf\Internal\EnumBuilderContext;
class DescriptorPool
{
private static $pool;
// Map from message names to sub-maps, which are maps from field numbers to
// field descriptors.
private $class_to_desc = [];
private $class_to_enum_desc = [];
private $proto_to_class = [];
public static function getGeneratedPool()
{
if (!isset(self::$pool)) {
self::$pool = new DescriptorPool();
}
return self::$pool;
}
public function internalAddGeneratedFile($data)
{
$files = new FileDescriptorSet();
$files->decode($data);
$file = FileDescriptor::buildFromProto($files->getFile()[0]);
foreach ($file->getMessageType() as &$desc) {
$this->addDescriptor($desc);
}
unset($desc);
foreach ($file->getEnumType() as &$desc) {
$this->addEnumDescriptor($desc);
}
unset($desc);
foreach ($file->getMessageType() as &$desc) {
$this->crossLink($desc);
}
unset($desc);
}
public function addMessage($name, $klass)
{
return new MessageBuilderContext($name, $klass, $this);
}
public function addEnum($name, $klass)
{
return new EnumBuilderContext($name, $klass, $this);
}
public function addDescriptor($descriptor)
{
$this->proto_to_class[$descriptor->getFullName()] =
$descriptor->getClass();
$this->class_to_desc[$descriptor->getClass()] = $descriptor;
foreach ($descriptor->getNestedType() as $nested_type) {
$this->addDescriptor($nested_type);
}
}
public function addEnumDescriptor($descriptor)
{
$this->proto_to_class[$descriptor->getFullName()] =
$descriptor->getClass();
$this->class_to_enum_desc[$descriptor->getClass()] = $descriptor;
}
public function getDescriptorByClassName($klass)
{
return $this->class_to_desc[$klass];
}
public function getEnumDescriptorByClassName($klass)
{
return $this->class_to_enum_desc[$klass];
}
public function getDescriptorByProtoName($proto)
{
$klass = $this->proto_to_class[$proto];
return $this->class_to_desc[$klass];
}
public function getEnumDescriptorByProtoName($proto)
{
$klass = $this->proto_to_class[$proto];
return $this->class_to_enum_desc[$klass];
}
private function crossLink(&$desc)
{
foreach ($desc->getField() as &$field) {
switch ($field->getType()) {
case GPBType::MESSAGE:
$proto = $field->getMessageType();
$field->setMessageType(
$this->getDescriptorByProtoName($proto));
break;
case GPBType::ENUM:
$proto = $field->getEnumType();
$field->setEnumType(
$this->getEnumDescriptorByProtoName($proto));
break;
default:
break;
}
}
unset($field);
foreach ($desc->getNestedType() as &$nested_type) {
$this->crossLink($nested_type);
}
unset($nested_type);
}
public function finish()
{
foreach ($this->class_to_desc as $klass => &$desc) {
$this->crossLink($desc);
}
unset($desc);
}
}

@ -0,0 +1,63 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
use Google\Protobuf\Internal\EnumDescriptor;
use Google\Protobuf\Internal\EnumValueDescriptor;
class EnumBuilderContext
{
private $descriptor;
private $pool;
public function __construct($full_name, $klass, $pool)
{
$this->descriptor = new EnumDescriptor();
$this->descriptor->setFullName($full_name);
$this->descriptor->setClass($klass);
$this->pool = $pool;
}
public function value($name, $number)
{
$value = new EnumValueDescriptor();
$this->descriptor->addValue($number, $value);
return $this;
}
public function finalizeToPool()
{
$this->pool->addEnumDescriptor($this->descriptor);
}
}

@ -0,0 +1,40 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
class GPBLabel
{
const OPTIONAL = 1;
const REQUIRED = 2;
const REPEATED = 3;
}

@ -0,0 +1,55 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
class GPBType
{
const DOUBLE = 1;
const FLOAT = 2;
const INT64 = 3;
const UINT64 = 4;
const INT32 = 5;
const FIXED64 = 6;
const FIXED32 = 7;
const BOOL = 8;
const STRING = 9;
const GROUP = 10;
const MESSAGE = 11;
const BYTES = 12;
const UINT32 = 13;
const ENUM = 14;
const SFIXED32 = 15;
const SFIXED64 = 16;
const SINT32 = 17;
const SINT64 = 18;
}

@ -0,0 +1,161 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
class GPBUtil
{
public static function checkString(&$var, $check_utf8)
{
if (is_array($var) || is_object($var)) {
trigger_error("Expect string.", E_USER_ERROR);
return;
}
if (!is_string($var)) {
$var = strval($var);
}
if ($check_utf8 && !preg_match('//u', $var)) {
trigger_error("Expect utf-8 encoding.", E_USER_ERROR);
return;
}
}
public static function checkEnum(&$var)
{
static::checkInt32($var);
}
public static function checkInt32(&$var)
{
if (is_numeric($var)) {
$var = intval($var);
} else {
trigger_error("Expect integer.", E_USER_ERROR);
}
}
public static function checkUint32(&$var)
{
if (is_numeric($var)) {
$var = intval($var);
if (PHP_INT_SIZE === 8) {
$var |= ((-(($var >> 31) & 0x1)) & ~0xFFFFFFFF);
}
} else {
trigger_error("Expect integer.", E_USER_ERROR);
}
}
public static function checkInt64(&$var)
{
if (is_numeric($var)) {
$var = intval($var);
} else {
trigger_error("Expect integer.", E_USER_ERROR);
}
}
public static function checkUint64(&$var)
{
if (is_numeric($var)) {
$var = intval($var);
} else {
trigger_error("Expect integer.", E_USER_ERROR);
}
}
public static function checkFloat(&$var)
{
if (is_float($var) || is_numeric($var)) {
$var = floatval($var);
} else {
trigger_error("Expect float.", E_USER_ERROR);
}
}
public static function checkDouble(&$var)
{
if (is_float($var) || is_numeric($var)) {
$var = floatval($var);
} else {
trigger_error("Expect float.", E_USER_ERROR);
}
}
public static function checkBool(&$var)
{
if (is_array($var) || is_object($var)) {
trigger_error("Expect boolean.", E_USER_ERROR);
return;
}
$var = boolval($var);
}
public static function checkMessage(&$var, $klass)
{
if (!$var instanceof $klass && !is_null($var)) {
trigger_error("Expect message.", E_USER_ERROR);
}
}
public static function checkRepeatedField(&$var, $type, $klass = null)
{
if (!$var instanceof RepeatedField) {
trigger_error("Expect repeated field.", E_USER_ERROR);
}
if ($var->getType() != $type) {
trigger_error(
"Expect repeated field of different type.",
E_USER_ERROR);
}
if ($var->getType() === GPBType::MESSAGE &&
$var->getClass() !== $klass) {
trigger_error(
"Expect repeated field of different message.",
E_USER_ERROR);
}
}
public static function Int64($value)
{
return new Int64($value);
}
public static function Uint64($value)
{
return new Uint64($value);
}
}

@ -0,0 +1,583 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
use Google\Protobuf\Internal\GPBUtil;
use Google\Protobuf\Internal\Int64;
use Google\Protobuf\Internal\Uint64;
class GPBWire
{
const TAG_TYPE_BITS = 3;
const WIRETYPE_VARINT = 0;
const WIRETYPE_FIXED64 = 1;
const WIRETYPE_LENGTH_DELIMITED = 2;
const WIRETYPE_START_GROUP = 3;
const WIRETYPE_END_GROUP = 4;
const WIRETYPE_FIXED32 = 5;
const UNKNOWN = 0;
const NORMAL_FORMAT = 1;
const PACKED_FORMAT = 2;
public static function getTagFieldNumber($tag)
{
return ($tag >> self::TAG_TYPE_BITS) &
(1 << ((PHP_INT_SIZE * 8) - self::TAG_TYPE_BITS)) - 1;
}
public static function getTagWireType($tag)
{
return $tag & 0x7;
}
public static function getWireType($type)
{
switch ($type) {
case GPBType::FLOAT:
case GPBType::FIXED32:
case GPBType::SFIXED32:
return self::WIRETYPE_FIXED32;
case GPBType::DOUBLE:
case GPBType::FIXED64:
case GPBType::SFIXED64:
return self::WIRETYPE_FIXED64;
case GPBType::UINT32:
case GPBType::UINT64:
case GPBType::INT32:
case GPBType::INT64:
case GPBType::SINT32:
case GPBType::SINT64:
case GPBType::ENUM:
case GPBType::BOOL:
return self::WIRETYPE_VARINT;
case GPBType::STRING:
case GPBType::BYTES:
case GPBType::MESSAGE:
return self::WIRETYPE_LENGTH_DELIMITED;
case GPBType::GROUP:
user_error("Unsupported type.");
return 0;
default:
user_error("Unsupported type.");
return 0;
}
}
// ZigZag Transform: Encodes signed integers so that they can be effectively
// used with varint encoding.
//
// varint operates on unsigned integers, encoding smaller numbers into fewer
// bytes. If you try to use it on a signed integer, it will treat this
// number as a very large unsigned integer, which means that even small
// signed numbers like -1 will take the maximum number of bytes (10) to
// encode. zigZagEncode() maps signed integers to unsigned in such a way
// that those with a small absolute value will have smaller encoded values,
// making them appropriate for encoding using varint.
//
// int32 -> uint32
// -------------------------
// 0 -> 0
// -1 -> 1
// 1 -> 2
// -2 -> 3
// ... -> ...
// 2147483647 -> 4294967294
// -2147483648 -> 4294967295
//
// >> encode >>
// << decode <<
public static function zigZagEncode32($int32)
{
// Fill high 32 bits.
if (PHP_INT_SIZE === 8) {
$int32 |= ((($int32 << 32) >> 31) & (0xFFFFFFFF << 32));
}
$uint32 = ($int32 << 1) ^ ($int32 >> 31);
// Fill high 32 bits.
if (PHP_INT_SIZE === 8) {
$uint32 |= ((($uint32 << 32) >> 31) & (0xFFFFFFFF << 32));
}
return $uint32;
}
public static function zigZagDecode32($uint32)
{
// Fill high 32 bits.
if (PHP_INT_SIZE === 8) {
$uint32 |= ($uint32 & 0xFFFFFFFF);
}
$int32 = (($uint32 >> 1) & 0x7FFFFFFF) ^ (-($uint32 & 1));
return $int32;
}
public static function zigZagEncode64($int64)
{
$a = $int64->copy()->leftShift(1);
$b = $int64->copy()->rightShift(63);
$result = $a->bitXor($b);
$uint64 = Uint64::newValue($result->high, $result->low);
return $uint64;
}
public static function zigZagDecode64($uint64)
{
$a = $uint64->copy()->rightShift(1);
$b = $uint64->oddMask();
$result = $a->bitXor($b);
$int64 = Int64::newValue($result->high, $result->low);
return $int64;
}
public static function readInt32(&$input, &$value)
{
return $input->readVarint32($value);
}
public static function readInt64(&$input, &$value)
{
return $input->readVarint64($value);
}
public static function readUint32(&$input, &$value)
{
return self::readInt32($input, $value);
}
public static function readUint64(&$input, &$value)
{
return self::readInt64($input, $value);
}
public static function readSint32(&$input, &$value)
{
if (!$input->readVarint32($value)) {
return false;
}
$value = GPBWire::zigZagDecode32($value);
return true;
}
public static function readSint64(&$input, &$value)
{
if (!$input->readVarint64($value)) {
return false;
}
$value = GPBWire::zigZagDecode64($value);
return true;
}
public static function readFixed32(&$input, &$value)
{
return $input->readLittleEndian32($value);
}
public static function readFixed64(&$input, &$value)
{
return $input->readLittleEndian64($value);
}
public static function readSfixed32(&$input, &$value)
{
if (!self::readFixed32($input, $value)) {
return false;
}
if (PHP_INT_SIZE === 8) {
$value |= (-($value >> 31) << 32);
}
return true;
}
public static function readSfixed64(&$input, &$value)
{
if (!self::readFixed64($input, $value)) {
return false;
}
$value = Int64::newValue($value->high, $value->low);
return true;
}
public static function readFloat(&$input, &$value)
{
$data = null;
if (!$input->readRaw(4, $data)) {
return false;
}
$value = unpack('f', $data)[1];
return true;
}
public static function readDouble(&$input, &$value)
{
$data = null;
if (!$input->readRaw(8, $data)) {
return false;
}
$value = unpack('d', $data)[1];
return true;
}
public static function readBool(&$input, &$value)
{
if (!$input->readVarint64($value)) {
return false;
}
if ($value->high === 0 && $value->low === 0) {
$value = false;
} else {
$value = true;
}
return true;
}
public static function readString(&$input, &$value)
{
$length = 0;
return $input->readVarintSizeAsInt($length) && $input->readRaw($length, $value);
}
public static function readMessage(&$input, &$message)
{
$length = 0;
if (!$input->readVarintSizeAsInt($length)) {
return false;
}
$old_limit = 0;
$recursion_limit = 0;
$input->incrementRecursionDepthAndPushLimit(
$length,
$old_limit,
$recursion_limit);
if ($recursion_limit < 0 || !$message->parseFromStream($input)) {
return false;
}
return $input->decrementRecursionDepthAndPopLimit($old_limit);
}
public static function writeTag(&$output, $tag)
{
return $output->writeTag($tag);
}
public static function writeInt32(&$output, $value)
{
return $output->writeVarint32($value);
}
public static function writeInt64(&$output, $value)
{
return $output->writeVarint64($value);
}
public static function writeUint32(&$output, $value)
{
return $output->writeVarint32($value);
}
public static function writeUint64(&$output, $value)
{
return $output->writeVarint64($value);
}
public static function writeSint32(&$output, $value)
{
$value = GPBWire::zigZagEncode32($value);
return $output->writeVarint64($value);
}
public static function writeSint64(&$output, $value)
{
$value = GPBWire::zigZagEncode64(GPBUtil::Int64($value));
return $output->writeVarint64($value->toInteger());
}
public static function writeFixed32(&$output, $value)
{
return $output->writeLittleEndian32($value);
}
public static function writeFixed64(&$output, $value)
{
return $output->writeLittleEndian64($value);
}
public static function writeSfixed32(&$output, $value)
{
return $output->writeLittleEndian32($value);
}
public static function writeSfixed64(&$output, $value)
{
return $output->writeLittleEndian64($value);
}
public static function writeBool(&$output, $value)
{
if ($value) {
return $output->writeVarint32(1);
} else {
return $output->writeVarint32(0);
}
}
public static function writeFloat(&$output, $value)
{
$data = pack("f", $value);
return $output->writeRaw($data, 4);
}
public static function writeDouble(&$output, $value)
{
$data = pack("d", $value);
return $output->writeRaw($data, 8);
}
public static function writeString(&$output, $value)
{
return self::writeBytes($output, $value);
}
public static function writeBytes(&$output, $value)
{
$size = strlen($value);
if (!$output->writeVarint32($size)) {
return false;
}
return $output->writeRaw($value, $size);
}
public static function writeMessage(&$output, $value)
{
$size = $value->byteSize();
if (!$output->writeVarint32($size)) {
return false;
}
return $value->serializeToStream($output);
}
public static function makeTag($number, $type)
{
return ($number << 3) | self::getWireType($type);
}
public static function tagSize($field)
{
$tag = self::makeTag($field->getNumber(), $field->getType());
return self::varint32Size($tag);
}
public static function varint32Size($value)
{
if ($value < 0) {
return 5;
}
if ($value < (1 << 7)) {
return 1;
}
if ($value < (1 << 14)) {
return 2;
}
if ($value < (1 << 21)) {
return 3;
}
if ($value < (1 << 28)) {
return 4;
}
return 5;
}
public static function sint32Size($value)
{
$value = self::zigZagEncode32($value);
return self::varint32Size($value);
}
public static function sint64Size($value)
{
$value = GPBUtil::Int64($value);
$value = self::zigZagEncode64($value);
return self::varint64Size($value->toInteger());
}
public static function varint64Size($value)
{
if ($value < 0) {
return 10;
}
if ($value < (1 << 7)) {
return 1;
}
if ($value < (1 << 14)) {
return 2;
}
if ($value < (1 << 21)) {
return 3;
}
if ($value < (1 << 28)) {
return 4;
}
if ($value < (1 << 35)) {
return 5;
}
if ($value < (1 << 42)) {
return 6;
}
if ($value < (1 << 49)) {
return 7;
}
if ($value < (1 << 56)) {
return 8;
}
return 9;
}
public static function serializeFieldToStream(
$value,
$field,
$need_tag,
&$output)
{
if ($need_tag) {
if (!GPBWire::writeTag(
$output,
self::makeTag(
$field->getNumber(),
$field->getType()))) {
return false;
}
}
switch ($field->getType()) {
case GPBType::DOUBLE:
if (!GPBWire::writeDouble($output, $value)) {
return false;
}
break;
case GPBType::FLOAT:
if (!GPBWire::writeFloat($output, $value)) {
return false;
}
break;
case GPBType::INT64:
if (!GPBWire::writeInt64($output, $value)) {
return false;
}
break;
case GPBType::UINT64:
if (!GPBWire::writeUint64($output, $value)) {
return false;
}
break;
case GPBType::INT32:
if (!GPBWire::writeInt32($output, $value)) {
return false;
}
break;
case GPBType::FIXED32:
if (!GPBWire::writeFixed32($output, $value)) {
return false;
}
break;
case GPBType::FIXED64:
if (!GPBWire::writeFixed64($output, $value)) {
return false;
}
break;
case GPBType::BOOL:
if (!GPBWire::writeBool($output, $value)) {
return false;
}
break;
case GPBType::STRING:
if (!GPBWire::writeString($output, $value)) {
return false;
}
break;
// case GPBType::GROUP:
// echo "GROUP\xA";
// trigger_error("Not implemented.", E_ERROR);
// break;
case GPBType::MESSAGE:
if (!GPBWire::writeMessage($output, $value)) {
return false;
}
break;
case GPBType::BYTES:
if (!GPBWire::writeBytes($output, $value)) {
return false;
}
break;
case GPBType::UINT32:
if (!GPBWire::writeUint32($output, $value)) {
return false;
}
break;
case GPBType::ENUM:
if (!GPBWire::writeInt32($output, $value)) {
return false;
}
break;
case GPBType::SFIXED32:
if (!GPBWire::writeSfixed32($output, $value)) {
return false;
}
break;
case GPBType::SFIXED64:
if (!GPBWire::writeSfixed64($output, $value)) {
return false;
}
break;
case GPBType::SINT32:
if (!GPBWire::writeSint32($output, $value)) {
return false;
}
break;
case GPBType::SINT64:
if (!GPBWire::writeSint64($output, $value)) {
return false;
}
break;
default:
user_error("Unsupported type.");
return false;
}
return true;
}
}

@ -0,0 +1,323 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
use Google\Protobuf\Internal\Uint64;
class InputStream
{
private $buffer;
private $buffer_size_after_limit;
private $buffer_end;
private $current;
private $current_limit;
private $legitimate_message_end;
private $recursion_budget;
private $recursion_limit;
private $total_bytes_limit;
private $total_bytes_read;
const MAX_VARINT_BYTES = 10;
const MAX_VARINT32_BYTES = 5;
const DEFAULT_RECURSION_LIMIT = 100;
const DEFAULT_TOTAL_BYTES_LIMIT = 33554432; // 32 << 20, 32MB
public function __construct($buffer)
{
$start = 0;
$end = strlen($buffer);
$this->buffer = $buffer;
$this->buffer_size_after_limit = 0;
$this->buffer_end = $end;
$this->current = $start;
$this->current_limit = $end;
$this->legitimate_message_end = false;
$this->recursion_budget = self::DEFAULT_RECURSION_LIMIT;
$this->recursion_limit = self::DEFAULT_RECURSION_LIMIT;
$this->total_bytes_limit = self::DEFAULT_TOTAL_BYTES_LIMIT;
$this->total_bytes_read = $end - $start;
}
private function advance($amount)
{
$this->current += $amount;
}
private function bufferSize()
{
return $this->buffer_end - $this->current;
}
private function current()
{
return $this->total_bytes_read -
($this->buffer_end - $this->current +
$this->buffer_size_after_limit);
}
private function recomputeBufferLimits()
{
$this->buffer_end += $this->buffer_size_after_limit;
$closest_limit = min($this->current_limit, $this->total_bytes_limit);
if ($closest_limit < $this->total_bytes_read) {
// The limit position is in the current buffer. We must adjust the
// buffer size accordingly.
$this->buffer_size_after_limit = $this->total_bytes_read -
$closest_limit;
$this->buffer_end -= $this->buffer_size_after_limit;
} else {
$this->buffer_size_after_limit = 0;
}
}
private function consumedEntireMessage()
{
return $this->legitimate_message_end;
}
/**
* Read uint32 into $var. Advance buffer with consumed bytes. If the
* contained varint is larger than 32 bits, discard the high order bits.
* @param $var.
*/
public function readVarint32(&$var)
{
if (!$this->readVarint64($var)) {
return false;
}
$var = $var->toInteger() & 0xFFFFFFFF;
// Convert large uint32 to int32.
if (PHP_INT_SIZE === 8 && ($var > 0x7FFFFFFF)) {
$var = $var | (0xFFFFFFFF << 32);
}
return true;
}
/**
* Read Uint64 into $var. Advance buffer with consumed bytes.
* @param $var.
*/
public function readVarint64(&$var)
{
$result = new Uint64(0);
$count = 0;
$b = 0;
do {
if ($this->current === $this->buffer_end) {
return false;
}
if ($count === self::MAX_VARINT_BYTES) {
return false;
}
$b = ord($this->buffer[$this->current]);
$result->bitOr((new Uint64($b & 0x7F))->leftShift(7 * $count));
$this->advance(1);
$count += 1;
} while ($b & 0x80);
$var = $result;
return true;
}
/**
* Read int into $var. If the result is larger than the largest integer, $var
* will be -1. Advance buffer with consumed bytes.
* @param $var.
*/
public function readVarintSizeAsInt(&$var)
{
if (!$this->readVarint64($var)) {
return false;
}
$var = $var->toInteger();
return true;
}
/**
* Read 32-bit unsiged integer to $var. If the buffer has less than 4 bytes,
* return false. Advance buffer with consumed bytes.
* @param $var.
*/
public function readLittleEndian32(&$var)
{
$data = null;
if (!$this->readRaw(4, $data)) {
return false;
}
$var = unpack('V', $data);
$var = $var[1];
return true;
}
/**
* Read 64-bit unsiged integer to $var. If the buffer has less than 8 bytes,
* return false. Advance buffer with consumed bytes.
* @param $var.
*/
public function readLittleEndian64(&$var)
{
$data = null;
if (!$this->readRaw(4, $data)) {
return false;
}
$low = unpack('V', $data)[1];
if (!$this->readRaw(4, $data)) {
return false;
}
$high = unpack('V', $data)[1];
$var = Uint64::newValue($high, $low);
return true;
}
/**
* Read tag into $var. Advance buffer with consumed bytes.
* @param $var.
*/
public function readTag()
{
if ($this->current === $this->buffer_end) {
// Make sure that it failed due to EOF, not because we hit
// total_bytes_limit, which, unlike normal limits, is not a valid
// place to end a message.
$current_position = $this->total_bytes_read -
$this->buffer_size_after_limit;
if ($current_position >= $this->total_bytes_limit) {
// Hit total_bytes_limit_. But if we also hit the normal limit,
// we're still OK.
$this->legitimate_message_end =
($this->current_limit === $this->total_bytes_limit);
} else {
$this->legitimate_message_end = true;
}
return 0;
}
$result = 0;
// The larget tag is 2^29 - 1, which can be represented by int32.
$success = $this->readVarint32($result);
if ($success) {
return $result;
} else {
return 0;
}
}
public function readRaw($size, &$buffer)
{
$current_buffer_size = 0;
if ($this->bufferSize() < $size) {
return false;
}
$buffer = substr($this->buffer, $this->current, $size);
$this->advance($size);
return true;
}
/* Places a limit on the number of bytes that the stream may read, starting
* from the current position. Once the stream hits this limit, it will act
* like the end of the input has been reached until popLimit() is called.
*
* As the names imply, the stream conceptually has a stack of limits. The
* shortest limit on the stack is always enforced, even if it is not the top
* limit.
*
* The value returned by pushLimit() is opaque to the caller, and must be
* passed unchanged to the corresponding call to popLimit().
*
* @param integer $byte_limit
*/
public function pushLimit($byte_limit)
{
// Current position relative to the beginning of the stream.
$current_position = $this->current();
$old_limit = $this->current_limit;
// security: byte_limit is possibly evil, so check for negative values
// and overflow.
if ($byte_limit >= 0 && $byte_limit <= PHP_INT_MAX - $current_position) {
$this->current_limit = $current_position + $byte_limit;
} else {
// Negative or overflow.
$this->current_limit = PHP_INT_MAX;
}
// We need to enforce all limits, not just the new one, so if the previous
// limit was before the new requested limit, we continue to enforce the
// previous limit.
$this->current_limit = min($this->current_limit, $old_limit);
$this->recomputeBufferLimits();
return $old_limit;
}
/* The limit passed in is actually the *old* limit, which we returned from
* PushLimit().
*
* @param integer $byte_limit
*/
public function popLimit($byte_limit)
{
$this->current_limit = $byte_limit;
$this->recomputeBufferLimits();
// We may no longer be at a legitimate message end. ReadTag() needs to
// be called again to find out.
$this->legitimate_message_end = false;
}
public function incrementRecursionDepthAndPushLimit(
$byte_limit, &$old_limit, &$recursion_budget)
{
$old_limit = $this->pushLimit($byte_limit);
$recursion_limit = --$this->recursion_limit;
}
public function decrementRecursionDepthAndPopLimit($byte_limit)
{
$result = $this->consumedEntireMessage();
$this->popLimit($byte_limit);
++$this->recursion_budget;
return $result;
}
public function bytesUntilLimit()
{
if ($this->current_limit === PHP_INT_MAX) {
return -1;
}
return $this->current_limit - $this->current;
}
}

@ -0,0 +1,57 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
use Google\Protobuf\Internal\Message;
class MapEntry extends Message
{
public $key;
public $value;
public function setKey(&$key) {
$this->key = $key;
}
public function getKey() {
return $this->key;
}
public function setValue(&$value) {
$this->value = $value;
}
public function getValue() {
return $this->value;
}
}

@ -0,0 +1,321 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/**
* MapField and MapFieldIter are used by generated protocol message classes to
* manipulate map fields.
*/
namespace Google\Protobuf\Internal;
/**
* MapFieldIter is used to iterate MapField. It is also need for the foreach
* syntax.
*/
class MapFieldIter implements \Iterator
{
/**
* @ignore
*/
private $container;
/**
* Create iterator instance for MapField.
*
* @param MapField The MapField instance for which this iterator is
* created.
* @ignore
*/
public function __construct($container)
{
$this->container = $container;
}
/**
* Reset the status of the iterator
*
* @return void
*/
public function rewind()
{
return reset($this->container);
}
/**
* Return the element at the current position.
*
* @return object The element at the current position.
*/
public function current()
{
return current($this->container);
}
/**
* Return the current key.
*
* @return object The current key.
*/
public function key()
{
return key($this->container);
}
/**
* Move to the next position.
*
* @return void
*/
public function next()
{
return next($this->container);
}
/**
* Check whether there are more elements to iterate.
*
* @return bool True if there are more elements to iterate.
*/
public function valid()
{
return key($this->container) !== null;
}
}
/**
* @ignore
*/
function checkKey($key_type, &$key)
{
switch ($key_type) {
case GPBType::INT32:
GPBUtil::checkInt32($key);
break;
case GPBType::UINT32:
GPBUtil::checkUint32($key);
break;
case GPBType::INT64:
GPBUtil::checkInt64($key);
break;
case GPBType::UINT64:
GPBUtil::checkUint64($key);
break;
case GPBType::FIXED64:
GPBUtil::checkUint64($key);
break;
case GPBType::FIXED32:
GPBUtil::checkUint32($key);
break;
case GPBType::SFIXED64:
GPBUtil::checkInt64($key);
break;
case GPBType::SFIXED32:
GPBUtil::checkInt32($key);
break;
case GPBType::SINT64:
GPBUtil::checkInt64($key);
break;
case GPBType::SINT32:
GPBUtil::checkInt32($key);
break;
case GPBType::BOOL:
GPBUtil::checkBool($key);
break;
case GPBType::STRING:
GPBUtil::checkString($key, true);
break;
default:
var_dump($key_type);
trigger_error(
"Given type cannot be map key.",
E_USER_ERROR);
break;
}
}
/**
* MapField is used by generated protocol message classes to manipulate map
* fields. It can be used like native PHP array.
*/
class MapField implements \ArrayAccess, \IteratorAggregate, \Countable
{
/**
* @ignore
*/
private $container;
/**
* @ignore
*/
private $key_type;
/**
* @ignore
*/
private $value_type;
/**
* @ignore
*/
private $value_klass;
/**
* Constructs an instance of MapField.
*
* @param long $key_type Type of the stored key element.
* @param long $value_type Type of the stored value element.
* @param string $klass Message/Enum class name of value instance
* (message/enum fields only).
* @ignore
*/
public function __construct($key_type, $value_type, $klass = null)
{
$this->container = [];
$this->key_type = $key_type;
$this->value_type = $value_type;
$this->klass = $klass;
}
/**
* Return the element at the given key.
*
* This will also be called for: $ele = $arr[$key]
*
* @param object $key The key of the element to be fetched.
* @return object The stored element at given key.
* @throws ErrorException Invalid type for index.
* @throws ErrorException Non-existing index.
*/
public function offsetGet($key)
{
return $this->container[$key];
}
/**
* Assign the element at the given key.
*
* This will also be called for: $arr[$key] = $value
*
* @param object $key The key of the element to be fetched.
* @param object $value The element to be assigned.
* @return void
* @throws ErrorException Invalid type for key.
* @throws ErrorException Invalid type for value.
* @throws ErrorException Non-existing key.
*/
public function offsetSet($key, $value)
{
checkKey($this->key_type, $key);
switch ($this->value_type) {
case GPBType::INT32:
GPBUtil::checkInt32($value);
break;
case GPBType::UINT32:
GPBUtil::checkUint32($value);
break;
case GPBType::INT64:
GPBUtil::checkInt64($value);
break;
case GPBType::UINT64:
GPBUtil::checkUint64($value);
break;
case GPBType::FLOAT:
GPBUtil::checkFloat($value);
break;
case GPBType::DOUBLE:
GPBUtil::checkDouble($value);
break;
case GPBType::BOOL:
GPBUtil::checkBool($value);
break;
case GPBType::STRING:
GPBUtil::checkString($value, true);
break;
case GPBType::MESSAGE:
GPBUtil::checkMessage($value, $this->klass);
break;
default:
break;
}
$this->container[$key] = $value;
}
/**
* Remove the element at the given key.
*
* This will also be called for: unset($arr)
*
* @param object $key The key of the element to be removed.
* @return void
* @throws ErrorException Invalid type for key.
*/
public function offsetUnset($key)
{
checkKey($this->key_type, $key);
unset($this->container[$key]);
}
/**
* Check the existence of the element at the given key.
*
* This will also be called for: isset($arr)
*
* @param object $key The key of the element to be removed.
* @return bool True if the element at the given key exists.
* @throws ErrorException Invalid type for key.
*/
public function offsetExists($key)
{
checkKey($this->key_type, $key);
return isset($this->container[$key]);
}
/**
* @ignore
*/
public function getIterator()
{
return new MapFieldIter($this->container);
}
/**
* Return the number of stored elements.
*
* This will also be called for: count($arr)
*
* @return integer The number of stored elements.
*/
public function count()
{
return count($this->container);
}
}

@ -0,0 +1,671 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/**
* Defines Message, the parent class extended by all protocol message classes.
*/
namespace Google\Protobuf\Internal;
use Google\Protobuf\Internal\InputStream;
use Google\Protobuf\Internal\OutputStream;
use Google\Protobuf\Internal\DescriptorPool;
use Google\Protobuf\Internal\GPBLabel;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\GPBWire;
use Google\Protobuf\Internal\MapEntry;
use Google\Protobuf\Internal\RepeatedField;
/**
* Parent class of all proto messages. Users should not instantiate this class
* or extend this class or its child classes by their own. See the comment of
* specific functions for more details.
*/
class Message
{
/**
* @ignore
*/
private $desc;
/**
* @ignore
*/
public function __construct($desc = NULL)
{
// MapEntry message is shared by all types of map fields, whose
// descriptors are different from each other. Thus, we cannot find a
// specific descriptor from the descriptor pool.
if (get_class($this) === 'Google\Protobuf\Internal\MapEntry') {
$this->desc = $desc;
return;
}
$pool = DescriptorPool::getGeneratedPool();
$this->desc = $pool->getDescriptorByClassName(get_class($this));
foreach ($this->desc->getField() as $field) {
$setter = $field->getSetter();
if ($field->isMap()) {
$message_type = $field->getMessageType();
$key_field = $message_type->getFieldByNumber(1);
$value_field = $message_type->getFieldByNumber(2);
switch ($value_field->getType()) {
case GPBType::MESSAGE:
case GPBType::GROUP:
$this->$setter(
new MapField(
$key_field->getType(),
$value_field->getType(),
$value_field->getMessageType()->getClass()));
break;
case GPBType::ENUM:
$this->$setter(
new MapField(
$key_field->getType(),
$value_field->getType(),
$value_field->getEnumType()->getClass()));
break;
default:
$this->$setter(new MapField($key_field->getType(),
$value_field->getType()));
break;
}
} else if ($field->getLabel() === GPBLabel::REPEATED) {
switch ($field->getType()) {
case GPBType::MESSAGE:
case GPBType::GROUP:
$this->$setter(
new RepeatedField(
$field->getType(),
$field->getMessageType()->getClass()));
break;
case GPBType::ENUM:
$this->$setter(
new RepeatedField(
$field->getType(),
$field->getEnumType()->getClass()));
break;
default:
$this->$setter(new RepeatedField($field->getType()));
break;
}
} else if ($field->getOneofIndex() !== -1) {
$oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
$oneof_name = $oneof->getName();
$this->$oneof_name = new OneofField($oneof);
}
}
}
protected function readOneof($number)
{
$field = $this->desc->getFieldByNumber($number);
$oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
$oneof_name = $oneof->getName();
$oneof_field = $this->$oneof_name;
if ($number === $oneof_field->getNumber()) {
return $oneof_field->getValue();
} else {
return $this->defaultValue($field);
}
}
protected function writeOneof($number, $value)
{
$field = $this->desc->getFieldByNumber($number);
$oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
$oneof_name = $oneof->getName();
$oneof_field = $this->$oneof_name;
$oneof_field->setValue($value);
$oneof_field->setFieldName($field->getName());
$oneof_field->setNumber($number);
}
/**
* @ignore
*/
private function defaultValue($field)
{
$value = null;
switch ($field->getType()) {
case GPBType::DOUBLE:
case GPBType::FLOAT:
return 0.0;
case GPBType::UINT32:
case GPBType::UINT64:
case GPBType::INT32:
case GPBType::INT64:
case GPBType::FIXED32:
case GPBType::FIXED64:
case GPBType::SFIXED32:
case GPBType::SFIXED64:
case GPBType::SINT32:
case GPBType::SINT64:
case GPBType::ENUM:
return 0;
case GPBType::BOOL:
return false;
case GPBType::STRING:
case GPBType::BYTES:
return "";
case GPBType::GROUP:
case GPBType::MESSAGE:
return null;
default:
user_error("Unsupported type.");
return false;
}
}
/**
* @ignore
*/
private static function parseFieldFromStreamNoTag($input, $field, &$value)
{
switch ($field->getType()) {
case GPBType::DOUBLE:
if (!GPBWire::readDouble($input, $value)) {
return false;
}
break;
case GPBType::FLOAT:
if (!GPBWire::readFloat($input, $value)) {
return false;
}
break;
case GPBType::INT64:
if (!GPBWire::readInt64($input, $value)) {
return false;
}
$value = $value->toInteger();
break;
case GPBType::UINT64:
if (!GPBWire::readUint64($input, $value)) {
return false;
}
$value = $value->toInteger();
break;
case GPBType::INT32:
if (!GPBWire::readInt32($input, $value)) {
return false;
}
break;
case GPBType::FIXED64:
if (!GPBWire::readFixed64($input, $value)) {
return false;
}
$value = $value->toInteger();
break;
case GPBType::FIXED32:
if (!GPBWire::readFixed32($input, $value)) {
return false;
}
break;
case GPBType::BOOL:
if (!GPBWire::readBool($input, $value)) {
return false;
}
break;
case GPBType::STRING:
// TODO(teboring): Add utf-8 check.
if (!GPBWire::readString($input, $value)) {
return false;
}
break;
case GPBType::GROUP:
echo "GROUP\xA";
trigger_error("Not implemented.", E_ERROR);
break;
case GPBType::MESSAGE:
if ($field->isMap()) {
$value = new MapEntry($field->getMessageType());
} else {
$klass = $field->getMessageType()->getClass();
$value = new $klass;
}
if (!GPBWire::readMessage($input, $value)) {
return false;
}
break;
case GPBType::BYTES:
if (!GPBWire::readString($input, $value)) {
return false;
}
break;
case GPBType::UINT32:
if (!GPBWire::readUint32($input, $value)) {
return false;
}
break;
case GPBType::ENUM:
// TODO(teboring): Check unknown enum value.
if (!GPBWire::readInt32($input, $value)) {
return false;
}
break;
case GPBType::SFIXED32:
if (!GPBWire::readSfixed32($input, $value)) {
return false;
}
break;
case GPBType::SFIXED64:
if (!GPBWire::readSfixed64($input, $value)) {
return false;
}
$value = $value->toInteger();
break;
case GPBType::SINT32:
if (!GPBWire::readSint32($input, $value)) {
return false;
}
break;
case GPBType::SINT64:
if (!GPBWire::readSint64($input, $value)) {
return false;
}
$value = $value->toInteger();
break;
default:
user_error("Unsupported type.");
return false;
}
return true;
}
/**
* @ignore
*/
private function parseFieldFromStream($tag, $input, $field)
{
$value = null;
$field_type = $field->getType();
$value_format = GPBWire::UNKNOWN;
if (GPBWire::getTagWireType($tag) ===
GPBWire::getWireType($field_type)) {
$value_format = GPBWire::NORMAL_FORMAT;
} elseif ($field->isPackable() &&
GPBWire::getTagWireType($tag) ===
GPBWire::WIRETYPE_LENGTH_DELIMITED) {
$value_format = GPBWire::PACKED_FORMAT;
}
if ($value_format === GPBWire::NORMAL_FORMAT) {
if (!self::parseFieldFromStreamNoTag($input, $field, $value)) {
return false;
}
} elseif ($value_format === GPBWire::PACKED_FORMAT) {
$length = 0;
if (!GPBWire::readInt32($input, $length)) {
return false;
}
$limit = $input->pushLimit($length);
$getter = $field->getGetter();
while ($input->bytesUntilLimit() > 0) {
if (!self::parseFieldFromStreamNoTag($input, $field, $value)) {
return false;
}
$this->$getter()[] = $value;
}
$input->popLimit($limit);
return true;
} else {
return false;
}
if ($field->isMap()) {
$getter = $field->getGetter();
$this->$getter()[$value->getKey()] = $value->getValue();
} else if ($field->isRepeated()) {
$getter = $field->getGetter();
$this->$getter()[] = $value;
} else {
$setter = $field->getSetter();
$this->$setter($value);
}
return true;
}
/**
* Parses a protocol buffer contained in a string.
*
* This function takes a string in the (non-human-readable) binary wire
* format, matching the encoding output by encode().
*
* @param string $data Binary protobuf data.
* @return bool Return true on success.
*/
public function decode($data)
{
$input = new InputStream($data);
$this->parseFromStream($input);
}
/**
* @ignore
*/
public function parseFromStream($input)
{
while (true) {
$tag = $input->readTag();
// End of input. This is a valid place to end, so return true.
if ($tag === 0) {
return true;
}
$number = GPBWire::getTagFieldNumber($tag);
$field = $this->desc->getFieldByNumber($number);
if (!$this->parseFieldFromStream($tag, $input, $field)) {
return false;
}
}
}
/**
* @ignore
*/
private function serializeSingularFieldToStream($field, &$output)
{
if (!$this->existField($field)) {
return true;
}
$getter = $field->getGetter();
$value = $this->$getter();
if (!GPBWire::serializeFieldToStream($value, $field, true, $output)) {
return false;
}
return true;
}
/**
* @ignore
*/
private function serializeRepeatedFieldToStream($field, &$output)
{
$getter = $field->getGetter();
$values = $this->$getter();
$count = count($values);
if ($count === 0) {
return true;
}
$packed = $field->getPacked();
if ($packed) {
if (!GPBWire::writeTag(
$output,
GPBWire::makeTag($field->getNumber(), GPBType::STRING))) {
return false;
}
$size = 0;
foreach ($values as $value) {
$size += $this->fieldDataOnlyByteSize($field, $value);
}
if (!$output->writeVarint32($size)) {
return false;
}
}
foreach ($values as $value) {
if (!GPBWire::serializeFieldToStream(
$value,
$field,
!$packed,
$output)) {
return false;
}
}
return true;
}
/**
* @ignore
*/
private function serializeMapFieldToStream($field, $output)
{
$getter = $field->getGetter();
$values = $this->$getter();
$count = count($values);
if ($count === 0) {
return true;
}
foreach ($values as $key => $value) {
$map_entry = new MapEntry($field->getMessageType());
$map_entry->setKey($key);
$map_entry->setValue($value);
if (!GPBWire::serializeFieldToStream(
$map_entry,
$field,
true,
$output)) {
return false;
}
}
return true;
}
/**
* @ignore
*/
private function serializeFieldToStream(&$output, $field)
{
if ($field->isMap()) {
return $this->serializeMapFieldToStream($field, $output);
} elseif ($field->isRepeated()) {
return $this->serializeRepeatedFieldToStream($field, $output);
} else {
return $this->serializeSingularFieldToStream($field, $output);
}
}
/**
* @ignore
*/
public function serializeToStream(&$output)
{
$fields = $this->desc->getField();
foreach ($fields as $field) {
if (!$this->serializeFieldToStream($output, $field)) {
return false;
}
}
return true;
}
/**
* Serialize the message to string.
* @return string Serialized binary protobuf data.
*/
public function encode()
{
$output = new OutputStream($this->byteSize());
$this->serializeToStream($output);
return $output->getData();
}
/**
* @ignore
*/
private function existField($field)
{
$getter = $field->getGetter();
$value = $this->$getter();
return $value !== $this->defaultValue($field);
}
/**
* @ignore
*/
private function repeatedFieldDataOnlyByteSize($field)
{
$size = 0;
$getter = $field->getGetter();
$values = $this->$getter();
$count = count($values);
if ($count !== 0) {
$size += $count * GPBWire::tagSize($field);
foreach ($values as $value) {
$size += $this->singularFieldDataOnlyByteSize($field);
}
}
}
/**
* @ignore
*/
private function fieldDataOnlyByteSize($field, $value)
{
$size = 0;
switch ($field->getType()) {
case GPBType::BOOL:
$size += 1;
break;
case GPBType::FLOAT:
case GPBType::FIXED32:
case GPBType::SFIXED32:
$size += 4;
break;
case GPBType::DOUBLE:
case GPBType::FIXED64:
case GPBType::SFIXED64:
$size += 8;
break;
case GPBType::UINT32:
case GPBType::INT32:
case GPBType::ENUM:
$size += GPBWire::varint32Size($value);
break;
case GPBType::UINT64:
case GPBType::INT64:
$size += GPBWire::varint64Size($value);
break;
case GPBType::SINT32:
$size += GPBWire::sint32Size($value);
break;
case GPBType::SINT64:
$size += GPBWire::sint64Size($value);
break;
case GPBType::STRING:
case GPBType::BYTES:
$size += strlen($value);
$size += GPBWire::varint32Size($size);
break;
case GPBType::MESSAGE:
$size += $value->byteSize();
$size += GPBWire::varint32Size($size);
break;
case GPBType::GROUP:
// TODO(teboring): Add support.
user_error("Unsupported type.");
break;
default:
user_error("Unsupported type.");
return 0;
}
return $size;
}
/**
* @ignore
*/
private function fieldByteSize($field)
{
$size = 0;
if ($field->isMap()) {
$getter = $field->getGetter();
$values = $this->$getter();
$count = count($values);
if ($count !== 0) {
$size += $count * GPBWire::tagSize($field);
$message_type = $field->getMessageType();
$key_field = $message_type->getFieldByNumber(1);
$value_field = $message_type->getFieldByNumber(2);
foreach ($values as $key => $value) {
$data_size = 0;
$data_size += $this->fieldDataOnlyByteSize($key_field, $key);
$data_size += $this->fieldDataOnlyByteSize(
$value_field,
$value);
$data_size += GPBWire::tagSize($key_field);
$data_size += GPBWire::tagSize($value_field);
$size += GPBWire::varint32Size($data_size) + $data_size;
}
}
} elseif ($field->isRepeated()) {
$getter = $field->getGetter();
$values = $this->$getter();
$count = count($values);
if ($count !== 0) {
if ($field->getPacked()) {
$data_size = 0;
foreach ($values as $value) {
$data_size += $this->fieldDataOnlyByteSize($field, $value);
}
$size += GPBWire::tagSize($field);
$size += GPBWire::varint32Size($data_size);
$size += $data_size;
} else {
$size += $count * GPBWire::tagSize($field);
foreach ($values as $value) {
$size += $this->fieldDataOnlyByteSize($field, $value);
}
}
}
} elseif ($this->existField($field)) {
$size += GPBWire::tagSize($field);
$getter = $field->getGetter();
$value = $this->$getter();
$size += $this->fieldDataOnlyByteSize($field, $value);
}
return $size;
}
/**
* @ignore
*/
public function byteSize()
{
$size = 0;
$fields = $this->desc->getField();
foreach ($fields as $field) {
$size += $this->fieldByteSize($field);
}
return $size;
}
}

@ -0,0 +1,120 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
use Google\Protobuf\Internal\GPBLabel;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\Descriptor;
use Google\Protobuf\Internal\FieldDescriptor;
class MessageBuilderContext
{
private $descriptor;
private $pool;
public function __construct($full_name, $klass, $pool)
{
$this->descriptor = new Descriptor();
$this->descriptor->setFullName($full_name);
$this->descriptor->setClass($klass);
$this->pool = $pool;
}
private function getFieldDescriptor($name, $label, $type,
$number, $type_name = null)
{
$field = new FieldDescriptor();
$field->setName($name);
$camel_name = implode('', array_map('ucwords', explode('_', $name)));
$field->setGetter('get' . $camel_name);
$field->setSetter('set' . $camel_name);
$field->setType($type);
$field->setNumber($number);
$field->setLabel($label);
// At this time, the message/enum type may have not been added to pool.
// So we use the type name as place holder and will replace it with the
// actual descriptor in cross building.
switch ($type) {
case GPBType::MESSAGE:
$field->setMessageType($type_name);
break;
case GPBType::ENUM:
$field->setEnumType($type_name);
break;
default:
break;
}
return $field;
}
public function optional($name, $type, $number, $type_name = null)
{
$this->descriptor->addField($this->getFieldDescriptor(
$name,
GPBLabel::OPTIONAL,
$type,
$number,
$type_name));
return $this;
}
public function repeated($name, $type, $number, $type_name = null)
{
$this->descriptor->addField($this->getFieldDescriptor(
$name,
GPBLabel::REPEATED,
$type,
$number,
$type_name));
return $this;
}
public function required($name, $type, $number, $type_name = null)
{
$this->descriptor->addField($this->getFieldDescriptor(
$name,
GPBLabel::REQUIRED,
$type,
$number,
$type_name));
return $this;
}
public function finalizeToPool()
{
$this->pool->addDescriptor($this->descriptor);
}
}

@ -0,0 +1,77 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
class OneofField
{
private $desc;
private $field_name;
private $number = 0;
private $value;
public function __construct($desc)
{
$this->desc = $desc;
}
public function setValue($value)
{
$this->value = $value;
}
public function getValue()
{
return $this->value;
}
public function setFieldName($field_name)
{
$this->field_name = $field_name;
}
public function getFieldName()
{
return $this->field_name;
}
public function setNumber($number)
{
$this->number = $number;
}
public function getNumber()
{
return $this->number;
}
}

@ -0,0 +1,143 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
class OutputStream
{
private $buffer;
private $buffer_size;
private $current;
const MAX_VARINT32_BYTES = 5;
const MAX_VARINT64_BYTES = 10;
public function __construct($size)
{
$this->current = 0;
$this->buffer_size = $size;
$this->buffer = str_repeat(chr(0), $this->buffer_size);
}
public function getData()
{
return $this->buffer;
}
public function writeVarint32($value)
{
$bytes = str_repeat(chr(0), self::MAX_VARINT32_BYTES);
$size = self::writeVarintToArray($value, $bytes, true);
return $this->writeRaw($bytes, $size);
}
public function writeVarint64($value)
{
$bytes = str_repeat(chr(0), self::MAX_VARINT64_BYTES);
$size = self::writeVarintToArray($value, $bytes);
return $this->writeRaw($bytes, $size);
}
public function writeLittleEndian32($value)
{
$bytes = str_repeat(chr(0), 4);
$size = self::writeLittleEndian32ToArray($value, $bytes);
return $this->writeRaw($bytes, $size);
}
public function writeLittleEndian64($value)
{
$bytes = str_repeat(chr(0), 8);
$size = self::writeLittleEndian64ToArray($value, $bytes);
return $this->writeRaw($bytes, $size);
}
public function writeTag($tag)
{
return $this->writeVarint32($tag);
}
public function writeRaw($data, $size)
{
if ($this->buffer_size < $size) {
var_dump($this->buffer_size);
var_dump($size);
trigger_error("Output stream doesn't have enough buffer.");
return false;
}
for ($i = 0; $i < $size; $i++) {
$this->buffer[$this->current] = $data[$i];
$this->current++;
$this->buffer_size--;
}
return true;
}
private static function writeVarintToArray($value, &$buffer, $trim = false)
{
$current = 0;
if ($trim) {
$value &= 0xFFFFFFFF;
}
while ($value >= 0x80 || $value < 0) {
$buffer[$current] = chr($value | 0x80);
$value = ($value >> 7) & ~(0x7F << ((PHP_INT_SIZE << 3) - 7));
$current++;
}
$buffer[$current] = chr($value);
return $current + 1;
}
private static function writeLittleEndian32ToArray($value, &$buffer)
{
$buffer[0] = chr($value & 0x000000FF);
$buffer[1] = chr(($value >> 8) & 0x000000FF);
$buffer[2] = chr(($value >> 16) & 0x000000FF);
$buffer[3] = chr(($value >> 24) & 0x000000FF);
return 4;
}
private static function writeLittleEndian64ToArray($value, &$buffer)
{
$buffer[0] = chr($value & 0x000000FF);
$buffer[1] = chr(($value >> 8) & 0x000000FF);
$buffer[2] = chr(($value >> 16) & 0x000000FF);
$buffer[3] = chr(($value >> 24) & 0x000000FF);
$buffer[4] = chr(($value >> 32) & 0x000000FF);
$buffer[5] = chr(($value >> 40) & 0x000000FF);
$buffer[6] = chr(($value >> 48) & 0x000000FF);
$buffer[7] = chr(($value >> 56) & 0x000000FF);
return 8;
}
}

@ -0,0 +1,303 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/**
* RepeatedField and RepeatedFieldIter are used by generated protocol message
* classes to manipulate repeated fields.
*/
namespace Google\Protobuf\Internal;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\GPBUtil;
/**
* RepeatedFieldIter is used to iterate RepeatedField. It is also need for the
* foreach syntax.
*/
class RepeatedFieldIter implements \Iterator
{
/**
* @ignore
*/
private $position;
/**
* @ignore
*/
private $container;
/**
* Create iterator instance for RepeatedField.
*
* @param RepeatedField The RepeatedField instance for which this iterator
* is created.
* @ignore
*/
public function __construct($container)
{
$this->position = 0;
$this->container = $container;
}
/**
* Reset the status of the iterator
*
* @return void
*/
public function rewind()
{
$this->position = 0;
}
/**
* Return the element at the current position.
*
* @return object The element at the current position.
*/
public function current()
{
return $this->container[$this->position];
}
/**
* Return the current position.
*
* @return integer The current position.
*/
public function key()
{
return $this->position;
}
/**
* Move to the next position.
*
* @return void
*/
public function next()
{
++$this->position;
}
/**
* Check whether there are more elements to iterate.
*
* @return bool True if there are more elements to iterate.
*/
public function valid()
{
return isset($this->container[$this->position]);
}
}
/**
* RepeatedField is used by generated protocol message classes to manipulate
* repeated fields. It can be used like native PHP array.
*/
class RepeatedField implements \ArrayAccess, \IteratorAggregate, \Countable
{
/**
* @ignore
*/
private $container;
/**
* @ignore
*/
private $type;
/**
* @ignore
*/
private $klass;
/**
* Constructs an instance of RepeatedField.
*
* @param long $type Type of the stored element.
* @param string $klass Message/Enum class name (message/enum fields only).
* @ignore
*/
public function __construct($type, $klass = null)
{
$this->container = [];
$this->type = $type;
$this->klass = $klass;
}
/**
* @ignore
*/
public function getType()
{
return $this->type;
}
/**
* @ignore
*/
public function getClass()
{
return $this->klass;
}
/**
* Return the element at the given index.
*
* This will also be called for: $ele = $arr[0]
*
* @param long $offset The index of the element to be fetched.
* @return object The stored element at given index.
* @throws ErrorException Invalid type for index.
* @throws ErrorException Non-existing index.
*/
public function offsetGet($offset)
{
return $this->container[$offset];
}
/**
* Assign the element at the given index.
*
* This will also be called for: $arr []= $ele and $arr[0] = ele
*
* @param long $offset The index of the element to be assigned.
* @param object $value The element to be assigned.
* @return void
* @throws ErrorException Invalid type for index.
* @throws ErrorException Non-existing index.
* @throws ErrorException Incorrect type of the element.
*/
public function offsetSet($offset, $value)
{
switch ($this->type) {
case GPBType::INT32:
GPBUtil::checkInt32($value);
break;
case GPBType::UINT32:
GPBUtil::checkUint32($value);
break;
case GPBType::INT64:
GPBUtil::checkInt64($value);
break;
case GPBType::UINT64:
GPBUtil::checkUint64($value);
break;
case GPBType::FLOAT:
GPBUtil::checkFloat($value);
break;
case GPBType::DOUBLE:
GPBUtil::checkDouble($value);
break;
case GPBType::BOOL:
GPBUtil::checkBool($value);
break;
case GPBType::STRING:
GPBUtil::checkString($value, true);
break;
case GPBType::MESSAGE:
GPBUtil::checkMessage($value, $this->klass);
break;
default:
break;
}
if (is_null($offset)) {
$this->container[] = $value;
} else {
$count = count($this->container);
if (!is_numeric($offset) || $offset < 0 || $offset >= $count) {
trigger_error(
"Cannot modify element at the given index",
E_USER_ERROR);
return;
}
$this->container[$offset] = $value;
}
}
/**
* Remove the element at the given index.
*
* This will also be called for: unset($arr)
*
* @param long $offset The index of the element to be removed.
* @return void
* @throws ErrorException Invalid type for index.
* @throws ErrorException The element to be removed is not at the end of the
* RepeatedField.
*/
public function offsetUnset($offset)
{
$count = count($this->container);
if (!is_numeric($offset) || $count === 0 || $offset !== $count - 1) {
trigger_error(
"Cannot remove element at the given index",
E_USER_ERROR);
return;
}
array_pop($this->container);
}
/**
* Check the existence of the element at the given index.
*
* This will also be called for: isset($arr)
*
* @param long $offset The index of the element to be removed.
* @return bool True if the element at the given offset exists.
* @throws ErrorException Invalid type for index.
*/
public function offsetExists($offset)
{
return isset($this->container[$offset]);
}
/**
* @ignore
*/
public function getIterator()
{
return new RepeatedFieldIter($this->container);
}
/**
* Return the number of stored elements.
*
* This will also be called for: count($arr)
*
* @return integer The number of stored elements.
*/
public function count()
{
return count($this->container);
}
}

@ -0,0 +1,175 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
class GPBInteger
{
public $high = 0;
public $low = 0;
public function __construct($value = 0)
{
$this->low = $value & 0xFFFFFFFF;
if (PHP_INT_SIZE === 8) {
$this->high = ($value >> 32) & 0xFFFFFFFF;
}
}
// Return 0 for unsigned integers and 1 for signed integers.
protected function sign()
{
trigger_error("Not implemented", E_ERROR);
}
public function leftShift($count)
{
if ($count > 63) {
$this->low = 0;
$this->high = 0;
return;
}
if ($count > 32) {
$this->high = $this->low;
$this->low = 0;
$count -= 32;
}
$mask = (1 << $count) - 1;
$this->high = (($this->high << $count) & 0xFFFFFFFF) |
(($this->low >> (32 - $count)) & $mask);
$this->low = ($this->low << $count) & 0xFFFFFFFF;
$this->high &= 0xFFFFFFFF;
$this->low &= 0xFFFFFFFF;
return $this;
}
public function rightShift($count)
{
$sign = (($this->high & 0x80000000) >> 31) & $this->sign();
if ($count > 63) {
$this->low = -$sign;
$this->high = -$sign;
return;
}
if ($count > 32) {
$this->low = $this->high;
$this->high = -$sign;
$count -= 32;
}
$this->low = (($this->low >> $count) & 0xFFFFFFFF) |
(($this->high << (32 - $count)) & 0xFFFFFFFF);
$this->high = (($this->high >> $count) | (-$sign << $count));
$this->high &= 0xFFFFFFFF;
$this->low &= 0xFFFFFFFF;
return $this;
}
public function bitOr($var)
{
$this->high |= $var->high;
$this->low |= $var->low;
return $this;
}
public function bitXor($var)
{
$this->high ^= $var->high;
$this->low ^= $var->low;
return $this;
}
public function bitAnd($var)
{
$this->high &= $var->high;
$this->low &= $var->low;
return $this;
}
// Even: all zero; Odd: all one.
public function oddMask()
{
$low = (-($this->low & 1)) & 0xFFFFFFFF;
$high = $low;
return UInt64::newValue($high, $low);
}
public function toInteger()
{
if (PHP_INT_SIZE === 8) {
return ($this->high << 32) | $this->low;
} else {
return $this->low;
}
}
public function copy()
{
return static::newValue($this->high, $this->low);
}
}
class Uint64 extends GPBInteger
{
public static function newValue($high, $low)
{
$uint64 = new Uint64(0);
$uint64->high = $high;
$uint64->low = $low;
return $uint64;
}
protected function sign()
{
return 0;
}
}
class Int64 extends GPBInteger
{
public static function newValue($high, $low)
{
$int64 = new Int64(0);
$int64->high = $high;
$int64->low = $low;
return $int64;
}
protected function sign()
{
return 1;
}
}

@ -0,0 +1,541 @@
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Google\Protobuf\Internal;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\MessageOptions;
class FileDescriptor
{
private $package;
private $message_type = [];
private $enum_type = [];
public function setPackage($package)
{
$this->package = $package;
}
public function getPackage()
{
return $this->package;
}
public function getMessageType()
{
return $this->message_type;
}
public function addMessageType($desc)
{
$this->message_type[] = $desc;
}
public function getEnumType()
{
return $this->enum_type;
}
public function addEnumType($desc)
{
$this->enum_type[]= $desc;
}
public static function buildFromProto($proto)
{
$file = new FileDescriptor();
$file->setPackage($proto->getPackage());
foreach ($proto->getMessageType() as $message_proto) {
$file->addMessageType(Descriptor::buildFromProto(
$message_proto, $file->getPackage(), ""));
}
foreach ($proto->getEnumType() as $enum_proto) {
$file->getEnumType()[] =
$file->addEnumType(
EnumDescriptor::buildFromProto(
$enum_proto,
$file->getPackage(),
""));
}
return $file;
}
}
class Descriptor
{
private $full_name;
private $field = [];
private $nested_type = [];
private $enum_type = [];
private $klass;
private $options;
private $oneof_decl = [];
public function addOneofDecl($oneof)
{
$this->oneof_decl[] = $oneof;
}
public function getOneofDecl()
{
return $this->oneof_decl;
}
public function setFullName($full_name)
{
$this->full_name = $full_name;
}
public function getFullName()
{
return $this->full_name;
}
public function addField($field)
{
$this->field[$field->getNumber()] = $field;
}
public function getField()
{
return $this->field;
}
public function addNestedType($desc)
{
$this->nested_type[] = $desc;
}
public function getNestedType()
{
return $this->nested_type;
}
public function addEnumType($desc)
{
$this->enum_type[] = $desc;
}
public function getEnumType()
{
return $this->enum_type;
}
public function getFieldByNumber($number)
{
return $this->field[$number];
}
public function setClass($klass)
{
$this->klass = $klass;
}
public function getClass()
{
return $this->klass;
}
public function setOptions($options)
{
$this->options = $options;
}
public function getOptions()
{
return $this->options;
}
public static function buildFromProto($proto, $package, $containing)
{
$desc = new Descriptor();
$message_name_without_package = "";
$classname = "";
$fullname = "";
getFullClassName(
$proto,
$containing,
$package,
$message_name_without_package,
$classname,
$fullname);
$desc->setFullName($fullname);
$desc->setClass($classname);
$desc->setOptions($proto->getOptions());
foreach ($proto->getField() as $field_proto) {
$desc->addField(FieldDescriptor::buildFromProto($field_proto));
}
// Handle nested types.
foreach ($proto->getNestedType() as $nested_proto) {
$desc->addNestedType(Descriptor::buildFromProto(
$nested_proto, $package, $message_name_without_package));
}
// Handle oneof fields.
foreach ($proto->getOneofDecl() as $oneof_proto) {
$desc->addOneofDecl(
OneofDescriptor::buildFromProto($oneof_proto, $desc));
}
return $desc;
}
}
function getFullClassName(
$proto,
$containing,
$package,
&$message_name_without_package,
&$classname,
&$fullname)
{
// Full name needs to start with '.'.
$message_name_without_package = $proto->getName();
if ($containing !== "") {
$message_name_without_package =
$containing . "." . $message_name_without_package;
}
if ($package === "") {
$fullname = "." . $message_name_without_package;
} else {
$fullname = "." . $package . "." . $message_name_without_package;
}
// Nested message class names are seperated by '_', and package names are
// seperated by '\'.
$class_name_without_package =
implode('_', array_map('ucwords',
explode('.', $message_name_without_package)));
$classname =
implode('\\', array_map('ucwords', explode('.', $package))).
"\\".$class_name_without_package;
}
class OneofDescriptor
{
private $name;
private $fields;
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function addField(&$field)
{
$this->fields[] = $field;
}
public function getFields()
{
return $this->fields;
}
public static function buildFromProto($oneof_proto)
{
$oneof = new OneofDescriptor();
$oneof->setName($oneof_proto->getName());
return $oneof;
}
}
class EnumDescriptor
{
private $klass;
private $full_name;
private $value;
public function setFullName($full_name)
{
$this->full_name = $full_name;
}
public function getFullName()
{
return $this->full_name;
}
public function addValue($number, $value)
{
$this->value[$number] = $value;
}
public function setClass($klass)
{
$this->klass = $klass;
}
public function getClass()
{
return $this->klass;
}
public static function buildFromProto($proto, $package, $containing)
{
$desc = new EnumDescriptor();
$enum_name_without_package = "";
$classname = "";
$fullname = "";
getFullClassName(
$proto,
$containing,
$package,
$enum_name_without_package,
$classname,
$fullname);
$desc->setFullName($fullname);
$desc->setClass($classname);
return $desc;
}
}
class EnumValueDescriptor
{
}
class FieldDescriptor
{
private $name;
private $setter;
private $getter;
private $number;
private $label;
private $type;
private $message_type;
private $enum_type;
private $packed;
private $is_map;
private $oneof_index = -1;
public function setOneofIndex($index)
{
$this->oneof_index = $index;
}
public function getOneofIndex()
{
return $this->oneof_index;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function setSetter($setter)
{
$this->setter = $setter;
}
public function getSetter()
{
return $this->setter;
}
public function setGetter($getter)
{
$this->getter = $getter;
}
public function getGetter()
{
return $this->getter;
}
public function setNumber($number)
{
$this->number = $number;
}
public function getNumber()
{
return $this->number;
}
public function setLabel($label)
{
$this->label = $label;
}
public function getLabel()
{
return $this->label;
}
public function isRepeated()
{
return $this->label === GPBLabel::REPEATED;
}
public function setType($type)
{
$this->type = $type;
}
public function getType()
{
return $this->type;
}
public function setMessageType($message_type)
{
$this->message_type = $message_type;
}
public function getMessageType()
{
return $this->message_type;
}
public function setEnumType($enum_type)
{
$this->enum_type = $enum_type;
}
public function getEnumType()
{
return $this->enum_type;
}
public function setPacked($packed)
{
$this->packed = $packed;
}
public function getPacked()
{
return $this->packed;
}
public function isPackable()
{
return $this->isRepeated() && self::isTypePackable($this->type);
}
public function isMap()
{
return $this->getType() == GPBType::MESSAGE &&
!is_null($this->getMessageType()->getOptions()) &&
$this->getMessageType()->getOptions()->getMapEntry();
}
private static function isTypePackable($field_type)
{
return ($field_type !== GPBType::STRING &&
$field_type !== GPBType::GROUP &&
$field_type !== GPBType::MESSAGE &&
$field_type !== GPBType::BYTES);
}
public static function getFieldDescriptor(
$name,
$label,
$type,
$number,
$oneof_index,
$packed,
$type_name = null)
{
$field = new FieldDescriptor();
$field->setName($name);
$camel_name = implode('', array_map('ucwords', explode('_', $name)));
$field->setGetter('get' . $camel_name);
$field->setSetter('set' . $camel_name);
$field->setType($type);
$field->setNumber($number);
$field->setLabel($label);
$field->setPacked($packed);
$field->setOneofIndex($oneof_index);
// At this time, the message/enum type may have not been added to pool.
// So we use the type name as place holder and will replace it with the
// actual descriptor in cross building.
switch ($type) {
case GPBType::MESSAGE:
$field->setMessageType($type_name);
break;
case GPBType::ENUM:
$field->setEnumType($type_name);
break;
default:
break;
}
return $field;
}
public static function buildFromProto($proto)
{
$type_name = null;
switch ($proto->getType()) {
case GPBType::MESSAGE:
case GPBType::GROUP:
case GPBType::ENUM:
$type_name = $proto->getTypeName();
break;
default:
break;
}
$oneof_index = $proto->hasOneofIndex() ? $proto->getOneofIndex() : -1;
$packed = false;
$options = $proto->getOptions();
if ($options !== null) {
$packed = $options->getPacked();
}
return FieldDescriptor::getFieldDescriptor(
$proto->getName(), $proto->getLabel(), $proto->getType(),
$proto->getNumber(), $oneof_index, $packed, $type_name);
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Install phpDocumentor to generate docs. -->
<phpdoc>
<parser>
<target>doc</target>
</parser>
<transformer>
<target>doc</target>
</transformer>
<files>
<file>Google/Protobuf/Internal/MapField.php</file>
<file>Google/Protobuf/Internal/Message.php</file>
<file>Google/Protobuf/Internal/RepeatedField.php</file>
</files>
</phpdoc>

@ -0,0 +1,888 @@
<?php
require_once('test.pb.php');
require_once('test_util.php');
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBType;
use Foo\TestMessage;
use Foo\TestMessage_Sub;
class RepeatedFieldTest extends PHPUnit_Framework_TestCase
{
#########################################################
# Test int32 field.
#########################################################
public function testInt32()
{
$arr = new RepeatedField(GPBType::INT32);
// Test append.
$arr []= MAX_INT32;
$this->assertSame(MAX_INT32, $arr[0]);
$arr []= MIN_INT32;
$this->assertSame(MIN_INT32, $arr[1]);
$arr []= 1.1;
$this->assertSame(1, $arr[2]);
$arr []= MAX_INT32_FLOAT;
$this->assertSame(MAX_INT32, $arr[3]);
$arr []= MAX_INT32_FLOAT;
$this->assertSame(MAX_INT32, $arr[4]);
$arr []= '2';
$this->assertSame(2, $arr[5]);
$arr []= '3.1';
$this->assertSame(3, $arr[6]);
$arr []= MAX_INT32_STRING;
$this->assertSame(MAX_INT32, $arr[7]);
$this->assertEquals(8, count($arr));
for ($i = 0; $i < count($arr); $i++) {
$arr[$i] = 0;
$this->assertSame(0, $arr[$i]);
}
// Test set.
$arr [0]= MAX_INT32;
$this->assertSame(MAX_INT32, $arr[0]);
$arr [1]= MIN_INT32;
$this->assertSame(MIN_INT32, $arr[1]);
$arr [2]= 1.1;
$this->assertSame(1, $arr[2]);
$arr [3]= MAX_INT32_FLOAT;
$this->assertSame(MAX_INT32, $arr[3]);
$arr [4]= MAX_INT32_FLOAT;
$this->assertSame(MAX_INT32, $arr[4]);
$arr [5]= '2';
$this->assertSame(2, $arr[5]);
$arr [6]= '3.1';
$this->assertSame(3, $arr[6]);
$arr [7]= MAX_INT32_STRING;
$this->assertSame(MAX_INT32, $arr[7]);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt32AppendStringFail()
{
$arr = new RepeatedField(GPBType::INT32);
$arr []= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt32SetStringFail()
{
$arr = new RepeatedField(GPBType::INT32);
$arr []= 0;
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt32AppendMessageFail()
{
$arr = new RepeatedField(GPBType::INT32);
$arr []= new TestMessage_Sub();
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt32SetMessageFail()
{
$arr = new RepeatedField(GPBType::INT32);
$arr []= 0;
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test uint32 field.
#########################################################
public function testUint32()
{
$arr = new RepeatedField(GPBType::UINT32);
// Test append.
$arr []= MAX_UINT32;
$this->assertSame(-1, $arr[0]);
$arr []= -1;
$this->assertSame(-1, $arr[1]);
$arr []= MIN_UINT32;
$this->assertSame(MIN_UINT32, $arr[2]);
$arr []= 1.1;
$this->assertSame(1, $arr[3]);
$arr []= MAX_UINT32_FLOAT;
$this->assertSame(-1, $arr[4]);
$arr []= -1.0;
$this->assertSame(-1, $arr[5]);
$arr []= MIN_UINT32_FLOAT;
$this->assertSame(MIN_UINT32, $arr[6]);
$arr []= '2';
$this->assertSame(2, $arr[7]);
$arr []= '3.1';
$this->assertSame(3, $arr[8]);
$arr []= MAX_UINT32_STRING;
$this->assertSame(-1, $arr[9]);
$arr []= '-1.0';
$this->assertSame(-1, $arr[10]);
$arr []= MIN_UINT32_STRING;
$this->assertSame(MIN_UINT32, $arr[11]);
$this->assertEquals(12, count($arr));
for ($i = 0; $i < count($arr); $i++) {
$arr[$i] = 0;
$this->assertSame(0, $arr[$i]);
}
// Test set.
$arr [0]= MAX_UINT32;
$this->assertSame(-1, $arr[0]);
$arr [1]= -1;
$this->assertSame(-1, $arr[1]);
$arr [2]= MIN_UINT32;
$this->assertSame(MIN_UINT32, $arr[2]);
$arr [3]= 1.1;
$this->assertSame(1, $arr[3]);
$arr [4]= MAX_UINT32_FLOAT;
$this->assertSame(-1, $arr[4]);
$arr [5]= -1.0;
$this->assertSame(-1, $arr[5]);
$arr [6]= MIN_UINT32_FLOAT;
$this->assertSame(MIN_UINT32, $arr[6]);
$arr [7]= '2';
$this->assertSame(2, $arr[7]);
$arr [8]= '3.1';
$this->assertSame(3, $arr[8]);
$arr [9]= MAX_UINT32_STRING;
$this->assertSame(-1, $arr[9]);
$arr [10]= '-1.0';
$this->assertSame(-1, $arr[10]);
$arr [11]= MIN_UINT32_STRING;
$this->assertSame(MIN_UINT32, $arr[11]);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint32AppendStringFail()
{
$arr = new RepeatedField(GPBType::UINT32);
$arr []= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint32SetStringFail()
{
$arr = new RepeatedField(GPBType::UINT32);
$arr []= 0;
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint32AppendMessageFail()
{
$arr = new RepeatedField(GPBType::UINT32);
$arr []= new TestMessage_Sub();
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint32SetMessageFail()
{
$arr = new RepeatedField(GPBType::UINT32);
$arr []= 0;
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test int64 field.
#########################################################
public function testInt64()
{
$arr = new RepeatedField(GPBType::INT64);
// Test append.
$arr []= MAX_INT64;
$this->assertSame(MAX_INT64, $arr[0]);
$arr []= MIN_INT64;
$this->assertEquals(MIN_INT64, $arr[1]);
$arr []= 1.1;
$this->assertSame(1, $arr[2]);
$arr []= '2';
$this->assertSame(2, $arr[3]);
$arr []= '3.1';
$this->assertSame(3, $arr[4]);
$arr []= MAX_INT64_STRING;
$this->assertSame(MAX_INT64, $arr[5]);
$arr []= MIN_INT64_STRING;
$this->assertEquals(MIN_INT64, $arr[6]);
$this->assertEquals(7, count($arr));
for ($i = 0; $i < count($arr); $i++) {
$arr[$i] = 0;
$this->assertSame(0, $arr[$i]);
}
// Test set.
$arr [0]= MAX_INT64;
$this->assertSame(MAX_INT64, $arr[0]);
$arr [1]= MIN_INT64;
$this->assertEquals(MIN_INT64, $arr[1]);
$arr [2]= 1.1;
$this->assertSame(1, $arr[2]);
$arr [3]= '2';
$this->assertSame(2, $arr[3]);
$arr [4]= '3.1';
$this->assertSame(3, $arr[4]);
$arr [5]= MAX_INT64_STRING;
$this->assertSame(MAX_INT64, $arr[5]);
$arr [6]= MIN_INT64_STRING;
$this->assertEquals(MIN_INT64, $arr[6]);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt64AppendStringFail()
{
$arr = new RepeatedField(GPBType::INT64);
$arr []= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt64SetStringFail()
{
$arr = new RepeatedField(GPBType::INT64);
$arr []= 0;
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt64AppendMessageFail()
{
$arr = new RepeatedField(GPBType::INT64);
$arr []= new TestMessage_Sub();
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt64SetMessageFail()
{
$arr = new RepeatedField(GPBType::INT64);
$arr []= 0;
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test uint64 field.
#########################################################
public function testUint64()
{
$arr = new RepeatedField(GPBType::UINT64);
// Test append.
$arr []= MAX_UINT64;
$this->assertEquals(MAX_UINT64, $arr[0]);
$arr []= 1.1;
$this->assertSame(1, $arr[1]);
$arr []= '2';
$this->assertSame(2, $arr[2]);
$arr []= '3.1';
$this->assertSame(3, $arr[3]);
$arr []= MAX_UINT64_STRING;
$this->assertEquals(MAX_UINT64, $arr[4]);
$this->assertEquals(5, count($arr));
for ($i = 0; $i < count($arr); $i++) {
$arr[$i] = 0;
$this->assertSame(0, $arr[$i]);
}
// Test set.
$arr [0]= MAX_UINT64;
$this->assertEquals(MAX_UINT64, $arr[0]);
$arr [1]= 1.1;
$this->assertSame(1, $arr[1]);
$arr [2]= '2';
$this->assertSame(2, $arr[2]);
$arr [3]= '3.1';
$this->assertSame(3, $arr[3]);
$arr [4]= MAX_UINT64_STRING;
$this->assertEquals(MAX_UINT64, $arr[4]);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint64AppendStringFail()
{
$arr = new RepeatedField(GPBType::UINT64);
$arr []= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint64SetStringFail()
{
$arr = new RepeatedField(GPBType::UINT64);
$arr []= 0;
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint64AppendMessageFail()
{
$arr = new RepeatedField(GPBType::UINT64);
$arr []= new TestMessage_Sub();
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint64SetMessageFail()
{
$arr = new RepeatedField(GPBType::UINT64);
$arr []= 0;
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test float field.
#########################################################
public function testFloat()
{
$arr = new RepeatedField(GPBType::FLOAT);
// Test append.
$arr []= 1;
$this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
$arr []= 1.1;
$this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
$arr []= '2';
$this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
$arr []= '3.1';
$this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
$this->assertEquals(4, count($arr));
for ($i = 0; $i < count($arr); $i++) {
$arr[$i] = 0;
$this->assertSame(0.0, $arr[$i]);
}
// Test set.
$arr [0]= 1;
$this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
$arr [1]= 1.1;
$this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
$arr [2]= '2';
$this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
$arr [3]= '3.1';
$this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testFloatAppendStringFail()
{
$arr = new RepeatedField(GPBType::FLOAT);
$arr []= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testFloatSetStringFail()
{
$arr = new RepeatedField(GPBType::FLOAT);
$arr []= 0.0;
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testFloatAppendMessageFail()
{
$arr = new RepeatedField(GPBType::FLOAT);
$arr []= new TestMessage_Sub();
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testFloatSetMessageFail()
{
$arr = new RepeatedField(GPBType::FLOAT);
$arr []= 0.0;
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test double field.
#########################################################
public function testDouble()
{
$arr = new RepeatedField(GPBType::DOUBLE);
// Test append.
$arr []= 1;
$this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
$arr []= 1.1;
$this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
$arr []= '2';
$this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
$arr []= '3.1';
$this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
$this->assertEquals(4, count($arr));
for ($i = 0; $i < count($arr); $i++) {
$arr[$i] = 0;
$this->assertSame(0.0, $arr[$i]);
}
// Test set.
$arr [0]= 1;
$this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
$arr [1]= 1.1;
$this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
$arr [2]= '2';
$this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
$arr [3]= '3.1';
$this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testDoubleAppendStringFail()
{
$arr = new RepeatedField(GPBType::DOUBLE);
$arr []= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testDoubleSetStringFail()
{
$arr = new RepeatedField(GPBType::DOUBLE);
$arr []= 0.0;
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testDoubleAppendMessageFail()
{
$arr = new RepeatedField(GPBType::DOUBLE);
$arr []= new TestMessage_Sub();
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testDoubleSetMessageFail()
{
$arr = new RepeatedField(GPBType::DOUBLE);
$arr []= 0.0;
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test bool field.
#########################################################
public function testBool()
{
$arr = new RepeatedField(GPBType::BOOL);
// Test append.
$arr []= true;
$this->assertSame(true, $arr[0]);
$arr []= -1;
$this->assertSame(true, $arr[1]);
$arr []= 1.1;
$this->assertSame(true, $arr[2]);
$arr []= '';
$this->assertSame(false, $arr[3]);
$this->assertEquals(4, count($arr));
for ($i = 0; $i < count($arr); $i++) {
$arr[$i] = 0;
$this->assertSame(false, $arr[$i]);
}
// Test set.
$arr [0]= true;
$this->assertSame(true, $arr[0]);
$arr [1]= -1;
$this->assertSame(true, $arr[1]);
$arr [2]= 1.1;
$this->assertSame(true, $arr[2]);
$arr [3]= '';
$this->assertSame(false, $arr[3]);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testBoolAppendMessageFail()
{
$arr = new RepeatedField(GPBType::BOOL);
$arr []= new TestMessage_Sub();
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testBoolSetMessageFail()
{
$arr = new RepeatedField(GPBType::BOOL);
$arr []= true;
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test string field.
#########################################################
public function testString()
{
$arr = new RepeatedField(GPBType::STRING);
// Test append.
$arr []= 'abc';
$this->assertSame('abc', $arr[0]);
$arr []= 1;
$this->assertSame('1', $arr[1]);
$arr []= 1.1;
$this->assertSame('1.1', $arr[2]);
$arr []= true;
$this->assertSame('1', $arr[3]);
$this->assertEquals(4, count($arr));
for ($i = 0; $i < count($arr); $i++) {
$arr[$i] = '';
$this->assertSame('', $arr[$i]);
}
// Test set.
$arr [0]= 'abc';
$this->assertSame('abc', $arr[0]);
$arr [1]= 1;
$this->assertSame('1', $arr[1]);
$arr [2]= 1.1;
$this->assertSame('1.1', $arr[2]);
$arr [3]= true;
$this->assertSame('1', $arr[3]);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testStringAppendMessageFail()
{
$arr = new RepeatedField(GPBType::STRING);
$arr []= new TestMessage_Sub();
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testStringSetMessageFail()
{
$arr = new RepeatedField(GPBType::STRING);
$arr []= 'abc';
$arr [0]= new TestMessage_Sub();
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testStringAppendInvalidUTF8Fail()
{
$arr = new RepeatedField(GPBType::STRING);
$hex = hex2bin("ff");
$arr []= $hex;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testStringSetInvalidUTF8Fail()
{
$arr = new RepeatedField(GPBType::STRING);
$arr []= 'abc';
$hex = hex2bin("ff");
$arr [0]= $hex;
}
#########################################################
# Test message field.
#########################################################
public function testMessage()
{
$arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
// Test append.
$sub_m = new TestMessage_Sub();
$sub_m->setA(1);
$arr []= $sub_m;
$this->assertSame(1, $arr[0]->getA());
$null = null;
$arr []= $null;
$this->assertNull($arr[1]);
$this->assertEquals(2, count($arr));
for ($i = 0; $i < count($arr); $i++) {
$arr[$i] = $null;
$this->assertNull($arr[$i]);
}
// Test set.
$arr [0]= $sub_m;
$this->assertSame(1, $arr[0]->getA());
$arr [1]= $null;
$this->assertNull($arr[1]);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testMessageAppendIntFail()
{
$arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
$arr []= 1;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testMessageSetIntFail()
{
$arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
$arr []= new TestMessage_Sub;
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testMessageAppendStringFail()
{
$arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
$arr []= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testMessageSetStringFail()
{
$arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
$arr []= new TestMessage_Sub;
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testMessageAppendOtherMessageFail()
{
$arr = new RepeatedField(GPBType::MESSAGE, TestMessage_Sub::class);
$arr []= new TestMessage;
}
#########################################################
# Test offset type
#########################################################
public function testOffset()
{
$arr = new RepeatedField(GPBType::INT32);
$arr []= 0;
$arr [0]= 1;
$this->assertSame(1, $arr[0]);
$this->assertSame(1, count($arr));
$arr ['0']= 2;
$this->assertSame(2, $arr['0']);
$this->assertSame(2, $arr[0]);
$this->assertSame(1, count($arr));
$arr [0.0]= 3;
$this->assertSame(3, $arr[0.0]);
$this->assertSame(1, count($arr));
}
public function testInsertRemoval()
{
$arr = new RepeatedField(GPBType::INT32);
$arr []= 0;
$arr []= 1;
$arr []= 2;
$this->assertSame(3, count($arr));
unset($arr[2]);
$this->assertSame(2, count($arr));
$this->assertSame(0, $arr[0]);
$this->assertSame(1, $arr[1]);
$arr [] = 3;
$this->assertSame(3, count($arr));
$this->assertSame(0, $arr[0]);
$this->assertSame(1, $arr[1]);
$this->assertSame(3, $arr[2]);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testRemoveMiddleFail()
{
$arr = new RepeatedField(GPBType::INT32);
$arr []= 0;
$arr []= 1;
$arr []= 2;
$this->assertSame(3, count($arr));
unset($arr[1]);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testRemoveEmptyFail()
{
$arr = new RepeatedField(GPBType::INT32);
unset($arr[0]);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testMessageOffsetFail()
{
$arr = new RepeatedField(GPBType::INT32);
$arr []= 0;
$arr [new TestMessage_Sub()]= 0;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testStringOffsetFail()
{
$arr = new RepeatedField(GPBType::INT32);
$arr []= 0;
$arr ['abc']= 0;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testSetNonExistedOffsetFail()
{
$arr = new RepeatedField(GPBType::INT32);
$arr [0]= 0;
}
#########################################################
# Test memory leak
#########################################################
public function testCycleLeak()
{
$arr = new RepeatedField(GPBType::MESSAGE, TestMessage::class);
$arr []= new TestMessage;
$arr[0]->SetRepeatedRecursive($arr);
// Clean up memory before test.
gc_collect_cycles();
$start = memory_get_usage();
unset($arr);
// Explicitly trigger garbage collection.
gc_collect_cycles();
$end = memory_get_usage();
$this->assertLessThan($start, $end);
}
}

@ -1,4 +0,0 @@
<?php
require_once('test.pb.php');
require_once('test_util.php');

@ -0,0 +1,136 @@
<?php
require_once('test.pb.php');
require_once('test_base.php');
require_once('test_util.php');
use Google\Protobuf\RepeatedField;
use Google\Protobuf\GPBType;
use Foo\TestEnum;
use Foo\TestMessage;
use Foo\TestMessage_Sub;
use Foo\TestPackedMessage;
use Foo\TestUnpackedMessage;
class EncodeDecodeTest extends TestBase
{
public function testEncode()
{
$from = new TestMessage();
$this->expectEmptyFields($from);
$this->setFields($from);
$this->expectFields($from);
$data = $from->encode();
$this->assertSame(TestUtil::getGoldenTestMessage(), $data);
}
public function testDecode()
{
$to = new TestMessage();
$to->decode(TestUtil::getGoldenTestMessage());
$this->expectFields($to);
}
public function testEncodeDecode()
{
$from = new TestMessage();
$this->expectEmptyFields($from);
$this->setFields($from);
$this->expectFields($from);
$data = $from->encode();
$to = new TestMessage();
$to->decode($data);
$this->expectFields($to);
}
public function testEncodeDecodeEmpty()
{
$from = new TestMessage();
$this->expectEmptyFields($from);
$data = $from->encode();
$to = new TestMessage();
$to->decode($data);
$this->expectEmptyFields($to);
}
public function testEncodeDecodeOneof()
{
$m = new TestMessage();
$m->setOneofInt32(1);
$data = $m->encode();
$n = new TestMessage();
$n->decode($data);
$this->assertSame(1, $n->getOneofInt32());
$m->setOneofFloat(2.0);
$data = $m->encode();
$n = new TestMessage();
$n->decode($data);
$this->assertSame(2.0, $n->getOneofFloat());
$m->setOneofString('abc');
$data = $m->encode();
$n = new TestMessage();
$n->decode($data);
$this->assertSame('abc', $n->getOneofString());
$sub_m = new TestMessage_Sub();
$sub_m->setA(1);
$m->setOneofMessage($sub_m);
$data = $m->encode();
$n = new TestMessage();
$n->decode($data);
$this->assertSame(1, $n->getOneofMessage()->getA());
}
public function testPackedEncode()
{
$from = new TestPackedMessage();
TestUtil::setTestPackedMessage($from);
$this->assertSame(TestUtil::getGoldenTestPackedMessage(),
$from->encode());
}
public function testPackedDecodePacked()
{
$to = new TestPackedMessage();
$to->decode(TestUtil::getGoldenTestPackedMessage());
TestUtil::assertTestPackedMessage($to);
}
public function testPackedDecodeUnpacked()
{
$to = new TestPackedMessage();
$to->decode(TestUtil::getGoldenTestUnpackedMessage());
TestUtil::assertTestPackedMessage($to);
}
public function testUnpackedEncode()
{
$from = new TestUnpackedMessage();
TestUtil::setTestPackedMessage($from);
$this->assertSame(TestUtil::getGoldenTestUnpackedMessage(),
$from->encode());
}
public function testUnpackedDecodePacked()
{
$to = new TestUnpackedMessage();
$to->decode(TestUtil::getGoldenTestPackedMessage());
TestUtil::assertTestPackedMessage($to);
}
public function testUnpackedDecodeUnpacked()
{
$to = new TestUnpackedMessage();
$to->decode(TestUtil::getGoldenTestUnpackedMessage());
TestUtil::assertTestPackedMessage($to);
}
}

@ -0,0 +1,557 @@
<?php
require_once('test.pb.php');
require_once('test_util.php');
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBType;
use Foo\TestEnum;
use Foo\TestMessage;
use Foo\TestMessage_Sub;
class GeneratedClassTest extends PHPUnit_Framework_TestCase
{
#########################################################
# Test field accessors.
#########################################################
public function testSetterGetter()
{
$m = new TestMessage();
$m->setOptionalInt32(1);
$this->assertSame(1, $m->getOptionalInt32());
}
#########################################################
# Test int32 field.
#########################################################
public function testInt32Field()
{
$m = new TestMessage();
// Set integer.
$m->setOptionalInt32(MAX_INT32);
$this->assertSame(MAX_INT32, $m->getOptionalInt32());
$m->setOptionalInt32(MIN_INT32);
$this->assertSame(MIN_INT32, $m->getOptionalInt32());
// Set float.
$m->setOptionalInt32(1.1);
$this->assertSame(1, $m->getOptionalInt32());
$m->setOptionalInt32(MAX_INT32_FLOAT);
$this->assertSame(MAX_INT32, $m->getOptionalInt32());
$m->setOptionalInt32(MIN_INT32_FLOAT);
$this->assertSame(MIN_INT32, $m->getOptionalInt32());
// Set string.
$m->setOptionalInt32('2');
$this->assertSame(2, $m->getOptionalInt32());
$m->setOptionalInt32('3.1');
$this->assertSame(3, $m->getOptionalInt32());
$m->setOptionalInt32(MAX_INT32_STRING);
$this->assertSame(MAX_INT32, $m->getOptionalInt32());
$m->setOptionalInt32(MIN_INT32_STRING);
$this->assertSame(MIN_INT32, $m->getOptionalInt32());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt32FieldInvalidTypeFail()
{
$m = new TestMessage();
$m->setOptionalInt32(new TestMessage());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt32FieldInvalidStringFail()
{
$m = new TestMessage();
$m->setOptionalInt32('abc');
}
#########################################################
# Test uint32 field.
#########################################################
public function testUint32Field()
{
$m = new TestMessage();
// Set integer.
$m->setOptionalUint32(MAX_UINT32);
$this->assertSame(-1, $m->getOptionalUint32());
$m->setOptionalUint32(-1);
$this->assertSame(-1, $m->getOptionalUint32());
$m->setOptionalUint32(MIN_UINT32);
$this->assertSame(MIN_INT32, $m->getOptionalUint32());
// Set float.
$m->setOptionalUint32(1.1);
$this->assertSame(1, $m->getOptionalUint32());
$m->setOptionalUint32(MAX_UINT32_FLOAT);
$this->assertSame(-1, $m->getOptionalUint32());
$m->setOptionalUint32(-1.0);
$this->assertSame(-1, $m->getOptionalUint32());
$m->setOptionalUint32(MIN_UINT32_FLOAT);
$this->assertSame(MIN_INT32, $m->getOptionalUint32());
// Set string.
$m->setOptionalUint32('2');
$this->assertSame(2, $m->getOptionalUint32());
$m->setOptionalUint32('3.1');
$this->assertSame(3, $m->getOptionalUint32());
$m->setOptionalUint32(MAX_UINT32_STRING);
$this->assertSame(-1, $m->getOptionalUint32());
$m->setOptionalUint32('-1.0');
$this->assertSame(-1, $m->getOptionalUint32());
$m->setOptionalUint32(MIN_UINT32_STRING);
$this->assertSame(MIN_INT32, $m->getOptionalUint32());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint32FieldInvalidTypeFail()
{
$m = new TestMessage();
$m->setOptionalUint32(new TestMessage());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint32FieldInvalidStringFail()
{
$m = new TestMessage();
$m->setOptionalUint32('abc');
}
#########################################################
# Test int64 field.
#########################################################
public function testInt64Field()
{
$m = new TestMessage();
// Set integer.
$m->setOptionalInt64(MAX_INT64);
$this->assertSame(MAX_INT64, $m->getOptionalInt64());
$m->setOptionalInt64(MIN_INT64);
$this->assertEquals(MIN_INT64, $m->getOptionalInt64());
// Set float.
$m->setOptionalInt64(1.1);
$this->assertSame(1, $m->getOptionalInt64());
// Set string.
$m->setOptionalInt64('2');
$this->assertSame(2, $m->getOptionalInt64());
$m->setOptionalInt64('3.1');
$this->assertSame(3, $m->getOptionalInt64());
$m->setOptionalInt64(MAX_INT64_STRING);
$this->assertSame(MAX_INT64, $m->getOptionalInt64());
$m->setOptionalInt64(MIN_INT64_STRING);
$this->assertEquals(MIN_INT64, $m->getOptionalInt64());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt64FieldInvalidTypeFail()
{
$m = new TestMessage();
$m->setOptionalInt64(new TestMessage());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt64FieldInvalidStringFail()
{
$m = new TestMessage();
$m->setOptionalInt64('abc');
}
#########################################################
# Test uint64 field.
#########################################################
public function testUint64Field()
{
$m = new TestMessage();
// Set integer.
$m->setOptionalUint64(MAX_UINT64);
$this->assertEquals(MAX_UINT64, $m->getOptionalUint64());
// Set float.
$m->setOptionalUint64(1.1);
$this->assertSame(1, $m->getOptionalUint64());
// Set string.
$m->setOptionalUint64('2');
$this->assertSame(2, $m->getOptionalUint64());
$m->setOptionalUint64('3.1');
$this->assertSame(3, $m->getOptionalUint64());
$m->setOptionalUint64(MAX_UINT64_STRING);
$this->assertEquals(MAX_UINT64, $m->getOptionalUint64());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint64FieldInvalidTypeFail()
{
$m = new TestMessage();
$m->setOptionalUint64(new TestMessage());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint64FieldInvalidStringFail()
{
$m = new TestMessage();
$m->setOptionalUint64('abc');
}
#########################################################
# Test enum field.
#########################################################
public function testEnumField()
{
$m = new TestMessage();
// Set enum.
$m->setOptionalEnum(TestEnum::ONE);
$this->assertEquals(TestEnum::ONE, $m->getOptionalEnum());
// Set integer.
$m->setOptionalEnum(1);
$this->assertEquals(TestEnum::ONE, $m->getOptionalEnum());
// Set float.
$m->setOptionalEnum(1.1);
$this->assertEquals(TestEnum::ONE, $m->getOptionalEnum());
// Set string.
$m->setOptionalEnum("1");
$this->assertEquals(TestEnum::ONE, $m->getOptionalEnum());
}
#########################################################
# Test float field.
#########################################################
public function testFloatField()
{
$m = new TestMessage();
// Set integer.
$m->setOptionalFloat(1);
$this->assertEquals(1.0, $m->getOptionalFloat(), '', MAX_FLOAT_DIFF);
// Set float.
$m->setOptionalFloat(1.1);
$this->assertEquals(1.1, $m->getOptionalFloat(), '', MAX_FLOAT_DIFF);
// Set string.
$m->setOptionalFloat('2');
$this->assertEquals(2.0, $m->getOptionalFloat(), '', MAX_FLOAT_DIFF);
$m->setOptionalFloat('3.1');
$this->assertEquals(3.1, $m->getOptionalFloat(), '', MAX_FLOAT_DIFF);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testFloatFieldInvalidTypeFail()
{
$m = new TestMessage();
$m->setOptionalFloat(new TestMessage());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testFloatFieldInvalidStringFail()
{
$m = new TestMessage();
$m->setOptionalFloat('abc');
}
#########################################################
# Test double field.
#########################################################
public function testDoubleField()
{
$m = new TestMessage();
// Set integer.
$m->setOptionalDouble(1);
$this->assertEquals(1.0, $m->getOptionalDouble(), '', MAX_FLOAT_DIFF);
// Set float.
$m->setOptionalDouble(1.1);
$this->assertEquals(1.1, $m->getOptionalDouble(), '', MAX_FLOAT_DIFF);
// Set string.
$m->setOptionalDouble('2');
$this->assertEquals(2.0, $m->getOptionalDouble(), '', MAX_FLOAT_DIFF);
$m->setOptionalDouble('3.1');
$this->assertEquals(3.1, $m->getOptionalDouble(), '', MAX_FLOAT_DIFF);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testDoubleFieldInvalidTypeFail()
{
$m = new TestMessage();
$m->setOptionalDouble(new TestMessage());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testDoubleFieldInvalidStringFail()
{
$m = new TestMessage();
$m->setOptionalDouble('abc');
}
#########################################################
# Test bool field.
#########################################################
public function testBoolField()
{
$m = new TestMessage();
// Set bool.
$m->setOptionalBool(true);
$this->assertSame(true, $m->getOptionalBool());
// Set integer.
$m->setOptionalBool(-1);
$this->assertSame(true, $m->getOptionalBool());
// Set float.
$m->setOptionalBool(1.1);
$this->assertSame(true, $m->getOptionalBool());
// Set string.
$m->setOptionalBool('');
$this->assertSame(false, $m->getOptionalBool());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testBoolFieldInvalidStringFail()
{
$m = new TestMessage();
$m->setOptionalBool(new TestMessage());
}
#########################################################
# Test string field.
#########################################################
public function testStringField()
{
$m = new TestMessage();
// Set string.
$m->setOptionalString('abc');
$this->assertSame('abc', $m->getOptionalString());
// Set integer.
$m->setOptionalString(1);
$this->assertSame('1', $m->getOptionalString());
// Set double.
$m->setOptionalString(1.1);
$this->assertSame('1.1', $m->getOptionalString());
// Set bool.
$m->setOptionalString(true);
$this->assertSame('1', $m->getOptionalString());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testStringFieldInvalidUTF8Fail()
{
$m = new TestMessage();
$hex = hex2bin("ff");
$m->setOptionalString($hex);
}
#########################################################
# Test bytes field.
#########################################################
public function testBytesField()
{
$m = new TestMessage();
// Set string.
$m->setOptionalBytes('abc');
$this->assertSame('abc', $m->getOptionalBytes());
// Set integer.
$m->setOptionalBytes(1);
$this->assertSame('1', $m->getOptionalBytes());
// Set double.
$m->setOptionalBytes(1.1);
$this->assertSame('1.1', $m->getOptionalBytes());
// Set bool.
$m->setOptionalBytes(true);
$this->assertSame('1', $m->getOptionalBytes());
}
public function testBytesFieldInvalidUTF8Success()
{
$m = new TestMessage();
$hex = hex2bin("ff");
$m->setOptionalBytes($hex);
}
#########################################################
# Test message field.
#########################################################
public function testMessageField()
{
$m = new TestMessage();
$sub_m = new TestMessage_Sub();
$sub_m->setA(1);
$m->setOptionalMessage($sub_m);
$this->assertSame(1, $m->getOptionalMessage()->getA());
$null = null;
$m->setOptionalMessage($null);
$this->assertNull($m->getOptionalMessage());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testMessageFieldWrongTypeFail()
{
$m = new TestMessage();
$a = 1;
$m->setOptionalMessage($a);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testMessageFieldWrongClassFail()
{
$m = new TestMessage();
$m->setOptionalMessage(new TestMessage());
}
#########################################################
# Test repeated field.
#########################################################
public function testRepeatedField()
{
$m = new TestMessage();
$repeated_int32 = new RepeatedField(GPBType::INT32);
$m->setRepeatedInt32($repeated_int32);
$this->assertSame($repeated_int32, $m->getRepeatedInt32());
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testRepeatedFieldWrongTypeFail()
{
$m = new TestMessage();
$a = 1;
$m->setRepeatedInt32($a);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testRepeatedFieldWrongObjectFail()
{
$m = new TestMessage();
$m->setRepeatedInt32($m);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testRepeatedFieldWrongRepeatedTypeFail()
{
$m = new TestMessage();
$repeated_int32 = new RepeatedField(GPBType::UINT32);
$m->setRepeatedInt32($repeated_int32);
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testRepeatedFieldWrongRepeatedMessageClassFail()
{
$m = new TestMessage();
$repeated_message = new RepeatedField(GPBType::MESSAGE,
TestMessage::class);
$m->setRepeatedMessage($repeated_message);
}
#########################################################
# Test oneof field.
#########################################################
public function testOneofField() {
$m = new TestMessage();
$m->setOneofInt32(1);
$this->assertSame(1, $m->getOneofInt32());
$this->assertSame(0.0, $m->getOneofFloat());
$this->assertSame('', $m->getOneofString());
$this->assertSame(NULL, $m->getOneofMessage());
$m->setOneofFloat(2.0);
$this->assertSame(0, $m->getOneofInt32());
$this->assertSame(2.0, $m->getOneofFloat());
$this->assertSame('', $m->getOneofString());
$this->assertSame(NULL, $m->getOneofMessage());
$m->setOneofString('abc');
$this->assertSame(0, $m->getOneofInt32());
$this->assertSame(0.0, $m->getOneofFloat());
$this->assertSame('abc', $m->getOneofString());
$this->assertSame(NULL, $m->getOneofMessage());
$sub_m = new TestMessage_Sub();
$sub_m->setA(1);
$m->setOneofMessage($sub_m);
$this->assertSame(0, $m->getOneofInt32());
$this->assertSame(0.0, $m->getOneofFloat());
$this->assertSame('', $m->getOneofString());
$this->assertSame(1, $m->getOneofMessage()->getA());
}
}

@ -0,0 +1,648 @@
<?php
require_once('test.pb.php');
require_once('test_util.php');
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\MapField;
use Foo\TestMessage;
use Foo\TestMessage_Sub;
class MapFieldTest extends PHPUnit_Framework_TestCase {
#########################################################
# Test int32 field.
#########################################################
public function testInt32() {
$arr = new MapField(GPBType::INT32, GPBType::INT32);
// Test integer argument.
$arr[MAX_INT32] = MAX_INT32;
$this->assertSame(MAX_INT32, $arr[MAX_INT32]);
$arr[MIN_INT32] = MIN_INT32;
$this->assertSame(MIN_INT32, $arr[MIN_INT32]);
$this->assertEquals(2, count($arr));
$this->assertTrue(isset($arr[MAX_INT32]));
$this->assertTrue(isset($arr[MIN_INT32]));
unset($arr[MAX_INT32]);
unset($arr[MIN_INT32]);
$this->assertEquals(0, count($arr));
// Test float argument.
$arr[1.9] = 1.9;
$arr[2.1] = 2.1;
$this->assertSame(1, $arr[1]);
$this->assertSame(2, $arr[2]);
$arr[MAX_INT32_FLOAT] = MAX_INT32_FLOAT;
$this->assertSame(MAX_INT32, $arr[MAX_INT32]);
$arr[MIN_INT32_FLOAT] = MIN_INT32_FLOAT;
$this->assertSame(MIN_INT32, $arr[MIN_INT32]);
$this->assertEquals(4, count($arr));
unset($arr[1.9]);
unset($arr[2.9]);
unset($arr[MAX_INT32_FLOAT]);
unset($arr[MIN_INT32_FLOAT]);
$this->assertEquals(0, count($arr));
// Test string argument.
$arr['2'] = '2';
$this->assertSame(2, $arr[2]);
$arr['3.1'] = '3.1';
$this->assertSame(3, $arr[3]);
$arr[MAX_INT32_STRING] = MAX_INT32_STRING;
$this->assertSame(MAX_INT32, $arr[MAX_INT32]);
$this->assertEquals(3, count($arr));
unset($arr['2']);
unset($arr['3.1']);
unset($arr[MAX_INT32_STRING]);
$this->assertEquals(0, count($arr));
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt32SetStringKeyFail()
{
$arr = new MapField(GPBType::INT32, GPBType::INT32);
$arr ['abc']= 0;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt32SetStringValueFail()
{
$arr = new MapField(GPBType::INT32, GPBType::INT32);
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt32SetMessageKeyFail()
{
$arr = new MapField(GPBType::INT32, GPBType::INT32);
$arr [new TestMessage_Sub()]= 0;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt32SetMessageValueFail()
{
$arr = new MapField(GPBType::INT32, GPBType::INT32);
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test uint32 field.
#########################################################
public function testUint32() {
$arr = new MapField(GPBType::UINT32, GPBType::UINT32);
// Test integer argument.
$arr[MAX_UINT32] = MAX_UINT32;
$this->assertSame(-1, $arr[-1]);
$this->assertEquals(1, count($arr));
unset($arr[MAX_UINT32]);
$this->assertEquals(0, count($arr));
$arr[-1] = -1;
$this->assertSame(-1, $arr[-1]);
$arr[MIN_UINT32] = MIN_UINT32;
$this->assertSame(MIN_UINT32, $arr[MIN_UINT32]);
$this->assertEquals(2, count($arr));
unset($arr[-1]);
unset($arr[MIN_UINT32]);
$this->assertEquals(0, count($arr));
// Test float argument.
$arr[MAX_UINT32_FLOAT] = MAX_UINT32_FLOAT;
$this->assertSame(-1, $arr[-1]);
$this->assertEquals(1, count($arr));
unset($arr[MAX_UINT32_FLOAT]);
$this->assertEquals(0, count($arr));
$arr[3.1] = 3.1;
$this->assertSame(3, $arr[3]);
$arr[-1.0] = -1.0;
$this->assertSame(-1, $arr[-1]);
$arr[MIN_UINT32_FLOAT] = MIN_UINT32_FLOAT;
$this->assertSame(MIN_UINT32, $arr[MIN_UINT32]);
$this->assertEquals(3, count($arr));
unset($arr[3.1]);
unset($arr[-1.0]);
unset($arr[MIN_UINT32_FLOAT]);
$this->assertEquals(0, count($arr));
// Test string argument.
$arr[MAX_UINT32_STRING] = MAX_UINT32_STRING;
$this->assertSame(-1, $arr[-1]);
$this->assertEquals(1, count($arr));
unset($arr[MAX_UINT32_STRING]);
$this->assertEquals(0, count($arr));
$arr['7'] = '7';
$this->assertSame(7, $arr[7]);
$arr['3.1'] = '3.1';
$this->assertSame(3, $arr[3]);
$arr['-1.0'] = '-1.0';
$this->assertSame(-1, $arr[-1]);
$arr[MIN_UINT32_STRING] = MIN_UINT32_STRING;
$this->assertSame(MIN_UINT32, $arr[MIN_UINT32]);
$this->assertEquals(4, count($arr));
unset($arr['7']);
unset($arr['3.1']);
unset($arr['-1.0']);
unset($arr[MIN_UINT32_STRING]);
$this->assertEquals(0, count($arr));
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint32SetStringKeyFail()
{
$arr = new MapField(GPBType::UINT32, GPBType::UINT32);
$arr ['abc']= 0;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint32SetStringValueFail()
{
$arr = new MapField(GPBType::UINT32, GPBType::UINT32);
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint32SetMessageKeyFail()
{
$arr = new MapField(GPBType::UINT32, GPBType::UINT32);
$arr [new TestMessage_Sub()]= 0;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint32SetMessageValueFail()
{
$arr = new MapField(GPBType::UINT32, GPBType::UINT32);
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test int64 field.
#########################################################
public function testInt64() {
$arr = new MapField(GPBType::INT64, GPBType::INT64);
// Test integer argument.
$arr[MAX_INT64] = MAX_INT64;
$this->assertSame(MAX_INT64, $arr[MAX_INT64]);
$arr[MIN_INT64] = MIN_INT64;
$this->assertEquals(MIN_INT64, $arr[MIN_INT64]);
$this->assertEquals(2, count($arr));
unset($arr[MAX_INT64]);
unset($arr[MIN_INT64]);
$this->assertEquals(0, count($arr));
// Test float argument.
$arr[1.1] = 1.1;
$this->assertSame(1, $arr[1]);
$this->assertEquals(1, count($arr));
unset($arr[1.1]);
$this->assertEquals(0, count($arr));
// Test string argument.
$arr['2'] = '2';
$this->assertSame(2, $arr[2]);
$arr['3.1'] = '3.1';
$this->assertSame(3, $arr[3]);
$arr[MAX_INT64_STRING] = MAX_INT64_STRING;
$this->assertSame(MAX_INT64, $arr[MAX_INT64]);
$arr[MIN_INT64_STRING] = MIN_INT64_STRING;
$this->assertEquals(MIN_INT64, $arr[MIN_INT64]);
$this->assertEquals(4, count($arr));
unset($arr['2']);
unset($arr['3.1']);
unset($arr[MAX_INT64_STRING]);
unset($arr[MIN_INT64_STRING]);
$this->assertEquals(0, count($arr));
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt64SetStringKeyFail()
{
$arr = new MapField(GPBType::INT64, GPBType::INT64);
$arr ['abc']= 0;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt64SetStringValueFail()
{
$arr = new MapField(GPBType::INT64, GPBType::INT64);
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt64SetMessageKeyFail()
{
$arr = new MapField(GPBType::INT64, GPBType::INT64);
$arr [new TestMessage_Sub()]= 0;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testInt64SetMessageValueFail()
{
$arr = new MapField(GPBType::INT64, GPBType::INT64);
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test uint64 field.
#########################################################
public function testUint64() {
$arr = new MapField(GPBType::UINT64, GPBType::UINT64);
// Test integer argument.
$arr[MAX_UINT64] = MAX_UINT64;
$this->assertEquals(MAX_UINT64, $arr[MAX_UINT64]);
$this->assertEquals(1, count($arr));
unset($arr[MAX_UINT64]);
$this->assertEquals(0, count($arr));
// Test float argument.
$arr[1.1] = 1.1;
$this->assertSame(1, $arr[1]);
$this->assertEquals(1, count($arr));
unset($arr[1.1]);
$this->assertEquals(0, count($arr));
// Test string argument.
$arr['2'] = '2';
$this->assertSame(2, $arr[2]);
$arr['3.1'] = '3.1';
$this->assertSame(3, $arr[3]);
$arr[MAX_UINT64_STRING] = MAX_UINT64_STRING;
$this->assertEquals(MAX_UINT64, $arr[MAX_UINT64]);
$this->assertEquals(3, count($arr));
unset($arr['2']);
unset($arr['3.1']);
unset($arr[MAX_UINT64_STRING]);
$this->assertEquals(0, count($arr));
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint64SetStringKeyFail()
{
$arr = new MapField(GPBType::UINT64, GPBType::UINT64);
$arr ['abc']= 0;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint64SetStringValueFail()
{
$arr = new MapField(GPBType::UINT64, GPBType::UINT64);
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint64SetMessageKeyFail()
{
$arr = new MapField(GPBType::UINT64, GPBType::UINT64);
$arr [new TestMessage_Sub()]= 0;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testUint64SetMessageValueFail()
{
$arr = new MapField(GPBType::UINT64, GPBType::UINT64);
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test float field.
#########################################################
public function testFloat() {
$arr = new MapField(GPBType::INT32, GPBType::FLOAT);
// Test set.
$arr[0] = 1;
$this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
$arr[1] = 1.1;
$this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
$arr[2] = '2';
$this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
$arr[3] = '3.1';
$this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
$this->assertEquals(4, count($arr));
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testFloatSetStringValueFail()
{
$arr = new MapField(GPBType::INT64, GPBType::FLOAT);
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testFloatSetMessageValueFail()
{
$arr = new MapField(GPBType::INT64, GPBType::FLOAT);
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test double field.
#########################################################
public function testDouble() {
$arr = new MapField(GPBType::INT32, GPBType::DOUBLE);
// Test set.
$arr[0] = 1;
$this->assertEquals(1.0, $arr[0], '', MAX_FLOAT_DIFF);
$arr[1] = 1.1;
$this->assertEquals(1.1, $arr[1], '', MAX_FLOAT_DIFF);
$arr[2] = '2';
$this->assertEquals(2.0, $arr[2], '', MAX_FLOAT_DIFF);
$arr[3] = '3.1';
$this->assertEquals(3.1, $arr[3], '', MAX_FLOAT_DIFF);
$this->assertEquals(4, count($arr));
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testDoubleSetStringValueFail()
{
$arr = new MapField(GPBType::INT64, GPBType::DOUBLE);
$arr [0]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testDoubleSetMessageValueFail()
{
$arr = new MapField(GPBType::INT64, GPBType::DOUBLE);
$arr [0]= new TestMessage_Sub();
}
#########################################################
# Test bool field.
#########################################################
public function testBool() {
$arr = new MapField(GPBType::BOOL, GPBType::BOOL);
// Test boolean.
$arr[True] = True;
$this->assertSame(True, $arr[True]);
$this->assertEquals(1, count($arr));
unset($arr[True]);
$this->assertEquals(0, count($arr));
$arr[False] = False;
$this->assertSame(False, $arr[False]);
$this->assertEquals(1, count($arr));
unset($arr[False]);
$this->assertEquals(0, count($arr));
// Test integer.
$arr[-1] = -1;
$this->assertSame(True, $arr[True]);
$this->assertEquals(1, count($arr));
unset($arr[-1]);
$this->assertEquals(0, count($arr));
$arr[0] = 0;
$this->assertSame(False, $arr[False]);
$this->assertEquals(1, count($arr));
unset($arr[0]);
$this->assertEquals(0, count($arr));
// Test float.
$arr[1.1] = 1.1;
$this->assertSame(True, $arr[True]);
$this->assertEquals(1, count($arr));
unset($arr[1.1]);
$this->assertEquals(0, count($arr));
$arr[0.0] = 0.0;
$this->assertSame(False, $arr[False]);
$this->assertEquals(1, count($arr));
unset($arr[0.0]);
$this->assertEquals(0, count($arr));
// Test string.
$arr['a'] = 'a';
$this->assertSame(True, $arr[True]);
$this->assertEquals(1, count($arr));
unset($arr['a']);
$this->assertEquals(0, count($arr));
$arr[''] = '';
$this->assertSame(False, $arr[False]);
$this->assertEquals(1, count($arr));
unset($arr['']);
$this->assertEquals(0, count($arr));
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testBoolSetMessageKeyFail()
{
$arr = new MapField(GPBType::BOOL, GPBType::BOOL);
$arr [new TestMessage_Sub()]= true;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testBoolSetMessageValueFail()
{
$arr = new MapField(GPBType::BOOL, GPBType::BOOL);
$arr [true]= new TestMessage_Sub();
}
#########################################################
# Test string field.
#########################################################
public function testString() {
$arr = new MapField(GPBType::STRING, GPBType::STRING);
// Test set.
$arr['abc'] = 'abc';
$this->assertSame('abc', $arr['abc']);
$this->assertEquals(1, count($arr));
unset($arr['abc']);
$this->assertEquals(0, count($arr));
$arr[1] = 1;
$this->assertSame('1', $arr['1']);
$this->assertEquals(1, count($arr));
unset($arr[1]);
$this->assertEquals(0, count($arr));
$arr[1.1] = 1.1;
$this->assertSame('1.1', $arr['1.1']);
$this->assertEquals(1, count($arr));
unset($arr[1.1]);
$this->assertEquals(0, count($arr));
$arr[True] = True;
$this->assertSame('1', $arr['1']);
$this->assertEquals(1, count($arr));
unset($arr[True]);
$this->assertEquals(0, count($arr));
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testStringSetInvalidUTF8KeyFail()
{
$arr = new MapField(GPBType::STRING, GPBType::STRING);
$arr[hex2bin("ff")]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testStringSetInvalidUTF8ValueFail()
{
$arr = new MapField(GPBType::STRING, GPBType::STRING);
$arr ['abc']= hex2bin("ff");
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testStringSetMessageKeyFail()
{
$arr = new MapField(GPBType::STRING, GPBType::STRING);
$arr [new TestMessage_Sub()]= 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testStringSetMessageValueFail()
{
$arr = new MapField(GPBType::STRING, GPBType::STRING);
$arr ['abc']= new TestMessage_Sub();
}
#########################################################
# Test message field.
#########################################################
public function testMessage() {
$arr = new MapField(GPBType::INT32,
GPBType::MESSAGE, TestMessage_Sub::class);
// Test append.
$sub_m = new TestMessage_Sub();
$sub_m->setA(1);
$arr[0] = $sub_m;
$this->assertSame(1, $arr[0]->getA());
$null = NULL;
$arr[1] = $null;
$this->assertNull($arr[1]);
$this->assertEquals(2, count($arr));
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testMessageSetIntValueFail()
{
$arr =
new MapField(GPBType::INT32, GPBType::MESSAGE, TestMessage::class);
$arr[0] = 0;
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testMessageSetStringValueFail()
{
$arr =
new MapField(GPBType::INT32, GPBType::MESSAGE, TestMessage::class);
$arr[0] = 'abc';
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testMessageSetOtherMessageValueFail()
{
$arr =
new MapField(GPBType::INT32, GPBType::MESSAGE, TestMessage::class);
$arr[0] = new TestMessage_Sub();
}
#########################################################
# Test memory leak
#########################################################
// TODO(teboring): Add it back.
// public function testCycleLeak()
// {
// $arr = new MapField(GPBType::INT32,
// GPBType::MESSAGE, TestMessage::class);
// $arr [0]= new TestMessage;
// $arr[0]->SetMapRecursive($arr);
// // Clean up memory before test.
// gc_collect_cycles();
// $start = memory_get_usage();
// unset($arr);
// // Explicitly trigger garbage collection.
// gc_collect_cycles();
// $end = memory_get_usage();
// $this->assertLessThan($start, $end);
// }
}

@ -0,0 +1,73 @@
<?php
# phpunit has memory leak by itself. Thus, it cannot be used to test memory leak.
require_once('test.pb.php');
require_once('test_util.php');
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBType;
use Foo\TestMessage;
use Foo\TestMessage_Sub;
$from = new TestMessage();
TestUtil::setTestMessage($from);
TestUtil::assertTestMessage($from);
$data = $from->encode();
$to = new TestMessage();
$to->decode($data);
TestUtil::assertTestMessage($to);
$from->setRecursive($from);
$arr = new RepeatedField(GPBType::MESSAGE, TestMessage::class);
$arr []= new TestMessage;
$arr[0]->SetRepeatedRecursive($arr);
// Test oneof fields.
$m = new TestMessage();
$m->setOneofInt32(1);
assert(1 === $m->getOneofInt32());
assert(0.0 === $m->getOneofFloat());
assert('' === $m->getOneofString());
assert(NULL === $m->getOneofMessage());
$data = $m->encode();
$n = new TestMessage();
$n->decode($data);
assert(1 === $n->getOneofInt32());
$m->setOneofFloat(2.0);
assert(0 === $m->getOneofInt32());
assert(2.0 === $m->getOneofFloat());
assert('' === $m->getOneofString());
assert(NULL === $m->getOneofMessage());
$data = $m->encode();
$n = new TestMessage();
$n->decode($data);
assert(2.0 === $n->getOneofFloat());
$m->setOneofString('abc');
assert(0 === $m->getOneofInt32());
assert(0.0 === $m->getOneofFloat());
assert('abc' === $m->getOneofString());
assert(NULL === $m->getOneofMessage());
$data = $m->encode();
$n = new TestMessage();
$n->decode($data);
assert('abc' === $n->getOneofString());
$sub_m = new TestMessage_Sub();
$sub_m->setA(1);
$m->setOneofMessage($sub_m);
assert(0 === $m->getOneofInt32());
assert(0.0 === $m->getOneofFloat());
assert('' === $m->getOneofString());
assert(1 === $m->getOneofMessage()->getA());
$data = $m->encode();
$n = new TestMessage();
$n->decode($data);
assert(1 === $n->getOneofMessage()->getA());

@ -0,0 +1,443 @@
<?php
require_once('test.pb.php');
require_once('test_base.php');
require_once('test_util.php');
use Foo\TestMessage;
use Foo\TestMessage_Sub;
use Foo\TestPackedMessage;
use Google\Protobuf\Internal\InputStream;
use Google\Protobuf\Internal\FileDescriptorSet;
use Google\Protobuf\Internal\GPBUtil;
use Google\Protobuf\Internal\Int64;
use Google\Protobuf\Internal\Uint64;
use Google\Protobuf\Internal\GPBLabel;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\GPBWire;
use Google\Protobuf\Internal\OutputStream;
use Google\Protobuf\Internal\RepeatedField;
class ImplementationTest extends TestBase
{
public function testReadInt32()
{
$value = null;
// Positive number.
$input = new InputStream(hex2bin("01"));
GPBWire::readInt32($input, $value);
$this->assertSame(1, $value);
// Negative number.
$input = new InputStream(hex2bin("ffffffff0f"));
GPBWire::readInt32($input, $value);
$this->assertSame(-1, $value);
// Discard overflow bits.
$input = new InputStream(hex2bin("ffffffff7f"));
GPBWire::readInt32($input, $value);
$this->assertSame(-1, $value);
}
public function testReadUint32()
{
$value = null;
// Positive number.
$input = new InputStream(hex2bin("01"));
GPBWire::readUint32($input, $value);
$this->assertSame(1, $value);
// Max uint32.
$input = new InputStream(hex2bin("ffffffff0f"));
GPBWire::readUint32($input, $value);
$this->assertSame(-1, $value);
// Discard overflow bits.
$input = new InputStream(hex2bin("ffffffff7f"));
GPBWire::readUint32($input, $value);
$this->assertSame(-1, $value);
}
public function testReadInt64()
{
$value = null;
// Positive number.
$input = new InputStream(hex2bin("01"));
GPBWire::readInt64($input, $value);
$this->assertSame(1, $value->toInteger());
// Negative number.
$input = new InputStream(hex2bin("ffffffffffffffffff01"));
GPBWire::readInt64($input, $value);
$this->assertSame(-1, $value->toInteger());
// Discard overflow bits.
$input = new InputStream(hex2bin("ffffffffffffffffff0f"));
GPBWire::readInt64($input, $value);
$this->assertSame(-1, $value->toInteger());
}
public function testReadUint64()
{
$value = null;
// Positive number.
$input = new InputStream(hex2bin("01"));
GPBWire::readUint64($input, $value);
$this->assertSame(1, $value->toInteger());
// Negative number.
$input = new InputStream(hex2bin("FFFFFFFFFFFFFFFFFF01"));
GPBWire::readUint64($input, $value);
$this->assertSame(-1, $value->toInteger());
// Discard overflow bits.
$input = new InputStream(hex2bin("FFFFFFFFFFFFFFFFFF0F"));
GPBWire::readUint64($input, $value);
$this->assertSame(-1, $value->toInteger());
}
public function testReadSint32()
{
$value = null;
$input = new InputStream(hex2bin("00"));
GPBWire::readSint32($input, $value);
$this->assertSame(0, $value);
$input = new InputStream(hex2bin("01"));
GPBWire::readSint32($input, $value);
$this->assertSame(-1, $value);
$input = new InputStream(hex2bin("02"));
GPBWire::readSint32($input, $value);
$this->assertSame(1, $value);
}
public function testReadSint64()
{
$value = null;
$input = new InputStream(hex2bin("00"));
GPBWire::readSint64($input, $value);
$this->assertEquals(GPBUtil::Int64(0), $value);
$input = new InputStream(hex2bin("01"));
GPBWire::readSint64($input, $value);
$this->assertEquals(GPBUtil::Int64(-1), $value);
$input = new InputStream(hex2bin("02"));
GPBWire::readSint64($input, $value);
$this->assertEquals(GPBUtil::Int64(1), $value);
}
public function testReadFixed32()
{
$value = null;
$input = new InputStream(hex2bin("12345678"));
GPBWire::readFixed32($input, $value);
$this->assertSame(0x78563412, $value);
}
public function testReadFixed64()
{
$value = null;
$input = new InputStream(hex2bin("1234567812345678"));
GPBWire::readFixed64($input, $value);
$this->assertEquals(Uint64::newValue(0x78563412, 0x78563412), $value);
}
public function testReadSfixed32()
{
$value = null;
$input = new InputStream(hex2bin("12345678"));
GPBWire::readSfixed32($input, $value);
$this->assertSame(0x78563412, $value);
}
public function testReadFloat()
{
$value = null;
$input = new InputStream(hex2bin("0000803F"));
GPBWire::readFloat($input, $value);
$this->assertSame(1.0, $value);
}
public function testReadBool()
{
$value = null;
$input = new InputStream(hex2bin("00"));
GPBWire::readBool($input, $value);
$this->assertSame(false, $value);
$input = new InputStream(hex2bin("01"));
GPBWire::readBool($input, $value);
$this->assertSame(true, $value);
}
public function testReadDouble()
{
$value = null;
$input = new InputStream(hex2bin("000000000000F03F"));
GPBWire::readDouble($input, $value);
$this->assertSame(1.0, $value);
}
public function testReadSfixed64()
{
$value = null;
$input = new InputStream(hex2bin("1234567812345678"));
GPBWire::readSfixed64($input, $value);
$this->assertEquals(Int64::newValue(0x78563412, 0x78563412), $value);
}
public function testZigZagEncodeDecode()
{
$this->assertSame(0, GPBWire::zigZagEncode32(0));
$this->assertSame(1, GPBWire::zigZagEncode32(-1));
$this->assertSame(2, GPBWire::zigZagEncode32(1));
$this->assertSame(3, GPBWire::zigZagEncode32(-2));
$this->assertSame(0x7FFFFFFE, GPBWire::zigZagEncode32(0x3FFFFFFF));
$this->assertSame(0x7FFFFFFF, GPBWire::zigZagEncode32(0xC0000000));
$this->assertSame(-2, GPBWire::zigZagEncode32(0x7FFFFFFF));
$this->assertSame(-1, GPBWire::zigZagEncode32(0x80000000));
$this->assertSame(0, GPBWire::zigZagDecode32(0));
$this->assertSame(-1, GPBWire::zigZagDecode32(1));
$this->assertSame(1, GPBWire::zigZagDecode32(2));
$this->assertSame(-2, GPBWire::zigZagDecode32(3));
$this->assertSame(0x3FFFFFFF, GPBWire::zigZagDecode32(0x7FFFFFFE));
$this->assertSame(-1073741824, GPBWire::zigZagDecode32(0x7FFFFFFF));
$this->assertSame(0x7FFFFFFF, GPBWire::zigZagDecode32(0xFFFFFFFE));
$this->assertSame(-2147483648, GPBWire::zigZagDecode32(0xFFFFFFFF));
$this->assertEquals(GPBUtil::Uint64(0),
GPBWire::zigZagEncode64(GPBUtil::Int64(0)));
$this->assertEquals(GPBUtil::Uint64(1),
GPBWire::zigZagEncode64(GPBUtil::Int64(-1)));
$this->assertEquals(GPBUtil::Uint64(2),
GPBWire::zigZagEncode64(GPBUtil::Int64(1)));
$this->assertEquals(GPBUtil::Uint64(3),
GPBWire::zigZagEncode64(GPBUtil::Int64(-2)));
$this->assertEquals(
GPBUtil::Uint64(0x000000007FFFFFFE),
GPBWire::zigZagEncode64(GPBUtil::Int64(0x000000003FFFFFFF)));
$this->assertEquals(
GPBUtil::Uint64(0x000000007FFFFFFF),
GPBWire::zigZagEncode64(GPBUtil::Int64(0xFFFFFFFFC0000000)));
$this->assertEquals(
GPBUtil::Uint64(0x00000000FFFFFFFE),
GPBWire::zigZagEncode64(GPBUtil::Int64(0x000000007FFFFFFF)));
$this->assertEquals(
GPBUtil::Uint64(0x00000000FFFFFFFF),
GPBWire::zigZagEncode64(GPBUtil::Int64(0xFFFFFFFF80000000)));
$this->assertEquals(
Uint64::newValue(4294967295, 4294967294),
GPBWire::zigZagEncode64(GPBUtil::Int64(0x7FFFFFFFFFFFFFFF)));
$this->assertEquals(
Uint64::newValue(4294967295, 4294967295),
GPBWire::zigZagEncode64(GPBUtil::Int64(0x8000000000000000)));
$this->assertEquals(GPBUtil::Int64(0),
GPBWire::zigZagDecode64(GPBUtil::Uint64(0)));
$this->assertEquals(GPBUtil::Int64(-1),
GPBWire::zigZagDecode64(GPBUtil::Uint64(1)));
$this->assertEquals(GPBUtil::Int64(1),
GPBWire::zigZagDecode64(GPBUtil::Uint64(2)));
$this->assertEquals(GPBUtil::Int64(-2),
GPBWire::zigZagDecode64(GPBUtil::Uint64(3)));
// Round trip
$this->assertSame(0, GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(0)));
$this->assertSame(1, GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(1)));
$this->assertSame(-1, GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(-1)));
$this->assertSame(14927,
GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(14927)));
$this->assertSame(-3612,
GPBWire::zigZagDecode32(GPBWire::zigZagEncode32(-3612)));
}
public function testDecode()
{
$m = new TestMessage();
$m->decode(TestUtil::getGoldenTestMessage());
TestUtil::assertTestMessage($m);
}
public function testDescriptorDecode()
{
$file_desc_set = new FileDescriptorSet();
$file_desc_set->decode(hex2bin(
"0a3b0a12746573745f696e636c7564652e70726f746f120362617222180a" .
"0b54657374496e636c75646512090a0161180120012805620670726f746f33"));
$this->assertSame(1, sizeof($file_desc_set->getFile()));
$file_desc = $file_desc_set->getFile()[0];
$this->assertSame("test_include.proto", $file_desc->getName());
$this->assertSame("bar", $file_desc->getPackage());
$this->assertSame(0, sizeof($file_desc->getDependency()));
$this->assertSame(1, sizeof($file_desc->getMessageType()));
$this->assertSame(0, sizeof($file_desc->getEnumType()));
$this->assertSame("proto3", $file_desc->getSyntax());
$desc = $file_desc->getMessageType()[0];
$this->assertSame("TestInclude", $desc->getName());
$this->assertSame(1, sizeof($desc->getField()));
$this->assertSame(0, sizeof($desc->getNestedType()));
$this->assertSame(0, sizeof($desc->getEnumType()));
$this->assertSame(0, sizeof($desc->getOneofDecl()));
$field = $desc->getField()[0];
$this->assertSame("a", $field->getName());
$this->assertSame(1, $field->getNumber());
$this->assertSame(GPBLabel::OPTIONAL, $field->getLabel());
$this->assertSame(GPBType::INT32, $field->getType());
}
public function testReadVarint64()
{
$var = 0;
// Empty buffer.
$input = new InputStream(hex2bin(''));
$this->assertFalse($input->readVarint64($var));
// The largest varint is 10 bytes long.
$input = new InputStream(hex2bin('8080808080808080808001'));
$this->assertFalse($input->readVarint64($var));
// Corrupted varint.
$input = new InputStream(hex2bin('808080'));
$this->assertFalse($input->readVarint64($var));
// Normal case.
$input = new InputStream(hex2bin('808001'));
$this->assertTrue($input->readVarint64($var));
$this->assertSame(16384, $var->toInteger());
$this->assertFalse($input->readVarint64($var));
// Read two varint.
$input = new InputStream(hex2bin('808001808002'));
$this->assertTrue($input->readVarint64($var));
$this->assertSame(16384, $var->toInteger());
$this->assertTrue($input->readVarint64($var));
$this->assertSame(32768, $var->toInteger());
$this->assertFalse($input->readVarint64($var));
}
public function testReadVarint32()
{
$var = 0;
// Empty buffer.
$input = new InputStream(hex2bin(''));
$this->assertFalse($input->readVarint32($var));
// The largest varint is 10 bytes long.
$input = new InputStream(hex2bin('8080808080808080808001'));
$this->assertFalse($input->readVarint32($var));
// Corrupted varint.
$input = new InputStream(hex2bin('808080'));
$this->assertFalse($input->readVarint32($var));
// Normal case.
$input = new InputStream(hex2bin('808001'));
$this->assertTrue($input->readVarint32($var));
$this->assertSame(16384, $var);
$this->assertFalse($input->readVarint32($var));
// Read two varint.
$input = new InputStream(hex2bin('808001808002'));
$this->assertTrue($input->readVarint32($var));
$this->assertSame(16384, $var);
$this->assertTrue($input->readVarint32($var));
$this->assertSame(32768, $var);
$this->assertFalse($input->readVarint32($var));
// Read a 64-bit integer. High-order bits should be discarded.
$input = new InputStream(hex2bin('808081808001'));
$this->assertTrue($input->readVarint32($var));
$this->assertSame(16384, $var);
$this->assertFalse($input->readVarint32($var));
}
public function testReadTag()
{
$input = new InputStream(hex2bin('808001'));
$tag = $input->readTag();
$this->assertSame(16384, $tag);
$tag = $input->readTag();
$this->assertSame(0, $tag);
}
public function testPushPopLimit()
{
$input = new InputStream(hex2bin('808001'));
$old_limit = $input->pushLimit(0);
$tag = $input->readTag();
$this->assertSame(0, $tag);
$input->popLimit($old_limit);
$tag = $input->readTag();
$this->assertSame(16384, $tag);
}
public function testReadRaw()
{
$input = new InputStream(hex2bin('808001'));
$buffer = null;
$this->assertTrue($input->readRaw(3, $buffer));
$this->assertSame(hex2bin('808001'), $buffer);
$this->assertFalse($input->readRaw(1, $buffer));
}
public function testWriteVarint32()
{
$output = new OutputStream(3);
$output->writeVarint32(16384);
$this->assertSame(hex2bin('808001'), $output->getData());
}
public function testWriteVarint64()
{
$output = new OutputStream(10);
$output->writeVarint64(-43);
$this->assertSame(hex2bin('D5FFFFFFFFFFFFFFFF01'), $output->getData());
}
public function testWriteLittleEndian32()
{
$output = new OutputStream(4);
$output->writeLittleEndian32(46);
$this->assertSame(hex2bin('2E000000'), $output->getData());
}
public function testWriteLittleEndian64()
{
$output = new OutputStream(8);
$output->writeLittleEndian64(47);
$this->assertSame(hex2bin('2F00000000000000'), $output->getData());
}
public function testByteSize()
{
$m = new TestMessage();
TestUtil::setTestMessage($m);
$this->assertSame(447, $m->byteSize());
}
public function testPackedByteSize()
{
$m = new TestPackedMessage();
TestUtil::setTestPackedMessage($m);
$this->assertSame(156, $m->byteSize());
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,136 @@
syntax = "proto3";
import 'test_include.proto';
package foo;
message TestMessage {
// Singular
int32 optional_int32 = 1;
int64 optional_int64 = 2;
uint32 optional_uint32 = 3;
uint64 optional_uint64 = 4;
sint32 optional_sint32 = 5;
sint64 optional_sint64 = 6;
fixed32 optional_fixed32 = 7;
fixed64 optional_fixed64 = 8;
sfixed32 optional_sfixed32 = 9;
sfixed64 optional_sfixed64 = 10;
float optional_float = 11;
double optional_double = 12;
bool optional_bool = 13;
string optional_string = 14;
bytes optional_bytes = 15;
TestEnum optional_enum = 16;
Sub optional_message = 17;
bar.TestInclude optional_included_message = 18;
TestMessage recursive = 19;
// Repeated
repeated int32 repeated_int32 = 31;
repeated int64 repeated_int64 = 32;
repeated uint32 repeated_uint32 = 33;
repeated uint64 repeated_uint64 = 34;
repeated sint32 repeated_sint32 = 35;
repeated sint64 repeated_sint64 = 36;
repeated fixed32 repeated_fixed32 = 37;
repeated fixed64 repeated_fixed64 = 38;
repeated sfixed32 repeated_sfixed32 = 39;
repeated sfixed64 repeated_sfixed64 = 40;
repeated float repeated_float = 41;
repeated double repeated_double = 42;
repeated bool repeated_bool = 43;
repeated string repeated_string = 44;
repeated bytes repeated_bytes = 45;
repeated TestEnum repeated_enum = 46;
repeated Sub repeated_message = 47;
repeated TestMessage repeated_recursive = 48;
oneof my_oneof {
int32 oneof_int32 = 51;
int64 oneof_int64 = 52;
uint32 oneof_uint32 = 53;
uint64 oneof_uint64 = 54;
uint32 oneof_sint32 = 55;
uint64 oneof_sint64 = 56;
uint32 oneof_fixed32 = 57;
uint64 oneof_fixed64 = 58;
uint32 oneof_sfixed32 = 59;
uint64 oneof_sfixed64 = 60;
double oneof_double = 61;
float oneof_float = 62;
bool oneof_bool = 63;
string oneof_string = 64;
bytes oneof_bytes = 65;
TestEnum oneof_enum = 66;
Sub oneof_message = 67;
}
map<int32, int32> map_int32_int32 = 71;
map<int64, int64> map_int64_int64 = 72;
map<uint32, uint32> map_uint32_uint32 = 73;
map<uint64, uint64> map_uint64_uint64 = 74;
map<sint32, sint32> map_sint32_sint32 = 75;
map<sint64, sint64> map_sint64_sint64 = 76;
map<fixed32, fixed32> map_fixed32_fixed32 = 77;
map<fixed64, fixed64> map_fixed64_fixed64 = 78;
map<sfixed32, sfixed32> map_sfixed32_sfixed32 = 79;
map<sfixed64, sfixed64> map_sfixed64_sfixed64 = 80;
map<int32, float> map_int32_float = 81;
map<int32, double> map_int32_double = 82;
map<bool, bool> map_bool_bool = 83;
map<string, string> map_string_string = 84;
map<int32, bytes> map_int32_bytes = 85;
map<int32, TestEnum> map_int32_enum = 86;
map<int32, Sub> map_int32_message = 87;
map<int32, TestMessage> map_recursive = 88;
message Sub {
int32 a = 1;
}
// NestedMessage nested_message = 90;
}
enum TestEnum {
ZERO = 0;
ONE = 1;
}
message TestPackedMessage {
repeated int32 repeated_int32 = 90 [packed = true];
repeated int64 repeated_int64 = 91 [packed = true];
repeated uint32 repeated_uint32 = 92 [packed = true];
repeated uint64 repeated_uint64 = 93 [packed = true];
repeated sint32 repeated_sint32 = 94 [packed = true];
repeated sint64 repeated_sint64 = 95 [packed = true];
repeated fixed32 repeated_fixed32 = 96 [packed = true];
repeated fixed64 repeated_fixed64 = 97 [packed = true];
repeated sfixed32 repeated_sfixed32 = 98 [packed = true];
repeated sfixed64 repeated_sfixed64 = 99 [packed = true];
repeated float repeated_float = 100 [packed = true];
repeated double repeated_double = 101 [packed = true];
repeated bool repeated_bool = 102 [packed = true];
repeated TestEnum repeated_enum = 103 [packed = true];
}
// Need to be in sync with TestPackedMessage.
message TestUnpackedMessage {
repeated int32 repeated_int32 = 90 [packed = false];
repeated int64 repeated_int64 = 91 [packed = false];
repeated uint32 repeated_uint32 = 92 [packed = false];
repeated uint64 repeated_uint64 = 93 [packed = false];
repeated sint32 repeated_sint32 = 94 [packed = false];
repeated sint64 repeated_sint64 = 95 [packed = false];
repeated fixed32 repeated_fixed32 = 96 [packed = false];
repeated fixed64 repeated_fixed64 = 97 [packed = false];
repeated sfixed32 repeated_sfixed32 = 98 [packed = false];
repeated sfixed64 repeated_sfixed64 = 99 [packed = false];
repeated float repeated_float = 100 [packed = false];
repeated double repeated_double = 101 [packed = false];
repeated bool repeated_bool = 102 [packed = false];
repeated TestEnum repeated_enum = 103 [packed = false];
}

@ -0,0 +1,23 @@
#!/bin/bash
cd ../ext/google/protobuf/
make clean
set -e
phpize && ./configure --enable-debug CFLAGS='-g -O0' && make
cd -
tests=( array_test.php encode_decode_test.php generated_class_test.php map_field_test.php )
for t in "${tests[@]}"
do
echo "****************************"
echo "* $t"
echo "****************************"
php -dextension=../ext/google/protobuf/modules/protobuf.so `which phpunit` $t
echo ""
done
# Make sure to run the memory test in debug mode.
php -dextension=../ext/google/protobuf/modules/protobuf.so memory_leak_test.php
USE_ZEND_ALLOC=0 valgrind --leak-check=yes php -dextension=../ext/google/protobuf/modules/protobuf.so memory_leak_test.php

@ -0,0 +1,92 @@
<?php
use Foo\TestMessage;
use Foo\TestMessage_Sub;
class TestBase extends PHPUnit_Framework_TestCase
{
public function setFields(TestMessage $m)
{
TestUtil::setTestMessage($m);
}
public function expectFields(TestMessage $m)
{
$this->assertSame(-42, $m->getOptionalInt32());
$this->assertSame(42, $m->getOptionalUint32());
$this->assertSame(-43, $m->getOptionalInt64());
$this->assertSame(43, $m->getOptionalUint64());
$this->assertSame(-44, $m->getOptionalSint32());
$this->assertSame(-45, $m->getOptionalSint64());
$this->assertSame(46, $m->getOptionalFixed32());
$this->assertSame(47, $m->getOptionalFixed64());
$this->assertSame(-46, $m->getOptionalSfixed32());
$this->assertSame(-47, $m->getOptionalSfixed64());
$this->assertSame(1.5, $m->getOptionalFloat());
$this->assertSame(1.6, $m->getOptionalDouble());
$this->assertSame(true, $m->getOptionalBool());
$this->assertSame('a', $m->getOptionalString());
$this->assertSame('b', $m->getOptionalBytes());
$this->assertSame(33, $m->getOptionalMessage()->getA());
$this->assertEquals(-42, $m->getRepeatedInt32()[0]);
$this->assertEquals(42, $m->getRepeatedUint32()[0]);
$this->assertEquals(-43, $m->getRepeatedInt64()[0]);
$this->assertEquals(43, $m->getRepeatedUint64()[0]);
$this->assertEquals(-44, $m->getRepeatedSint32()[0]);
$this->assertEquals(-45, $m->getRepeatedSint64()[0]);
$this->assertEquals(46, $m->getRepeatedFixed32()[0]);
$this->assertEquals(47, $m->getRepeatedFixed64()[0]);
$this->assertEquals(-46, $m->getRepeatedSfixed32()[0]);
$this->assertEquals(-47, $m->getRepeatedSfixed64()[0]);
$this->assertEquals(1.5, $m->getRepeatedFloat()[0]);
$this->assertEquals(1.6, $m->getRepeatedDouble()[0]);
$this->assertEquals(true, $m->getRepeatedBool()[0]);
$this->assertEquals('a', $m->getRepeatedString()[0]);
$this->assertEquals('b', $m->getRepeatedBytes()[0]);
$this->assertEquals(34, $m->getRepeatedMessage()[0]->GetA());
$this->assertEquals(-52, $m->getRepeatedInt32()[1]);
$this->assertEquals(52, $m->getRepeatedUint32()[1]);
$this->assertEquals(-53, $m->getRepeatedInt64()[1]);
$this->assertEquals(53, $m->getRepeatedUint64()[1]);
$this->assertEquals(-54, $m->getRepeatedSint32()[1]);
$this->assertEquals(-55, $m->getRepeatedSint64()[1]);
$this->assertEquals(56, $m->getRepeatedFixed32()[1]);
$this->assertEquals(57, $m->getRepeatedFixed64()[1]);
$this->assertEquals(-56, $m->getRepeatedSfixed32()[1]);
$this->assertEquals(-57, $m->getRepeatedSfixed64()[1]);
$this->assertEquals(2.5, $m->getRepeatedFloat()[1]);
$this->assertEquals(2.6, $m->getRepeatedDouble()[1]);
$this->assertEquals(false, $m->getRepeatedBool()[1]);
$this->assertEquals('c', $m->getRepeatedString()[1]);
$this->assertEquals('d', $m->getRepeatedBytes()[1]);
$this->assertEquals(35, $m->getRepeatedMessage()[1]->GetA());
}
public function expectEmptyFields(TestMessage $m)
{
$this->assertSame(0, $m->getOptionalInt32());
$this->assertSame(0, $m->getOptionalUint32());
$this->assertSame(0, $m->getOptionalInt64());
$this->assertSame(0, $m->getOptionalUint64());
$this->assertSame(0, $m->getOptionalSint32());
$this->assertSame(0, $m->getOptionalSint64());
$this->assertSame(0, $m->getOptionalFixed32());
$this->assertSame(0, $m->getOptionalFixed64());
$this->assertSame(0, $m->getOptionalSfixed32());
$this->assertSame(0, $m->getOptionalSfixed64());
$this->assertSame(0.0, $m->getOptionalFloat());
$this->assertSame(0.0, $m->getOptionalDouble());
$this->assertSame(false, $m->getOptionalBool());
$this->assertSame('', $m->getOptionalString());
$this->assertSame('', $m->getOptionalBytes());
$this->assertNull($m->getOptionalMessage());
}
// This test is to avoid the warning of no test by php unit.
public function testNone()
{
}
}

@ -0,0 +1,36 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: test_include.proto
namespace Bar;
use Google\Protobuf\Internal\DescriptorPool;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBUtil;
class TestInclude extends \Google\Protobuf\Internal\Message
{
private $a = 0;
public function getA()
{
return $this->a;
}
public function setA($var)
{
GPBUtil::checkInt32($var);
$this->a = $var;
}
}
$pool = DescriptorPool::getGeneratedPool();
$pool->internalAddGeneratedFile(hex2bin(
"0a3b0a12746573745f696e636c7564652e70726f746f120362617222180a" .
"0b54657374496e636c75646512090a0161180120012805620670726f746f" .
"33"
));

@ -0,0 +1,393 @@
<?php
use Foo\TestEnum;
use Foo\TestMessage;
use Foo\TestMessage_Sub;
use Foo\TestPackedMessage;
use Foo\TestUnpackedMessage;
define('MAX_FLOAT_DIFF', 0.000001);
if (PHP_INT_SIZE == 8) {
define('MAX_INT_STRING', '9223372036854775807');
define('MAX_INT_UPPER_STRING', '9223372036854775808');
} else {
define('MAX_INT_STRING', '2147483647');
define('MAX_INT_UPPER_STRING', '2147483648');
}
define('MAX_INT32', 2147483647);
define('MAX_INT32_FLOAT', 2147483647.0);
define('MAX_INT32_STRING', '2147483647');
define('MIN_INT32', -2147483648);
define('MIN_INT32_FLOAT', -2147483648.0);
define('MIN_INT32_STRING', '-2147483648');
define('MAX_UINT32', 4294967295);
define('MAX_UINT32_FLOAT', 4294967295.0);
define('MAX_UINT32_STRING', '4294967295');
define('MIN_UINT32', -2147483648);
define('MIN_UINT32_FLOAT', -2147483648.0);
define('MIN_UINT32_STRING', '-2147483648');
define('MAX_INT64', 9223372036854775807);
define('MAX_INT64_STRING', '9223372036854775807');
define('MIN_INT64_STRING', '-9223372036854775808');
if (PHP_INT_SIZE === 8) {
define('MIN_INT64', -9223372036854775808);
} else {
define('MIN_INT64', MIN_INT64_STRING);
}
define('MAX_UINT64_STRING', '-9223372036854775808');
define('MAX_UINT64', MAX_UINT64_STRING);
class TestUtil
{
public static function setTestMessage(TestMessage $m)
{
$m->setOptionalInt32(-42);
$m->setOptionalInt64(-43);
$m->setOptionalUint32(42);
$m->setOptionalUint64(43);
$m->setOptionalSint32(-44);
$m->setOptionalSint64(-45);
$m->setOptionalFixed32(46);
$m->setOptionalFixed64(47);
$m->setOptionalSfixed32(-46);
$m->setOptionalSfixed64(-47);
$m->setOptionalFloat(1.5);
$m->setOptionalDouble(1.6);
$m->setOptionalBool(true);
$m->setOptionalString('a');
$m->setOptionalBytes('b');
$m->setOptionalEnum(TestEnum::ONE);
$m->setOptionalMessage(new TestMessage_Sub());
$m->getOptionalMessage()->SetA(33);
$m->getRepeatedInt32() []= -42;
$m->getRepeatedInt64() []= -43;
$m->getRepeatedUint32() []= 42;
$m->getRepeatedUint64() []= 43;
$m->getRepeatedSint32() []= -44;
$m->getRepeatedSint64() []= -45;
$m->getRepeatedFixed32() []= 46;
$m->getRepeatedFixed64() []= 47;
$m->getRepeatedSfixed32() []= -46;
$m->getRepeatedSfixed64() []= -47;
$m->getRepeatedFloat() []= 1.5;
$m->getRepeatedDouble() []= 1.6;
$m->getRepeatedBool() []= true;
$m->getRepeatedString() []= 'a';
$m->getRepeatedBytes() []= 'b';
$m->getRepeatedEnum() []= TestEnum::ZERO;
$m->getRepeatedMessage() []= new TestMessage_Sub();
$m->getRepeatedMessage()[0]->setA(34);
$m->getRepeatedInt32() []= -52;
$m->getRepeatedInt64() []= -53;
$m->getRepeatedUint32() []= 52;
$m->getRepeatedUint64() []= 53;
$m->getRepeatedSint32() []= -54;
$m->getRepeatedSint64() []= -55;
$m->getRepeatedFixed32() []= 56;
$m->getRepeatedFixed64() []= 57;
$m->getRepeatedSfixed32() []= -56;
$m->getRepeatedSfixed64() []= -57;
$m->getRepeatedFloat() []= 2.5;
$m->getRepeatedDouble() []= 2.6;
$m->getRepeatedBool() []= false;
$m->getRepeatedString() []= 'c';
$m->getRepeatedBytes() []= 'd';
$m->getRepeatedEnum() []= TestEnum::ONE;
$m->getRepeatedMessage() []= new TestMessage_Sub();
$m->getRepeatedMessage()[1]->SetA(35);
$m->getMapInt32Int32()[-62] = -62;
$m->getMapInt64Int64()[-63] = -63;
$m->getMapUint32Uint32()[62] = 62;
$m->getMapUint64Uint64()[63] = 63;
$m->getMapSint32Sint32()[-64] = -64;
$m->getMapSint64Sint64()[-65] = -65;
$m->getMapFixed32Fixed32()[66] = 66;
$m->getMapFixed64Fixed64()[67] = 67;
$m->getMapInt32Float()[1] = 3.5;
$m->getMapInt32Double()[1] = 3.6;
$m->getMapBoolBool()[true] = true;
$m->getMapStringString()['e'] = 'e';
$m->getMapInt32Bytes()[1] = 'f';
$m->getMapInt32Enum()[1] = TestEnum::ONE;
$m->getMapInt32Message()[1] = new TestMessage_Sub();
$m->getMapInt32Message()[1]->SetA(36);
}
public static function assertTestMessage(TestMessage $m)
{
assert(-42 === $m->getOptionalInt32());
assert(42 === $m->getOptionalUint32());
assert(-43 === $m->getOptionalInt64());
assert(43 === $m->getOptionalUint64());
assert(-44 === $m->getOptionalSint32());
assert(-45 === $m->getOptionalSint64());
assert(46 === $m->getOptionalFixed32());
assert(47 === $m->getOptionalFixed64());
assert(-46 === $m->getOptionalSfixed32());
assert(-47 === $m->getOptionalSfixed64());
assert(1.5 === $m->getOptionalFloat());
assert(1.6 === $m->getOptionalDouble());
assert(true=== $m->getOptionalBool());
assert('a' === $m->getOptionalString());
assert('b' === $m->getOptionalBytes());
assert(TestEnum::ONE === $m->getOptionalEnum());
assert(33 === $m->getOptionalMessage()->getA());
assert(-42 === $m->getRepeatedInt32()[0]);
assert(42 === $m->getRepeatedUint32()[0]);
assert(-43 === $m->getRepeatedInt64()[0]);
assert(43 === $m->getRepeatedUint64()[0]);
assert(-44 === $m->getRepeatedSint32()[0]);
assert(-45 === $m->getRepeatedSint64()[0]);
assert(46 === $m->getRepeatedFixed32()[0]);
assert(47 === $m->getRepeatedFixed64()[0]);
assert(-46 === $m->getRepeatedSfixed32()[0]);
assert(-47 === $m->getRepeatedSfixed64()[0]);
assert(1.5 === $m->getRepeatedFloat()[0]);
assert(1.6 === $m->getRepeatedDouble()[0]);
assert(true=== $m->getRepeatedBool()[0]);
assert('a' === $m->getRepeatedString()[0]);
assert('b' === $m->getRepeatedBytes()[0]);
assert(TestEnum::ZERO === $m->getRepeatedEnum()[0]);
assert(34 === $m->getRepeatedMessage()[0]->getA());
assert(-52 === $m->getRepeatedInt32()[1]);
assert(52 === $m->getRepeatedUint32()[1]);
assert(-53 === $m->getRepeatedInt64()[1]);
assert(53 === $m->getRepeatedUint64()[1]);
assert(-54 === $m->getRepeatedSint32()[1]);
assert(-55 === $m->getRepeatedSint64()[1]);
assert(56 === $m->getRepeatedFixed32()[1]);
assert(57 === $m->getRepeatedFixed64()[1]);
assert(-56 === $m->getRepeatedSfixed32()[1]);
assert(-57 === $m->getRepeatedSfixed64()[1]);
assert(2.5 === $m->getRepeatedFloat()[1]);
assert(2.6 === $m->getRepeatedDouble()[1]);
assert(false === $m->getRepeatedBool()[1]);
assert('c' === $m->getRepeatedString()[1]);
assert('d' === $m->getRepeatedBytes()[1]);
assert(TestEnum::ONE === $m->getRepeatedEnum()[1]);
assert(35 === $m->getRepeatedMessage()[1]->getA());
assert(-62 === $m->getMapInt32Int32()[-62]);
assert(-63 === $m->getMapInt64Int64()[-63]);
assert(62 === $m->getMapUint32Uint32()[62]);
assert(63 === $m->getMapUint64Uint64()[63]);
assert(-64 === $m->getMapSint32Sint32()[-64]);
assert(-65 === $m->getMapSint64Sint64()[-65]);
assert(66 === $m->getMapFixed32Fixed32()[66]);
assert(67 === $m->getMapFixed64Fixed64()[67]);
assert(3.5 === $m->getMapInt32Float()[1]);
assert(3.6 === $m->getMapInt32Double()[1]);
assert(true === $m->getMapBoolBool()[true]);
assert('e' === $m->getMapStringString()['e']);
assert('f' === $m->getMapInt32Bytes()[1]);
assert(TestEnum::ONE === $m->getMapInt32Enum()[1]);
assert(36 === $m->getMapInt32Message()[1]->GetA());
}
public static function getGoldenTestMessage()
{
return hex2bin(
"08D6FFFFFF0F" .
"10D5FFFFFFFFFFFFFFFF01" .
"182A" .
"202B" .
"2857" .
"3059" .
"3D2E000000" .
"412F00000000000000" .
"4DD2FFFFFF" .
"51D1FFFFFFFFFFFFFF" .
"5D0000C03F" .
"619A9999999999F93F" .
"6801" .
"720161" .
"7A0162" .
"800101" .
"8A01020821" .
"F801D6FFFFFF0F" .
"F801CCFFFFFF0F" .
"8002D5FFFFFFFFFFFFFFFF01" .
"8002CBFFFFFFFFFFFFFFFF01" .
"88022A" .
"880234" .
"90022B" .
"900235" .
"980257" .
"98026B" .
"A00259" .
"A0026D" .
"AD022E000000" .
"AD0238000000" .
"B1022F00000000000000" .
"B1023900000000000000" .
"BD02D2FFFFFF" .
"BD02C8FFFFFF" .
"C102D1FFFFFFFFFFFFFF" .
"C102C7FFFFFFFFFFFFFF" .
"CD020000C03F" .
"CD0200002040" .
"D1029A9999999999F93F" .
"D102CDCCCCCCCCCC0440" .
"D80201" .
"D80200" .
"E2020161" .
"E2020163" .
"EA020162" .
"EA020164" .
"F00200" .
"F00201" .
"FA02020822" .
"FA02020823" .
"BA040C08C2FFFFFF0F10C2FFFFFF0F" .
"C2041608C1FFFFFFFFFFFFFFFF0110C1FFFFFFFFFFFFFFFF01" .
"CA0404083E103E" .
"D20404083F103F" .
"DA0404087f107F" .
"E20406088101108101" .
"EA040A0D420000001542000000" .
"F20412094300000000000000114300000000000000" .
"8A050708011500006040" .
"92050B080111CDCCCCCCCCCC0C40" .
"9A050408011001" .
"A205060a0165120165" .
"AA05050801120166" .
"B2050408011001" .
"Ba0506080112020824"
);
}
public static function setTestPackedMessage($m)
{
$m->getRepeatedInt32()[] = -42;
$m->getRepeatedInt32()[] = -52;
$m->getRepeatedInt64()[] = -43;
$m->getRepeatedInt64()[] = -53;
$m->getRepeatedUint32()[] = 42;
$m->getRepeatedUint32()[] = 52;
$m->getRepeatedUint64()[] = 43;
$m->getRepeatedUint64()[] = 53;
$m->getRepeatedSint32()[] = -44;
$m->getRepeatedSint32()[] = -54;
$m->getRepeatedSint64()[] = -45;
$m->getRepeatedSint64()[] = -55;
$m->getRepeatedFixed32()[] = 46;
$m->getRepeatedFixed32()[] = 56;
$m->getRepeatedFixed64()[] = 47;
$m->getRepeatedFixed64()[] = 57;
$m->getRepeatedSfixed32()[] = -46;
$m->getRepeatedSfixed32()[] = -56;
$m->getRepeatedSfixed64()[] = -47;
$m->getRepeatedSfixed64()[] = -57;
$m->getRepeatedFloat()[] = 1.5;
$m->getRepeatedFloat()[] = 2.5;
$m->getRepeatedDouble()[] = 1.6;
$m->getRepeatedDouble()[] = 2.6;
$m->getRepeatedBool()[] = true;
$m->getRepeatedBool()[] = false;
$m->getRepeatedEnum()[] = TestEnum::ONE;
$m->getRepeatedEnum()[] = TestEnum::ZERO;
}
public static function assertTestPackedMessage($m)
{
assert(2 === count($m->getRepeatedInt32()));
assert(2 === count($m->getRepeatedInt64()));
assert(2 === count($m->getRepeatedUint32()));
assert(2 === count($m->getRepeatedUint64()));
assert(2 === count($m->getRepeatedSint32()));
assert(2 === count($m->getRepeatedSint64()));
assert(2 === count($m->getRepeatedFixed32()));
assert(2 === count($m->getRepeatedFixed64()));
assert(2 === count($m->getRepeatedSfixed32()));
assert(2 === count($m->getRepeatedSfixed64()));
assert(2 === count($m->getRepeatedFloat()));
assert(2 === count($m->getRepeatedDouble()));
assert(2 === count($m->getRepeatedBool()));
assert(2 === count($m->getRepeatedEnum()));
assert(-42 === $m->getRepeatedInt32()[0]);
assert(-52 === $m->getRepeatedInt32()[1]);
assert(-43 === $m->getRepeatedInt64()[0]);
assert(-53 === $m->getRepeatedInt64()[1]);
assert(42 === $m->getRepeatedUint32()[0]);
assert(52 === $m->getRepeatedUint32()[1]);
assert(43 === $m->getRepeatedUint64()[0]);
assert(53 === $m->getRepeatedUint64()[1]);
assert(-44 === $m->getRepeatedSint32()[0]);
assert(-54 === $m->getRepeatedSint32()[1]);
assert(-45 === $m->getRepeatedSint64()[0]);
assert(-55 === $m->getRepeatedSint64()[1]);
assert(46 === $m->getRepeatedFixed32()[0]);
assert(56 === $m->getRepeatedFixed32()[1]);
assert(47 === $m->getRepeatedFixed64()[0]);
assert(57 === $m->getRepeatedFixed64()[1]);
assert(-46 === $m->getRepeatedSfixed32()[0]);
assert(-56 === $m->getRepeatedSfixed32()[1]);
assert(-47 === $m->getRepeatedSfixed64()[0]);
assert(-57 === $m->getRepeatedSfixed64()[1]);
assert(1.5 === $m->getRepeatedFloat()[0]);
assert(2.5 === $m->getRepeatedFloat()[1]);
assert(1.6 === $m->getRepeatedDouble()[0]);
assert(2.6 === $m->getRepeatedDouble()[1]);
assert(true === $m->getRepeatedBool()[0]);
assert(false === $m->getRepeatedBool()[1]);
assert(TestEnum::ONE === $m->getRepeatedEnum()[0]);
assert(TestEnum::ZERO === $m->getRepeatedEnum()[1]);
}
public static function getGoldenTestPackedMessage()
{
return hex2bin(
"D2050AD6FFFFFF0FCCFFFFFF0F" .
"DA0514D5FFFFFFFFFFFFFFFF01CBFFFFFFFFFFFFFFFF01" .
"E205022A34" .
"EA05022B35" .
"F20502576B" .
"FA0502596D" .
"8206082E00000038000000" .
"8A06102F000000000000003900000000000000" .
"920608D2FFFFFFC8FFFFFF" .
"9A0610D1FFFFFFFFFFFFFFC7FFFFFFFFFFFFFF" .
"A206080000C03F00002040" .
"AA06109A9999999999F93FCDCCCCCCCCCC0440" .
"B206020100" .
"BA06020100"
);
}
public static function getGoldenTestUnpackedMessage()
{
return hex2bin(
"D005D6FFFFFF0FD005CCFFFFFF0F" .
"D805D5FFFFFFFFFFFFFFFF01D805CBFFFFFFFFFFFFFFFF01" .
"E0052AE00534" .
"E8052BE80535" .
"F00557F0056B" .
"F80559F8056D" .
"85062E000000850638000000" .
"89062F0000000000000089063900000000000000" .
"9506D2FFFFFF9506C8FFFFFF" .
"9906D1FFFFFFFFFFFFFF9906C7FFFFFFFFFFFFFF" .
"A5060000C03FA50600002040" .
"A9069A9999999999F93FA906CDCCCCCCCCCC0440" .
"B00601B00600" .
"B80601B80600"
);
}
}

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="protobuf-tests">
<file>php/tests/php_implementation_test.php</file>
<file>php/tests/array_test.php</file>
<file>php/tests/encode_decode_test.php</file>
<file>php/tests/generated_class_test.php</file>
<file>php/tests/map_field_test.php</file>
</testsuite>
</testsuites>
</phpunit>

@ -162,6 +162,7 @@ nobase_include_HEADERS = \
google/protobuf/compiler/js/js_generator.h \
google/protobuf/compiler/objectivec/objectivec_generator.h \
google/protobuf/compiler/objectivec/objectivec_helpers.h \
google/protobuf/compiler/php/php_generator.h \
google/protobuf/compiler/python/python_generator.h \
google/protobuf/compiler/ruby/ruby_generator.h \
google/protobuf/util/type_resolver.h \
@ -443,6 +444,7 @@ libprotoc_la_SOURCES = \
google/protobuf/compiler/objectivec/objectivec_oneof.h \
google/protobuf/compiler/objectivec/objectivec_primitive_field.cc \
google/protobuf/compiler/objectivec/objectivec_primitive_field.h \
google/protobuf/compiler/php/php_generator.cc \
google/protobuf/compiler/python/python_generator.cc \
google/protobuf/compiler/ruby/ruby_generator.cc \
google/protobuf/compiler/csharp/csharp_doc_comment.cc \

@ -35,8 +35,7 @@
#include <google/protobuf/compiler/python/python_generator.h>
#include <google/protobuf/compiler/java/java_generator.h>
#include <google/protobuf/compiler/javanano/javanano_generator.h>
// TODO(teboring): Add it back when php implementation is ready
// #include <google/protobuf/compiler/php/php_generator.h>
#include <google/protobuf/compiler/php/php_generator.h>
#include <google/protobuf/compiler/ruby/ruby_generator.h>
#include <google/protobuf/compiler/csharp/csharp_generator.h>
#include <google/protobuf/compiler/objectivec/objectivec_generator.h>
@ -68,11 +67,10 @@ int main(int argc, char* argv[]) {
cli.RegisterGenerator("--javanano_out", &javanano_generator,
"Generate Java Nano source file.");
// TODO(teboring): Add it back when php implementation is ready
// PHP
// google::protobuf::compiler::php::Generator php_generator;
// cli.RegisterGenerator("--php_out", &php_generator,
// "Generate PHP source file.");
google::protobuf::compiler::php::Generator php_generator;
cli.RegisterGenerator("--php_out", &php_generator,
"Generate PHP source file.");
// Ruby
google::protobuf::compiler::ruby::Generator rb_generator;

@ -0,0 +1,781 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/compiler/php/php_generator.h>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/plugin.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <sstream>
using google::protobuf::internal::scoped_ptr;
const std::string kDescriptorFile = "google/protobuf/descriptor.proto";
const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal";
namespace google {
namespace protobuf {
namespace compiler {
namespace php {
// Forward decls.
std::string PhpName(const std::string& full_name, bool is_descriptor);
std::string DefaultForField(google::protobuf::FieldDescriptor* field);
std::string IntToString(int32 value);
std::string GeneratedFileName(const std::string& proto_file,
bool is_descriptor);
std::string LabelForField(google::protobuf::FieldDescriptor* field);
std::string TypeName(google::protobuf::FieldDescriptor* field);
std::string UnderscoresToCamelCase(const string& name, bool cap_first_letter);
std::string EscapeDollor(const string& to_escape);
std::string BinaryToHex(const string& binary);
void GenerateMessage(const string& name_prefix,
const google::protobuf::Descriptor* message,
bool is_descriptor,
google::protobuf::io::Printer* printer);
void GenerateEnum(const google::protobuf::EnumDescriptor* en,
google::protobuf::io::Printer* printer);
void Indent(google::protobuf::io::Printer* printer);
void Outdent(google::protobuf::io::Printer* printer);
std::string MessageName(const google::protobuf::Descriptor* message,
bool is_descriptor) {
string message_name = message->name();
const google::protobuf::Descriptor* descriptor = message->containing_type();
while (descriptor != NULL) {
message_name = descriptor->name() + '_' + message_name;
descriptor = descriptor->containing_type();
}
return PhpName(message->file()->package(), is_descriptor) + '\\' +
message_name;
}
std::string MessageFullName(const google::protobuf::Descriptor* message,
bool is_descriptor) {
if (is_descriptor) {
return StringReplace(message->full_name(),
"google.protobuf",
"google.protobuf.internal", false);
} else {
return message->full_name();
}
}
std::string EnumFullName(const google::protobuf::EnumDescriptor* envm,
bool is_descriptor) {
if (is_descriptor) {
return StringReplace(envm->full_name(),
"google.protobuf",
"google.protobuf.internal", false);
} else {
return envm->full_name();
}
}
std::string EnumClassName(const google::protobuf::EnumDescriptor* envm) {
string enum_class_name = envm->name();
const google::protobuf::Descriptor* descriptor = envm->containing_type();
while (descriptor != NULL) {
enum_class_name = descriptor->name() + '_' + enum_class_name;
descriptor = descriptor->containing_type();
}
return enum_class_name;
}
std::string EnumName(const google::protobuf::EnumDescriptor* envm,
bool is_descriptor) {
string enum_name = EnumClassName(envm);
return PhpName(envm->file()->package(), is_descriptor) + '\\' + enum_name;
}
std::string PhpName(const std::string& full_name, bool is_descriptor) {
if (is_descriptor) {
return kDescriptorPackageName;
}
std::string result;
bool cap_next_letter = true;
for (int i = 0; i < full_name.size(); i++) {
if ('a' <= full_name[i] && full_name[i] <= 'z' && cap_next_letter) {
result += full_name[i] + ('A' - 'a');
cap_next_letter = false;
} else if (full_name[i] == '.') {
result += '\\';
cap_next_letter = true;
} else {
result += full_name[i];
cap_next_letter = false;
}
}
return result;
}
std::string DefaultForField(const google::protobuf::FieldDescriptor* field) {
switch (field->type()) {
case FieldDescriptor::TYPE_INT32:
case FieldDescriptor::TYPE_INT64:
case FieldDescriptor::TYPE_UINT32:
case FieldDescriptor::TYPE_UINT64:
case FieldDescriptor::TYPE_SINT32:
case FieldDescriptor::TYPE_SINT64:
case FieldDescriptor::TYPE_FIXED32:
case FieldDescriptor::TYPE_FIXED64:
case FieldDescriptor::TYPE_SFIXED32:
case FieldDescriptor::TYPE_SFIXED64:
case FieldDescriptor::TYPE_ENUM: return "0";
case FieldDescriptor::TYPE_DOUBLE:
case FieldDescriptor::TYPE_FLOAT: return "0.0";
case FieldDescriptor::TYPE_BOOL: return "false";
case FieldDescriptor::TYPE_STRING:
case FieldDescriptor::TYPE_BYTES: return "''";
case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP: return "null";
default: assert(false); return "";
}
}
std::string GeneratedFileName(const std::string& proto_file,
bool is_descriptor) {
if (is_descriptor) {
return "descriptor_internal.pb.php";
} else {
int lastindex = proto_file.find_last_of(".");
return proto_file.substr(0, lastindex) + ".pb.php";
}
}
std::string IntToString(int32 value) {
std::ostringstream os;
os << value;
return os.str();
}
std::string LabelForField(const google::protobuf::FieldDescriptor* field) {
switch (field->label()) {
case FieldDescriptor::LABEL_OPTIONAL: return "optional";
case FieldDescriptor::LABEL_REQUIRED: return "required";
case FieldDescriptor::LABEL_REPEATED: return "repeated";
default: assert(false); return "";
}
}
std::string TypeName(const google::protobuf::FieldDescriptor* field) {
switch (field->type()) {
case FieldDescriptor::TYPE_INT32: return "int32";
case FieldDescriptor::TYPE_INT64: return "int64";
case FieldDescriptor::TYPE_UINT32: return "uint32";
case FieldDescriptor::TYPE_UINT64: return "uint64";
case FieldDescriptor::TYPE_SINT32: return "sint32";
case FieldDescriptor::TYPE_SINT64: return "sint64";
case FieldDescriptor::TYPE_FIXED32: return "fixed32";
case FieldDescriptor::TYPE_FIXED64: return "fixed64";
case FieldDescriptor::TYPE_SFIXED32: return "sfixed32";
case FieldDescriptor::TYPE_SFIXED64: return "sfixed64";
case FieldDescriptor::TYPE_DOUBLE: return "double";
case FieldDescriptor::TYPE_FLOAT: return "float";
case FieldDescriptor::TYPE_BOOL: return "bool";
case FieldDescriptor::TYPE_ENUM: return "enum";
case FieldDescriptor::TYPE_STRING: return "string";
case FieldDescriptor::TYPE_BYTES: return "bytes";
case FieldDescriptor::TYPE_MESSAGE: return "message";
case FieldDescriptor::TYPE_GROUP: return "group";
default: assert(false); return "";
}
}
std::string EnumOrMessageSuffix(
const google::protobuf::FieldDescriptor* field, bool is_descriptor) {
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
return ", '" + MessageFullName(field->message_type(), is_descriptor) + "'";
}
if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
return ", '" + EnumFullName(field->enum_type(), is_descriptor) + "'";
}
return "";
}
// Converts a name to camel-case. If cap_first_letter is true, capitalize the
// first letter.
std::string UnderscoresToCamelCase(const string& input, bool cap_first_letter) {
std::string result;
for (int i = 0; i < input.size(); i++) {
if ('a' <= input[i] && input[i] <= 'z') {
if (cap_first_letter) {
result += input[i] + ('A' - 'a');
} else {
result += input[i];
}
cap_first_letter = false;
} else if ('A' <= input[i] && input[i] <= 'Z') {
if (i == 0 && !cap_first_letter) {
// Force first letter to lower-case unless explicitly told to
// capitalize it.
result += input[i] + ('a' - 'A');
} else {
// Capital letters after the first are left as-is.
result += input[i];
}
cap_first_letter = false;
} else if ('0' <= input[i] && input[i] <= '9') {
result += input[i];
cap_first_letter = true;
} else {
cap_first_letter = true;
}
}
// Add a trailing "_" if the name should be altered.
if (input[input.size() - 1] == '#') {
result += '_';
}
return result;
}
std::string EscapeDollor(const string& to_escape) {
return StringReplace(to_escape, "$", "\\$", true);
}
std::string BinaryToHex(const string& src) {
string dest;
size_t i;
unsigned char symbol[16] = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f',
};
dest.resize(src.size() * 2);
char* append_ptr = &dest[0];
for (i = 0; i < src.size(); i++) {
*append_ptr++ = symbol[(src[i] & 0xf0) >> 4];
*append_ptr++ = symbol[src[i] & 0x0f];
}
return dest;
}
void Indent(google::protobuf::io::Printer* printer) {
printer->Indent();
printer->Indent();
}
void Outdent(google::protobuf::io::Printer* printer) {
printer->Outdent();
printer->Outdent();
}
void GenerateField(const google::protobuf::FieldDescriptor* field,
google::protobuf::io::Printer* printer, bool is_descriptor) {
if (field->is_repeated()) {
printer->Print(
"private $@name@;\n",
"name", field->name());
} else if (field->containing_oneof()) {
// Oneof fields are handled by GenerateOneofField.
return;
} else {
printer->Print(
"private $@name@ = @default@;\n",
"name", field->name(),
"default", DefaultForField(field));
}
if (is_descriptor) {
printer->Print(
"private $has_@name@ = false;\n",
"name", field->name());
}
}
void GenerateOneofField(const google::protobuf::OneofDescriptor* oneof,
google::protobuf::io::Printer* printer) {
// Oneof property needs to be protected in order to be accessed by parent
// class in implementation.
printer->Print(
"protected $@name@;\n",
"name", oneof->name());
}
void GenerateFieldAccessor(const google::protobuf::FieldDescriptor* field,
bool is_descriptor,
google::protobuf::io::Printer* printer) {
const OneofDescriptor* oneof = field->containing_oneof();
// Generate getter.
if (oneof != NULL) {
printer->Print(
"public function get@camel_name@()\n"
"{\n"
" return $this->readOneof(@number@);\n"
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true),
"number", IntToString(field->number()));
} else {
printer->Print(
"public function get@camel_name@()\n"
"{\n"
" return $this->@name@;\n"
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true), "name",
field->name());
}
// Generate setter.
printer->Print(
"public function set@camel_name@(@var@)\n"
"{\n",
"camel_name", UnderscoresToCamelCase(field->name(), true),
"var", (field->is_repeated() ||
field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
"&$var": "$var");
Indent(printer);
// Type check.
if (field->is_map()) {
} else if (field->is_repeated()) {
printer->Print(
"GPBUtil::checkRepeatedField($var, GPBType::@type@",
"type", ToUpper(field->type_name()));
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
printer->Print(
", \\@class_name@);\n",
"class_name",
MessageName(field->message_type(), is_descriptor) + "::class");
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
printer->Print(
", @class_name@);\n",
"class_name",
EnumName(field->enum_type(), is_descriptor) + "::class");
} else {
printer->Print(");\n");
}
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
printer->Print(
"GPBUtil::checkMessage($var, \\@class_name@::class);\n",
"class_name", MessageName(field->message_type(), is_descriptor));
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
printer->Print(
"GPBUtil::checkEnum($var, \\@class_name@::class);\n",
"class_name", EnumName(field->enum_type(), is_descriptor));
} else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
printer->Print(
"GPBUtil::checkString($var, @utf8@);\n",
"utf8",
field->type() == FieldDescriptor::TYPE_STRING ? "True": "False");
} else {
printer->Print(
"GPBUtil::check@type@($var);\n",
"type", UnderscoresToCamelCase(field->cpp_type_name(), true));
}
if (oneof != NULL) {
printer->Print(
"$this->writeOneof(@number@, $var);\n",
"number", IntToString(field->number()));
} else {
printer->Print(
"$this->@name@ = $var;\n",
"name", field->name());
}
// Set has bit for proto2 only.
if (is_descriptor) {
printer->Print(
"$this->has_@field_name@ = true;\n",
"field_name", field->name());
}
Outdent(printer);
printer->Print(
"}\n\n");
// Generate has method for proto2 only.
if (is_descriptor) {
printer->Print(
"public function has@camel_name@()\n"
"{\n"
" return $this->has_@field_name@;\n"
"}\n\n",
"camel_name", UnderscoresToCamelCase(field->name(), true),
"field_name", field->name());
}
}
void GenerateRepeatedFieldDecode(
const google::protobuf::FieldDescriptor* field,
google::protobuf::io::Printer* printer) {
printer->Print(
"if ($input->read@cap_wire_type@($var)) return False;\n"
"$this->get@cap_field_name@() []= $var;\n",
"cap_field_name", UnderscoresToCamelCase(field->name(), true),
"cap_wire_type", UnderscoresToCamelCase(field->type_name(), true));
}
void GeneratePrimitiveFieldDecode(
const google::protobuf::FieldDescriptor* field,
google::protobuf::io::Printer* printer) {
printer->Print(
"if ($input->read@cap_wire_type@($var)) return False;\n"
"$this->set@cap_field_name@($var);\n",
"cap_field_name", UnderscoresToCamelCase(field->name(), true),
"cap_wire_type", UnderscoresToCamelCase(field->type_name(), true));
}
void GenerateFieldDecode(const google::protobuf::FieldDescriptor* field,
google::protobuf::io::Printer* printer) {
printer->Print(
"case @number@:\n",
"number", IntToString(field->number()));
Indent(printer);
if (field->is_repeated()) {
GenerateRepeatedFieldDecode(field, printer);
} else {
GeneratePrimitiveFieldDecode(field, printer);
}
printer->Print(
"break;\n");
Outdent(printer);
}
void GenerateMessage(const string& name_prefix,
const google::protobuf::Descriptor* message,
bool is_descriptor,
google::protobuf::io::Printer* printer) {
// Don't generate MapEntry messages -- we use the PHP extension's native
// support for map fields instead.
if (message->options().map_entry()) {
return;
}
string message_name = name_prefix.empty()?
message->name() : name_prefix + "_" + message->name();
printer->Print(
"class @name@ extends \\Google\\Protobuf\\Internal\\Message\n"
"{\n",
"name", message_name);
Indent(printer);
// Field and oneof definitions.
for (int i = 0; i < message->field_count(); i++) {
const FieldDescriptor* field = message->field(i);
GenerateField(field, printer, is_descriptor);
}
for (int i = 0; i < message->oneof_decl_count(); i++) {
const OneofDescriptor* oneof = message->oneof_decl(i);
GenerateOneofField(oneof, printer);
}
printer->Print("\n");
// Field and oneof accessors.
for (int i = 0; i < message->field_count(); i++) {
const FieldDescriptor* field = message->field(i);
GenerateFieldAccessor(field, is_descriptor, printer);
}
for (int i = 0; i < message->oneof_decl_count(); i++) {
const google::protobuf::OneofDescriptor* oneof = message->oneof_decl(i);
printer->Print(
"public function get@camel_name@()\n"
"{\n"
" return $this->@name@;\n"
"}\n\n",
"camel_name", UnderscoresToCamelCase(oneof->name(), true), "name",
oneof->name());
}
Outdent(printer);
printer->Print("}\n\n");
// Nested messages and enums.
for (int i = 0; i < message->nested_type_count(); i++) {
GenerateMessage(message_name, message->nested_type(i), is_descriptor,
printer);
}
for (int i = 0; i < message->enum_type_count(); i++) {
GenerateEnum(message->enum_type(i), printer);
}
}
void GenerateEnumToPool(const google::protobuf::EnumDescriptor* en,
bool is_descriptor,
google::protobuf::io::Printer* printer) {
printer->Print(
"$pool->addEnum('@name@', @class_name@::class)\n",
"name", EnumFullName(en, is_descriptor),
"class_name", en->name());
Indent(printer);
for (int i = 0; i < en->value_count(); i++) {
const EnumValueDescriptor* value = en->value(i);
printer->Print(
"->value(\"@name@\", @number@)\n",
"name", value->name(),
"number", IntToString(value->number()));
}
printer->Print("->finalizeToPool();\n\n");
Outdent(printer);
}
void GenerateMessageToPool(const string& name_prefix,
const google::protobuf::Descriptor* message,
bool is_descriptor,
google::protobuf::io::Printer* printer) {
// Don't generate MapEntry messages -- we use the PHP extension's native
// support for map fields instead.
if (message->options().map_entry()) {
return;
}
string class_name = name_prefix.empty()?
message->name() : name_prefix + "_" + message->name();
printer->Print(
"$pool->addMessage('@message@', @class_name@::class)\n",
"message", MessageFullName(message, is_descriptor),
"class_name", class_name);
Indent(printer);
for (int i = 0; i < message->field_count(); i++) {
const FieldDescriptor* field = message->field(i);
if (field->is_map()) {
const FieldDescriptor* key =
field->message_type()->FindFieldByName("key");
const FieldDescriptor* val =
field->message_type()->FindFieldByName("value");
printer->Print(
"->map('@field@', GPBType::@key@, "
"GPBType::@value@, @number@@other@)\n",
"field", field->name(),
"key", ToUpper(key->type_name()),
"value", ToUpper(val->type_name()),
"number", SimpleItoa(field->number()),
"other", EnumOrMessageSuffix(val, is_descriptor));
} else if (!field->containing_oneof()) {
printer->Print(
"->@label@('@field@', GPBType::@type@, @number@@other@)\n",
"field", field->name(),
"label", LabelForField(field),
"type", ToUpper(field->type_name()),
"number", SimpleItoa(field->number()),
"other", EnumOrMessageSuffix(field, is_descriptor));
}
}
// oneofs.
for (int i = 0; i < message->oneof_decl_count(); i++) {
const OneofDescriptor* oneof = message->oneof_decl(i);
printer->Print("->oneof(@name@)\n",
"name", oneof->name());
Indent(printer);
for (int index = 0; index < oneof->field_count(); index++) {
const FieldDescriptor* field = oneof->field(index);
printer->Print(
"->value('@field@', GPBType::@type@, @number@@other@)\n",
"field", field->name(),
"type", ToUpper(field->type_name()),
"number", SimpleItoa(field->number()),
"other", EnumOrMessageSuffix(field, is_descriptor));
}
printer->Print("->finish()\n");
Outdent(printer);
}
printer->Print(
"->finalizeToPool();\n");
Outdent(printer);
printer->Print(
"\n");
for (int i = 0; i < message->nested_type_count(); i++) {
GenerateMessageToPool(class_name, message->nested_type(i), is_descriptor,
printer);
}
for (int i = 0; i < message->enum_type_count(); i++) {
GenerateEnumToPool(message->enum_type(i), is_descriptor, printer);
}
}
void GenerateAddFileToPool(const google::protobuf::FileDescriptor* file,
bool is_descriptor,
google::protobuf::io::Printer* printer) {
if (is_descriptor) {
printer->Print("$pool = DescriptorPool::getGeneratedPool();\n\n");
for (int i = 0; i < file->message_type_count(); i++) {
GenerateMessageToPool("", file->message_type(i), is_descriptor, printer);
}
for (int i = 0; i < file->enum_type_count(); i++) {
GenerateEnumToPool(file->enum_type(i), is_descriptor, printer);
}
printer->Print(
"$pool->finish();\n");
} else {
// Add messages and enums to descriptor pool.
printer->Print("$pool = DescriptorPool::getGeneratedPool();\n\n");
FileDescriptorSet files;
FileDescriptorProto* file_proto = files.add_file();
file->CopyTo(file_proto);
string files_data;
files.SerializeToString(&files_data);
printer->Print("$pool->internalAddGeneratedFile(hex2bin(\n");
Indent(printer);
// Only write 30 bytes per line.
static const int kBytesPerLine = 30;
for (int i = 0; i < files_data.size(); i += kBytesPerLine) {
printer->Print(
"\"@data@\"@dot@\n",
"data", BinaryToHex(files_data.substr(i, kBytesPerLine)),
"dot", i + kBytesPerLine < files_data.size() ? " ." : "");
}
Outdent(printer);
printer->Print(
"));\n\n");
}
}
void GenerateEnum(const google::protobuf::EnumDescriptor* en,
google::protobuf::io::Printer* printer) {
printer->Print(
"class @name@\n"
"{\n",
"name", EnumClassName(en));
Indent(printer);
for (int i = 0; i < en->value_count(); i++) {
const EnumValueDescriptor* value = en->value(i);
printer->Print("const @name@ = @number@;\n",
"name", value->name(),
"number", IntToString(value->number()));
}
Outdent(printer);
printer->Print("}\n\n");
}
void GenerateUseDeclaration(bool is_descriptor,
google::protobuf::io::Printer* printer) {
if (!is_descriptor) {
printer->Print(
"use Google\\Protobuf\\Internal\\DescriptorPool;\n"
"use Google\\Protobuf\\Internal\\GPBType;\n"
"use Google\\Protobuf\\Internal\\RepeatedField;\n"
"use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
} else {
printer->Print(
"use Google\\Protobuf\\Internal\\DescriptorPool;\n"
"use Google\\Protobuf\\Internal\\GPBType;\n"
"use Google\\Protobuf\\Internal\\GPBWire;\n"
"use Google\\Protobuf\\Internal\\RepeatedField;\n"
"use Google\\Protobuf\\Internal\\InputStream;\n\n"
"use Google\\Protobuf\\Internal\\GPBUtil;\n\n");
}
}
void GenerateFile(const google::protobuf::FileDescriptor* file,
bool is_descriptor, google::protobuf::io::Printer* printer) {
printer->Print(
"<?php\n"
"# Generated by the protocol buffer compiler. DO NOT EDIT!\n"
"# source: @filename@\n"
"\n",
"filename", file->name());
if (!file->package().empty()) {
printer->Print("namespace @name@;\n\n",
"name", PhpName(file->package(), is_descriptor));
}
for (int i = 0; i < file->dependency_count(); i++) {
const std::string& name = file->dependency(i)->name();
printer->Print("require_once('@name@');\n", "name",
GeneratedFileName(name, is_descriptor));
}
GenerateUseDeclaration(is_descriptor, printer);
for (int i = 0; i < file->message_type_count(); i++) {
GenerateMessage("", file->message_type(i), is_descriptor, printer);
}
for (int i = 0; i < file->enum_type_count(); i++) {
GenerateEnum(file->enum_type(i), printer);
}
GenerateAddFileToPool(file, is_descriptor, printer);
}
bool Generator::Generate(
const FileDescriptor* file,
const string& parameter,
GeneratorContext* generator_context,
string* error) const {
bool is_descriptor = parameter == "internal";
if (is_descriptor && file->name() != kDescriptorFile) {
*error =
"Can only generate PHP code for google/protobuf/descriptor.proto.\n";
return false;
}
if (!is_descriptor && file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
*error =
"Can only generate PHP code for proto3 .proto files.\n"
"Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
return false;
}
std::string filename = GeneratedFileName(file->name(), is_descriptor);
scoped_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(filename));
io::Printer printer(output.get(), '@');
GenerateFile(file, is_descriptor, &printer);
return true;
}
} // namespace php
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -0,0 +1,57 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__
#define GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__
#include <google/protobuf/compiler/code_generator.h>
#include <string>
namespace google {
namespace protobuf {
namespace compiler {
namespace php {
class LIBPROTOC_EXPORT Generator
: public google::protobuf::compiler::CodeGenerator {
virtual bool Generate(
const FileDescriptor* file,
const string& parameter,
GeneratorContext* generator_context,
string* error) const;
};
} // namespace php
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_PHP_GENERATOR_H__
Loading…
Cancel
Save