New PHP C Extension based on upb_msg (#7614)
* Added code for the new PHP extension. * Removed a bunch of unused variables shown by compiler warnings. * Test protobuf2 in the tests. * Updated upb version to fix a goldenfile test. * Added pure-PHP changes. Extension now passes all tests. * Enabled protobuf2 for all C extension tests. * Fixed pure=PHP lib: full names no longer start with '.'. * Added files for new extension to Makefile.am. * Downgraded make-preload.py to python 2, since python3 isn't available in the php_all Kokoro image. * Disable tests of new C extension with PHP 5.x. * Also do not compile the extension for PHP5. * Accept version 5.*.*, and use /usr/bin/python. * Addressed PR comments. * Addressed PR comments. * Added "const" to a parameter and fixed a memory leak seen in Valgrind. * Stop testing the C extension for PHP5. The next release of protobuf will deprecate the C extension for PHP5, see: https://github.com/protocolbuffers/protobuf/issues/7525 * Made the PHP5.6 Mac test only exercise pure-PHP. * Build protoc for PHP 5.6 test. * Rewrote bundling script in PHP to avoid dependency on Python. * A few more fixes. * Fixed int32/int64 behavior for 32-bit builds. * Match more PHP versions in testing script. * Use phpize --clean before building extension. * Force-delete configure.in to avoid phpize problems cross-version. * Delete both configure.ac and configure.in.pull/7652/head
parent
2b7b7f7f72
commit
093faebcdb
28 changed files with 16922 additions and 108 deletions
@ -0,0 +1,95 @@ |
||||
// 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_API.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Arena
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct Arena { |
||||
zend_object std; |
||||
upb_arena* arena; |
||||
} Arena; |
||||
|
||||
zend_class_entry *Arena_class_entry; |
||||
static zend_object_handlers Arena_object_handlers; |
||||
|
||||
// PHP Object Handlers /////////////////////////////////////////////////////////
|
||||
|
||||
static zend_object* Arena_Create(zend_class_entry *class_type) { |
||||
Arena *intern = emalloc(sizeof(Arena)); |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &Arena_object_handlers; |
||||
intern->arena = upb_arena_new(); |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
return &intern->std; |
||||
} |
||||
|
||||
static void Arena_Free(zend_object* obj) { |
||||
Arena* intern = (Arena*)obj; |
||||
upb_arena_free(intern->arena); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
// C Functions from arena.h ////////////////////////////////////////////////////
|
||||
|
||||
void Arena_Init(zval* val) { |
||||
ZVAL_OBJ(val, Arena_Create(Arena_class_entry)); |
||||
} |
||||
|
||||
upb_arena *Arena_Get(zval *val) { |
||||
Arena *a = (Arena*)Z_OBJ_P(val); |
||||
return a->arena; |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Module init.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// No public methods.
|
||||
static const zend_function_entry Arena_methods[] = { |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
void Arena_ModuleInit() { |
||||
zend_class_entry tmp_ce; |
||||
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\Arena", Arena_methods); |
||||
Arena_class_entry = zend_register_internal_class(&tmp_ce); |
||||
Arena_class_entry->create_object = Arena_Create; |
||||
Arena_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
|
||||
memcpy(&Arena_object_handlers, &std_object_handlers, |
||||
sizeof(zend_object_handlers)); |
||||
Arena_object_handlers.free_obj = Arena_Free; |
||||
} |
@ -0,0 +1,47 @@ |
||||
// 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 PHP_PROTOBUF_ARENA_H_ |
||||
#define PHP_PROTOBUF_ARENA_H_ |
||||
|
||||
#include <php.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
// Registers the PHP Arena class.
|
||||
void Arena_ModuleInit(); |
||||
|
||||
// Creates and returns a new arena object that wraps a new upb_arena*.
|
||||
void Arena_Init(zval *val); |
||||
|
||||
// Gets the underlying upb_arena from this arena object.
|
||||
upb_arena *Arena_Get(zval *arena); |
||||
|
||||
#endif // PHP_PROTOBUF_ARENA_H_
|
@ -0,0 +1,602 @@ |
||||
// 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 "array.h" |
||||
|
||||
#include <Zend/zend_API.h> |
||||
#include <Zend/zend_interfaces.h> |
||||
|
||||
#include <ext/spl/spl_iterators.h> |
||||
|
||||
// This is not self-contained: it must be after other Zend includes.
|
||||
#include <Zend/zend_exceptions.h> |
||||
|
||||
#include "arena.h" |
||||
#include "convert.h" |
||||
#include "def.h" |
||||
#include "php-upb.h" |
||||
#include "protobuf.h" |
||||
|
||||
static void RepeatedFieldIter_make(zval *val, zval *repeated_field); |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// RepeatedField
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct { |
||||
zend_object std; |
||||
zval arena; |
||||
upb_array *array; |
||||
upb_fieldtype_t type; |
||||
const Descriptor* desc; // When values are messages.
|
||||
} RepeatedField; |
||||
|
||||
zend_class_entry *RepeatedField_class_entry; |
||||
static zend_object_handlers RepeatedField_object_handlers; |
||||
|
||||
// PHP Object Handlers /////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* RepeatedField_create() |
||||
* |
||||
* PHP class entry function to allocate and initialize a new RepeatedField |
||||
* object. |
||||
*/ |
||||
static zend_object* RepeatedField_create(zend_class_entry *class_type) { |
||||
RepeatedField *intern = emalloc(sizeof(RepeatedField)); |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &RepeatedField_object_handlers; |
||||
Arena_Init(&intern->arena); |
||||
intern->array = NULL; |
||||
intern->desc = NULL; |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
return &intern->std; |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField_dtor() |
||||
* |
||||
* Object handler to destroy a RepeatedField. This releases all resources |
||||
* associated with the message. Note that it is possible to access a destroyed |
||||
* object from PHP in rare cases. |
||||
*/ |
||||
static void RepeatedField_destructor(zend_object* obj) { |
||||
RepeatedField* intern = (RepeatedField*)obj; |
||||
ObjCache_Delete(intern->array); |
||||
zval_ptr_dtor(&intern->arena); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
static HashTable *RepeatedField_GetProperties(zval *object TSRMLS_DC) { |
||||
return NULL; // We do not have a properties table.
|
||||
} |
||||
|
||||
static zval *RepeatedField_GetPropertyPtrPtr(zval *object, zval *member, |
||||
int type, void **cache_slot) { |
||||
return NULL; // We don't offer direct references to our properties.
|
||||
} |
||||
|
||||
// C Functions from array.h ////////////////////////////////////////////////////
|
||||
|
||||
// These are documented in the header file.
|
||||
|
||||
void RepeatedField_GetPhpWrapper(zval *val, upb_array *arr, |
||||
const upb_fielddef *f, zval *arena) { |
||||
if (!arr) { |
||||
ZVAL_NULL(val); |
||||
return; |
||||
} |
||||
|
||||
if (!ObjCache_Get(arr, val)) { |
||||
RepeatedField *intern = emalloc(sizeof(RepeatedField)); |
||||
zend_object_std_init(&intern->std, RepeatedField_class_entry); |
||||
intern->std.handlers = &RepeatedField_object_handlers; |
||||
ZVAL_COPY(&intern->arena, arena); |
||||
intern->array = arr; |
||||
intern->type = upb_fielddef_type(f); |
||||
intern->desc = Descriptor_GetFromFieldDef(f); |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
ObjCache_Add(intern->array, &intern->std); |
||||
ZVAL_OBJ(val, &intern->std); |
||||
} |
||||
} |
||||
|
||||
upb_array *RepeatedField_GetUpbArray(zval *val, const upb_fielddef *f, |
||||
upb_arena *arena) { |
||||
if (Z_ISREF_P(val)) { |
||||
ZVAL_DEREF(val); |
||||
} |
||||
|
||||
if (Z_TYPE_P(val) == IS_ARRAY) { |
||||
// Auto-construct, eg. [1, 2, 3] -> upb_array([1, 2, 3]).
|
||||
upb_array *arr = upb_array_new(arena, upb_fielddef_type(f)); |
||||
HashTable *table = HASH_OF(val); |
||||
HashPosition pos; |
||||
upb_fieldtype_t type = upb_fielddef_type(f); |
||||
const Descriptor *desc = Descriptor_GetFromFieldDef(f); |
||||
|
||||
zend_hash_internal_pointer_reset_ex(table, &pos); |
||||
|
||||
while (true) { |
||||
zval *zv = zend_hash_get_current_data_ex(table, &pos); |
||||
upb_msgval val; |
||||
|
||||
if (!zv) return arr; |
||||
|
||||
if (!Convert_PhpToUpbAutoWrap(zv, &val, type, desc, arena)) { |
||||
return NULL; |
||||
} |
||||
|
||||
upb_array_append(arr, val, arena); |
||||
zend_hash_move_forward_ex(table, &pos); |
||||
} |
||||
} else if (Z_TYPE_P(val) == IS_OBJECT && |
||||
Z_OBJCE_P(val) == RepeatedField_class_entry) { |
||||
// Unwrap existing RepeatedField object to get the upb_array* inside.
|
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(val); |
||||
const Descriptor *desc = Descriptor_GetFromFieldDef(f); |
||||
|
||||
if (intern->type != upb_fielddef_type(f) || intern->desc != desc) { |
||||
php_error_docref(NULL, E_USER_ERROR, |
||||
"Wrong type for this repeated field."); |
||||
} |
||||
|
||||
upb_arena_fuse(arena, Arena_Get(&intern->arena)); |
||||
return intern->array; |
||||
} else { |
||||
php_error_docref(NULL, E_USER_ERROR, "Must be a repeated field"); |
||||
return NULL; |
||||
} |
||||
} |
||||
|
||||
// RepeatedField PHP methods ///////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* RepeatedField::__construct() |
||||
* |
||||
* Constructs an instance of RepeatedField. |
||||
* @param long Type of the stored element. |
||||
* @param string Message/Enum class. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, __construct) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
zend_long type; |
||||
zend_class_entry* klass = NULL; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|C", &type, &klass) != SUCCESS) { |
||||
return; |
||||
} |
||||
|
||||
intern->type = pbphp_dtype_to_type(type); |
||||
intern->desc = Descriptor_GetFromClassEntry(klass); |
||||
|
||||
if (intern->type == UPB_TYPE_MESSAGE && klass == NULL) { |
||||
php_error_docref(NULL, E_USER_ERROR, |
||||
"Message/enum type must have concrete class."); |
||||
return; |
||||
} |
||||
|
||||
intern->array = upb_array_new(arena, intern->type); |
||||
ObjCache_Add(intern->array, &intern->std); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::append() |
||||
* |
||||
* Append element to the end of the repeated field. |
||||
* @param object The element to be added. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, append) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
zval *php_val; |
||||
upb_msgval msgval; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &php_val) != SUCCESS || |
||||
!Convert_PhpToUpb(php_val, &msgval, intern->type, intern->desc, arena)) { |
||||
return; |
||||
} |
||||
|
||||
upb_array_append(intern->array, msgval, arena); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::offsetExists() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* isset($arr[$idx]); |
||||
* empty($arr[$idx]); |
||||
* |
||||
* @param long The index to be checked. |
||||
* @return bool True if the element at the given index exists. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, offsetExists) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
zend_long index; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
RETURN_BOOL(index >= 0 && index < upb_array_size(intern->array)); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::offsetGet() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* $x = $arr[$idx]; |
||||
* |
||||
* @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) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
zend_long index; |
||||
upb_msgval msgval; |
||||
zval ret; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
if (index < 0 || index >= upb_array_size(intern->array)) { |
||||
zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index); |
||||
return; |
||||
} |
||||
|
||||
msgval = upb_array_get(intern->array, index); |
||||
Convert_UpbToPhp(msgval, &ret, intern->type, intern->desc, &intern->arena); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::offsetSet() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* $arr[$idx] = $x; |
||||
* $arr []= $x; // Append
|
||||
* |
||||
* @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) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
size_t size = upb_array_size(intern->array); |
||||
zval *offset, *val; |
||||
int64_t index; |
||||
upb_msgval msgval; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &offset, &val) != SUCCESS) { |
||||
return; |
||||
} |
||||
|
||||
if (Z_TYPE_P(offset) == IS_NULL) { |
||||
index = size; |
||||
} else if (!Convert_PhpToInt64(offset, &index)) { |
||||
return; |
||||
} |
||||
|
||||
if (!Convert_PhpToUpb(val, &msgval, intern->type, intern->desc, arena)) { |
||||
return; |
||||
} |
||||
|
||||
if (index > size) { |
||||
zend_error(E_USER_ERROR, "Element at index %ld doesn't exist.\n", index); |
||||
} else if (index == size) { |
||||
upb_array_append(intern->array, msgval, Arena_Get(&intern->arena)); |
||||
} else { |
||||
upb_array_set(intern->array, index, msgval); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::offsetUnset() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* unset($arr[$idx]); |
||||
* |
||||
* @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) { |
||||
RepeatedField *intern = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
zend_long index; |
||||
zend_long size = upb_array_size(intern->array); |
||||
|
||||
// Only the element at the end of the array can be removed.
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &index) != SUCCESS) { |
||||
return; |
||||
} |
||||
|
||||
if (size == 0 || index != size - 1) { |
||||
php_error_docref(NULL, E_USER_ERROR, "Cannot remove element at %ld.\n", |
||||
index); |
||||
return; |
||||
} |
||||
|
||||
upb_array_resize(intern->array, size - 1, Arena_Get(&intern->arena)); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::count() |
||||
* |
||||
* Implements the Countable interface. Invoked when PHP code calls: |
||||
* |
||||
* $len = count($arr); |
||||
* 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 = (RepeatedField*)Z_OBJ_P(getThis()); |
||||
|
||||
if (zend_parse_parameters_none() == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
RETURN_LONG(upb_array_size(intern->array)); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedField::getIterator() |
||||
* |
||||
* Implements the IteratorAggregate interface. Invoked when PHP code calls: |
||||
* |
||||
* foreach ($arr) {} |
||||
* |
||||
* @return object Beginning iterator. |
||||
*/ |
||||
PHP_METHOD(RepeatedField, getIterator) { |
||||
zval ret; |
||||
RepeatedFieldIter_make(&ret, getThis()); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
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) |
||||
PHP_ME(RepeatedField, getIterator, arginfo_void, ZEND_ACC_PUBLIC) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PHP RepeatedFieldIter
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct { |
||||
zend_object std; |
||||
zval repeated_field; |
||||
zend_long position; |
||||
} RepeatedFieldIter; |
||||
|
||||
zend_class_entry *RepeatedFieldIter_class_entry; |
||||
static zend_object_handlers repeated_field_iter_object_handlers; |
||||
|
||||
/**
|
||||
* RepeatedFieldIter_create() |
||||
* |
||||
* PHP class entry function to allocate and initialize a new RepeatedFieldIter |
||||
* object. |
||||
*/ |
||||
zend_object* RepeatedFieldIter_create(zend_class_entry *class_type) { |
||||
RepeatedFieldIter *intern = emalloc(sizeof(RepeatedFieldIter)); |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &repeated_field_iter_object_handlers; |
||||
ZVAL_NULL(&intern->repeated_field); |
||||
intern->position = 0; |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
return &intern->std; |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter_dtor() |
||||
* |
||||
* Object handler to destroy a RepeatedFieldIter. This releases all resources |
||||
* associated with the message. Note that it is possible to access a destroyed |
||||
* object from PHP in rare cases. |
||||
*/ |
||||
static void RepeatedFieldIter_dtor(zend_object* obj) { |
||||
RepeatedFieldIter* intern = (RepeatedFieldIter*)obj; |
||||
zval_ptr_dtor(&intern->repeated_field); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter_make() |
||||
* |
||||
* C function to create a RepeatedFieldIter. |
||||
*/ |
||||
static void RepeatedFieldIter_make(zval *val, zval *repeated_field) { |
||||
RepeatedFieldIter *iter; |
||||
ZVAL_OBJ(val, RepeatedFieldIter_class_entry->create_object( |
||||
RepeatedFieldIter_class_entry)); |
||||
iter = (RepeatedFieldIter*)Z_OBJ_P(val); |
||||
ZVAL_COPY(&iter->repeated_field, repeated_field); |
||||
} |
||||
|
||||
/*
|
||||
* When a user writes: |
||||
* |
||||
* foreach($arr as $key => $val) {} |
||||
* |
||||
* PHP's iterator protocol is: |
||||
* |
||||
* $iter = $arr->getIterator(); |
||||
* for ($iter->rewind(); $iter->valid(); $iter->next()) { |
||||
* $key = $iter->key(); |
||||
* $val = $iter->current(); |
||||
* } |
||||
*/ |
||||
|
||||
/**
|
||||
* RepeatedFieldIter::rewind() |
||||
* |
||||
* Implements the Iterator interface. Sets the iterator to the first element. |
||||
*/ |
||||
PHP_METHOD(RepeatedFieldIter, rewind) { |
||||
RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis()); |
||||
intern->position = 0; |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter::current() |
||||
* |
||||
* Implements the Iterator interface. Returns the current value. |
||||
*/ |
||||
PHP_METHOD(RepeatedFieldIter, current) { |
||||
RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis()); |
||||
RepeatedField *field = (RepeatedField*)Z_OBJ_P(&intern->repeated_field); |
||||
upb_array *array = field->array; |
||||
zend_long index = intern->position; |
||||
upb_msgval msgval; |
||||
zval ret; |
||||
|
||||
if (index < 0 || index >= upb_array_size(array)) { |
||||
zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index); |
||||
} |
||||
|
||||
msgval = upb_array_get(array, index); |
||||
|
||||
Convert_UpbToPhp(msgval, &ret, field->type, field->desc, &field->arena); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter::key() |
||||
* |
||||
* Implements the Iterator interface. Returns the current key. |
||||
*/ |
||||
PHP_METHOD(RepeatedFieldIter, key) { |
||||
RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis()); |
||||
RETURN_LONG(intern->position); |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter::next() |
||||
* |
||||
* Implements the Iterator interface. Advances to the next element. |
||||
*/ |
||||
PHP_METHOD(RepeatedFieldIter, next) { |
||||
RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis()); |
||||
++intern->position; |
||||
} |
||||
|
||||
/**
|
||||
* RepeatedFieldIter::valid() |
||||
* |
||||
* Implements the Iterator interface. Returns true if this is a valid element. |
||||
*/ |
||||
PHP_METHOD(RepeatedFieldIter, valid) { |
||||
RepeatedFieldIter *intern = (RepeatedFieldIter*)Z_OBJ_P(getThis()); |
||||
RepeatedField *field = (RepeatedField*)Z_OBJ_P(&intern->repeated_field); |
||||
RETURN_BOOL(intern->position < upb_array_size(field->array)); |
||||
} |
||||
|
||||
static zend_function_entry repeated_field_iter_methods[] = { |
||||
PHP_ME(RepeatedFieldIter, rewind, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedFieldIter, current, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedFieldIter, key, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedFieldIter, next, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(RepeatedFieldIter, valid, arginfo_void, ZEND_ACC_PUBLIC) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Module init.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Array_ModuleInit() |
||||
* |
||||
* Called when the C extension is loaded to register all types. |
||||
*/ |
||||
void Array_ModuleInit() { |
||||
zend_class_entry tmp_ce; |
||||
zend_object_handlers *h; |
||||
|
||||
// RepeatedField.
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\RepeatedField", |
||||
repeated_field_methods); |
||||
|
||||
RepeatedField_class_entry = zend_register_internal_class(&tmp_ce); |
||||
zend_class_implements(RepeatedField_class_entry, 3, spl_ce_ArrayAccess, |
||||
zend_ce_aggregate, spl_ce_Countable); |
||||
RepeatedField_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
RepeatedField_class_entry->create_object = RepeatedField_create; |
||||
|
||||
h = &RepeatedField_object_handlers; |
||||
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
||||
h->dtor_obj = RepeatedField_destructor; |
||||
h->get_properties = RepeatedField_GetProperties; |
||||
h->get_property_ptr_ptr = RepeatedField_GetPropertyPtrPtr; |
||||
|
||||
// RepeatedFieldIter
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\RepeatedFieldIter", |
||||
repeated_field_iter_methods); |
||||
|
||||
RepeatedFieldIter_class_entry = zend_register_internal_class(&tmp_ce); |
||||
zend_class_implements(RepeatedFieldIter_class_entry, 1, zend_ce_iterator); |
||||
RepeatedFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
RepeatedFieldIter_class_entry->create_object = RepeatedFieldIter_create; |
||||
|
||||
h = &repeated_field_iter_object_handlers; |
||||
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
||||
h->dtor_obj = RepeatedFieldIter_dtor; |
||||
} |
@ -0,0 +1,61 @@ |
||||
// 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 PHP_PROTOBUF_ARRAY_H_ |
||||
#define PHP_PROTOBUF_ARRAY_H_ |
||||
|
||||
#include <php.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
// Registers PHP classes for RepeatedField.
|
||||
void Array_ModuleInit(); |
||||
|
||||
// Gets a upb_array* for the PHP object |val|:
|
||||
// * If |val| is a RepeatedField object, we first check its type and verify
|
||||
// that that the elements have the correct type for |f|. If so, we return the
|
||||
// wrapped upb_array*. We also make sure that this array's arena is fused to
|
||||
// |arena|, so the returned upb_array is guaranteed to live as long as
|
||||
// |arena|.
|
||||
// * If |val| is a PHP Array, we attempt to create a new upb_array using
|
||||
// |arena| and add all of the PHP elements to it.
|
||||
//
|
||||
// If an error occurs, we raise a PHP error and return NULL.
|
||||
upb_array *RepeatedField_GetUpbArray(zval *val, const upb_fielddef *f, upb_arena *arena); |
||||
|
||||
// Creates a PHP RepeatedField object for the given upb_array* and |f| and
|
||||
// returns it in |val|. The PHP object will keep a reference to this |arena| to
|
||||
// ensure the underlying array data stays alive.
|
||||
//
|
||||
// If |arr| is NULL, this will return a PHP null object.
|
||||
void RepeatedField_GetPhpWrapper(zval *val, upb_array *arr, |
||||
const upb_fielddef *f, zval *arena); |
||||
|
||||
#endif // PHP_PROTOBUF_ARRAY_H_
|
@ -0,0 +1,46 @@ |
||||
// 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 PHP_PROTOBUF_BUNDLED_PHP_H_ |
||||
#define PHP_PROTOBUF_BUNDLED_PHP_H_ |
||||
|
||||
// We embed PHP source code into the binary for things we don't want to
|
||||
// implement in C. This struct serves as a table of contents for all of
|
||||
// the embedded files.
|
||||
typedef struct { |
||||
const char *filename; |
||||
const char *contents; |
||||
} BundledPhp_File; |
||||
|
||||
// An array of all the embedded file structs. This array is terminated with a
|
||||
// {NULL, NULL} entry.
|
||||
extern BundledPhp_File *bundled_files; |
||||
|
||||
#endif // PHP_PROTOBUF_BUNDLED_PHP_H_
|
@ -0,0 +1,10 @@ |
||||
PHP_ARG_ENABLE(protobuf, whether to enable Protobuf extension, [ --enable-protobuf Enable Protobuf extension]) |
||||
|
||||
if test "$PHP_PROTOBUF" != "no"; then |
||||
|
||||
PHP_NEW_EXTENSION( |
||||
protobuf, |
||||
arena.c array.c bundled_php.c convert.c def.c map.c message.c names.c php-upb.c protobuf.c, |
||||
$ext_shared) |
||||
|
||||
fi |
@ -0,0 +1,478 @@ |
||||
// 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 "convert.h" |
||||
|
||||
#include <php.h> |
||||
|
||||
// This is not self-contained: it must be after other Zend includes.
|
||||
#include <Zend/zend_exceptions.h> |
||||
|
||||
#include "array.h" |
||||
#include "map.h" |
||||
#include "message.h" |
||||
#include "php-upb.h" |
||||
#include "protobuf.h" |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// GPBUtil
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static zend_class_entry* GPBUtil_class_entry; |
||||
|
||||
// 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).
|
||||
|
||||
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) {} |
||||
|
||||
// The result of checkMapField() is assigned, so we need to return the first
|
||||
// param:
|
||||
// $arr = GPBUtil::checkMapField($var,
|
||||
// \Google\Protobuf\Internal\GPBType::INT64,
|
||||
// \Google\Protobuf\Internal\GPBType::INT32);
|
||||
PHP_METHOD(Util, checkMapField) { |
||||
zval *val, *key_type, *val_type, *klass; |
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zzz|z", &val, &key_type, |
||||
&val_type, &klass) == FAILURE) { |
||||
return; |
||||
} |
||||
RETURN_ZVAL(val, 1, 0); |
||||
} |
||||
|
||||
// The result of checkRepeatedField() is assigned, so we need to return the
|
||||
// first param:
|
||||
// $arr = GPBUtil::checkRepeatedField(
|
||||
// $var, \Google\Protobuf\Internal\GPBType::STRING);
|
||||
PHP_METHOD(Util, checkRepeatedField) { |
||||
zval *val, *type, *klass; |
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz|z", &val, &type, &klass) == |
||||
FAILURE) { |
||||
return; |
||||
} |
||||
RETURN_ZVAL(val, 1, 0); |
||||
} |
||||
|
||||
static zend_function_entry util_methods[] = { |
||||
PHP_ME(Util, checkInt32, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkUint32, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkInt64, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkUint64, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkEnum, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkFloat, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkDouble, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkBool, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkString, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkBytes, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkMessage, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkMapField, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
PHP_ME(Util, checkRepeatedField, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Conversion functions used from C
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
upb_fieldtype_t pbphp_dtype_to_type(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); |
||||
|
||||
#undef CASE |
||||
|
||||
} |
||||
|
||||
zend_error(E_ERROR, "Unknown field type."); |
||||
return 0; |
||||
} |
||||
|
||||
static bool buftouint64(const char *ptr, const char *end, uint64_t *val) { |
||||
uint64_t u64 = 0; |
||||
while (ptr < end) { |
||||
unsigned ch = (unsigned)(*ptr - '0'); |
||||
if (ch >= 10) break; |
||||
if (u64 > UINT64_MAX / 10 || u64 * 10 > UINT64_MAX - ch) { |
||||
return false; |
||||
} |
||||
u64 *= 10; |
||||
u64 += ch; |
||||
ptr++; |
||||
} |
||||
|
||||
if (ptr != end) { |
||||
// In PHP tradition, we allow truncation: "1.1" -> 1.
|
||||
// But we don't allow 'e', eg. '1.1e2' or any other non-numeric chars.
|
||||
if (*ptr++ != '.') return false; |
||||
|
||||
for (;ptr < end; ptr++) { |
||||
if (*ptr < '0' || *ptr > '9') { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
*val = u64; |
||||
return true; |
||||
} |
||||
|
||||
static bool buftoint64(const char *ptr, const char *end, int64_t *val) { |
||||
bool neg = false; |
||||
uint64_t u64; |
||||
|
||||
if (ptr != end && *ptr == '-') { |
||||
ptr++; |
||||
neg = true; |
||||
} |
||||
|
||||
if (!buftouint64(ptr, end, &u64) || |
||||
u64 > (uint64_t)INT64_MAX + neg) { |
||||
return false; |
||||
} |
||||
|
||||
*val = neg ? -u64 : u64; |
||||
return true; |
||||
} |
||||
|
||||
static void throw_conversion_exception(const char *to, const zval *zv) { |
||||
zval tmp; |
||||
ZVAL_COPY(&tmp, zv); |
||||
convert_to_string(&tmp); |
||||
|
||||
zend_throw_exception_ex(NULL, 0, "Cannot convert '%s' to %s", |
||||
Z_STRVAL_P(&tmp), to); |
||||
|
||||
zval_ptr_dtor(&tmp); |
||||
} |
||||
|
||||
bool Convert_PhpToInt64(const zval *php_val, int64_t *i64) { |
||||
switch (Z_TYPE_P(php_val)) { |
||||
case IS_LONG: |
||||
*i64 = Z_LVAL_P(php_val); |
||||
return true; |
||||
case IS_DOUBLE: { |
||||
double dbl = Z_DVAL_P(php_val); |
||||
if (dbl > 9223372036854774784.0 || dbl < -9223372036854775808.0) { |
||||
zend_throw_exception_ex(NULL, 0, "Out of range"); |
||||
return false; |
||||
} |
||||
*i64 = dbl; /* must be guarded, overflow here is UB */ |
||||
return true; |
||||
} |
||||
case IS_STRING: { |
||||
const char *buf = Z_STRVAL_P(php_val); |
||||
// PHP would accept scientific notation here, but we're going to be a
|
||||
// little more discerning and only accept pure integers.
|
||||
bool ok = buftoint64(buf, buf + Z_STRLEN_P(php_val), i64); |
||||
if (!ok) { |
||||
throw_conversion_exception("integer", php_val); |
||||
} |
||||
return ok; |
||||
} |
||||
default: |
||||
throw_conversion_exception("integer", php_val); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
static bool to_double(zval *php_val, double *dbl) { |
||||
switch (Z_TYPE_P(php_val)) { |
||||
case IS_LONG: |
||||
*dbl = Z_LVAL_P(php_val); |
||||
return true; |
||||
case IS_DOUBLE: |
||||
*dbl = Z_DVAL_P(php_val); |
||||
return true; |
||||
case IS_STRING: { |
||||
zend_long lval; |
||||
switch (is_numeric_string(Z_STRVAL_P(php_val), Z_STRLEN_P(php_val), &lval, |
||||
dbl, false)) { |
||||
case IS_LONG: |
||||
*dbl = lval; |
||||
return true; |
||||
case IS_DOUBLE: |
||||
return true; |
||||
default: |
||||
goto fail; |
||||
} |
||||
} |
||||
default: |
||||
fail: |
||||
throw_conversion_exception("double", php_val); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
static bool to_bool(zval* from, bool* to) { |
||||
switch (Z_TYPE_P(from)) { |
||||
case IS_TRUE: |
||||
*to = true; |
||||
return true; |
||||
case IS_FALSE: |
||||
*to = false; |
||||
return true; |
||||
case IS_LONG: |
||||
*to = (Z_LVAL_P(from) != 0); |
||||
return true; |
||||
case IS_DOUBLE: |
||||
*to = (Z_LVAL_P(from) != 0); |
||||
return true; |
||||
case IS_STRING: |
||||
if (Z_STRLEN_P(from) == 0 || |
||||
(Z_STRLEN_P(from) == 1 && Z_STRVAL_P(from)[0] == '0')) { |
||||
*to = false; |
||||
} else { |
||||
*to = true; |
||||
} |
||||
return true; |
||||
default: |
||||
throw_conversion_exception("bool", from); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
static bool to_string(zval* from) { |
||||
if (Z_ISREF_P(from)) { |
||||
ZVAL_DEREF(from); |
||||
} |
||||
|
||||
switch (Z_TYPE_P(from)) { |
||||
case IS_STRING: |
||||
return true; |
||||
case IS_TRUE: |
||||
case IS_FALSE: |
||||
case IS_LONG: |
||||
case IS_DOUBLE: { |
||||
zval tmp; |
||||
zend_make_printable_zval(from, &tmp); |
||||
ZVAL_COPY_VALUE(from, &tmp); |
||||
return true; |
||||
} |
||||
default: |
||||
throw_conversion_exception("string", from); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
bool Convert_PhpToUpb(zval *php_val, upb_msgval *upb_val, upb_fieldtype_t type, |
||||
const Descriptor *desc, upb_arena *arena) { |
||||
int64_t i64; |
||||
|
||||
if (Z_ISREF_P(php_val)) { |
||||
ZVAL_DEREF(php_val); |
||||
} |
||||
|
||||
switch (type) { |
||||
case UPB_TYPE_INT64: |
||||
return Convert_PhpToInt64(php_val, &upb_val->int64_val); |
||||
case UPB_TYPE_INT32: |
||||
case UPB_TYPE_ENUM: |
||||
if (!Convert_PhpToInt64(php_val, &i64)) { |
||||
return false; |
||||
} |
||||
upb_val->int32_val = i64; |
||||
return true; |
||||
case UPB_TYPE_UINT64: |
||||
if (!Convert_PhpToInt64(php_val, &i64)) { |
||||
return false; |
||||
} |
||||
upb_val->uint64_val = i64; |
||||
return true; |
||||
case UPB_TYPE_UINT32: |
||||
if (!Convert_PhpToInt64(php_val, &i64)) { |
||||
return false; |
||||
} |
||||
upb_val->uint32_val = i64; |
||||
return true; |
||||
case UPB_TYPE_DOUBLE: |
||||
return to_double(php_val, &upb_val->double_val); |
||||
case UPB_TYPE_FLOAT: |
||||
if (!to_double(php_val, &upb_val->double_val)) return false; |
||||
upb_val->float_val = upb_val->double_val; |
||||
return true; |
||||
case UPB_TYPE_BOOL: |
||||
return to_bool(php_val, &upb_val->bool_val); |
||||
case UPB_TYPE_STRING: |
||||
case UPB_TYPE_BYTES: { |
||||
char *ptr; |
||||
size_t size; |
||||
|
||||
if (!to_string(php_val)) return false; |
||||
|
||||
size = Z_STRLEN_P(php_val); |
||||
|
||||
// If arena is NULL we reference the input zval.
|
||||
// The resulting upb_strview will only be value while the zval is alive.
|
||||
if (arena) { |
||||
ptr = upb_arena_malloc(arena, size); |
||||
memcpy(ptr, Z_STRVAL_P(php_val), size); |
||||
} else { |
||||
ptr = Z_STRVAL_P(php_val); |
||||
} |
||||
|
||||
upb_val->str_val = upb_strview_make(ptr, size); |
||||
return true; |
||||
} |
||||
case UPB_TYPE_MESSAGE: |
||||
PBPHP_ASSERT(desc); |
||||
return Message_GetUpbMessage(php_val, desc, arena, |
||||
(upb_msg **)&upb_val->msg_val); |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
void Convert_UpbToPhp(upb_msgval upb_val, zval *php_val, upb_fieldtype_t type, |
||||
const Descriptor *desc, zval *arena) { |
||||
switch (type) { |
||||
case UPB_TYPE_INT64: |
||||
#if SIZEOF_ZEND_LONG == 8 |
||||
ZVAL_LONG(php_val, upb_val.int64_val); |
||||
#else |
||||
{ |
||||
char buf[20]; |
||||
int size = sprintf(buf, "%lld", upb_val.int64_val); |
||||
ZVAL_NEW_STR(php_val, zend_string_init(buf, size, 0)); |
||||
} |
||||
#endif |
||||
break; |
||||
case UPB_TYPE_UINT64: |
||||
#if SIZEOF_ZEND_LONG == 8 |
||||
ZVAL_LONG(php_val, upb_val.uint64_val); |
||||
#else |
||||
{ |
||||
char buf[20]; |
||||
int size = sprintf(buf, "%lld", (int64_t)upb_val.uint64_val); |
||||
ZVAL_NEW_STR(php_val, zend_string_init(buf, size, 0)); |
||||
} |
||||
#endif |
||||
break; |
||||
case UPB_TYPE_INT32: |
||||
case UPB_TYPE_ENUM: |
||||
ZVAL_LONG(php_val, upb_val.int32_val); |
||||
break; |
||||
case UPB_TYPE_UINT32: { |
||||
// Sign-extend for consistency between 32/64-bit builds.
|
||||
zend_long val = (int32_t)upb_val.uint32_val; |
||||
ZVAL_LONG(php_val, val); |
||||
break; |
||||
} |
||||
case UPB_TYPE_DOUBLE: |
||||
ZVAL_DOUBLE(php_val, upb_val.double_val); |
||||
break; |
||||
case UPB_TYPE_FLOAT: |
||||
ZVAL_DOUBLE(php_val, upb_val.float_val); |
||||
break; |
||||
case UPB_TYPE_BOOL: |
||||
ZVAL_BOOL(php_val, upb_val.bool_val); |
||||
break; |
||||
case UPB_TYPE_STRING: |
||||
case UPB_TYPE_BYTES: { |
||||
upb_strview str = upb_val.str_val; |
||||
ZVAL_NEW_STR(php_val, zend_string_init(str.data, str.size, 0)); |
||||
break; |
||||
} |
||||
case UPB_TYPE_MESSAGE: |
||||
PBPHP_ASSERT(desc); |
||||
Message_GetPhpWrapper(php_val, desc, (upb_msg*)upb_val.msg_val, arena); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
bool Convert_PhpToUpbAutoWrap(zval *val, upb_msgval *upb_val, |
||||
upb_fieldtype_t type, const Descriptor *desc, |
||||
upb_arena *arena) { |
||||
const upb_msgdef *subm = desc ? desc->msgdef : NULL; |
||||
if (subm && upb_msgdef_iswrapper(subm) && Z_TYPE_P(val) != IS_OBJECT) { |
||||
// Assigning a scalar to a wrapper-typed value. We will automatically wrap
|
||||
// the value, so the user doesn't need to create a FooWrapper(['value': X])
|
||||
// message manually.
|
||||
upb_msg *wrapper = upb_msg_new(subm, arena); |
||||
const upb_fielddef *val_f = upb_msgdef_itof(subm, 1); |
||||
upb_fieldtype_t type_f = upb_fielddef_type(val_f); |
||||
upb_msgval msgval; |
||||
if (!Convert_PhpToUpb(val, &msgval, type_f, NULL, arena)) return false; |
||||
upb_msg_set(wrapper, val_f, msgval, arena); |
||||
upb_val->msg_val = wrapper; |
||||
return true; |
||||
} else { |
||||
// Convert_PhpToUpb doesn't auto-construct messages. This means that we only
|
||||
// allow:
|
||||
// ['foo_submsg': new Foo(['a' => 1])]
|
||||
// not:
|
||||
// ['foo_submsg': ['a' => 1]]
|
||||
return Convert_PhpToUpb(val, upb_val, type, desc, arena); |
||||
} |
||||
} |
||||
|
||||
void Convert_ModuleInit(void) { |
||||
const char *prefix_name = "TYPE_URL_PREFIX"; |
||||
zend_class_entry class_type; |
||||
|
||||
INIT_CLASS_ENTRY(class_type, "Google\\Protobuf\\Internal\\GPBUtil", |
||||
util_methods); |
||||
GPBUtil_class_entry = zend_register_internal_class(&class_type); |
||||
|
||||
zend_declare_class_constant_string(GPBUtil_class_entry, prefix_name, |
||||
strlen(prefix_name), |
||||
"type.googleapis.com/"); |
||||
} |
@ -0,0 +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 PHP_PROTOBUF_CONVERT_H_ |
||||
#define PHP_PROTOBUF_CONVERT_H_ |
||||
|
||||
#include <php.h> |
||||
|
||||
#include "php-upb.h" |
||||
#include "def.h" |
||||
|
||||
upb_fieldtype_t pbphp_dtype_to_type(upb_descriptortype_t type); |
||||
|
||||
// Converts |php_val| to an int64_t. Returns false if the value cannot be
|
||||
// converted.
|
||||
bool Convert_PhpToInt64(const zval *php_val, int64_t *i64); |
||||
|
||||
// Converts |php_val| to a upb_msgval according to |type|. If type is
|
||||
// UPB_TYPE_MESSAGE, then |desc| must be the Descriptor for this message type.
|
||||
// If type is string, message, or bytes, then |arena| will be used to copy
|
||||
// string data or fuse this arena to the given message's arena.
|
||||
bool Convert_PhpToUpb(zval *php_val, upb_msgval *upb_val, upb_fieldtype_t type, |
||||
const Descriptor *desc, upb_arena *arena); |
||||
|
||||
// Similar to Convert_PhpToUpb, but supports automatically wrapping the wrapper
|
||||
// types if a primitive is specified:
|
||||
//
|
||||
// 5 -> Int64Wrapper(value=5)
|
||||
//
|
||||
// We currently allow this implicit conversion in initializers, but not for
|
||||
// assignment.
|
||||
bool Convert_PhpToUpbAutoWrap(zval *val, upb_msgval *upb_val, |
||||
upb_fieldtype_t type, const Descriptor *desc, |
||||
upb_arena *arena); |
||||
|
||||
// Converts |upb_val| to a PHP zval according to |type|. This may involve
|
||||
// creating a PHP wrapper object. If type == UPB_TYPE_MESSAGE, then |desc| must
|
||||
// be the Descriptor for this message type. Any newly created wrapper object
|
||||
// will reference |arena|.
|
||||
void Convert_UpbToPhp(upb_msgval upb_val, zval *php_val, upb_fieldtype_t type, |
||||
const Descriptor *desc, zval *arena); |
||||
|
||||
// Registers the GPBUtil class.
|
||||
void Convert_ModuleInit(void); |
||||
|
||||
#endif // PHP_PROTOBUF_CONVERT_H_
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,69 @@ |
||||
// 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 PHP_PROTOBUF_DEF_H_ |
||||
#define PHP_PROTOBUF_DEF_H_ |
||||
|
||||
#include <php.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
// Initializes the Def module, which defines all of the descriptor classes.
|
||||
void Def_ModuleInit(); |
||||
|
||||
// Creates a new DescriptorPool to wrap the given symtab. The DescriptorPool
|
||||
// takes ownership of the given symtab. If symtab is NULL, the DescriptorPool
|
||||
// will create an empty symtab instead.
|
||||
void DescriptorPool_CreateWithSymbolTable(zval *zv, upb_symtab *symtab); |
||||
|
||||
// Given a zval representing a DescriptorPool, steals and returns its symtab,
|
||||
// which is now owned by the caller.
|
||||
upb_symtab *DescriptorPool_Steal(zval *zv); |
||||
|
||||
upb_symtab *DescriptorPool_GetSymbolTable(); |
||||
|
||||
typedef struct Descriptor { |
||||
zend_object std; |
||||
const upb_msgdef *msgdef; |
||||
zend_class_entry *class_entry; |
||||
} Descriptor; |
||||
|
||||
// Gets or creates a PHP Descriptor object for a |ce| and stores it in |val|.
|
||||
// If this is not a protobuf generated class, |val| will be set to null.
|
||||
void Descriptor_FromClassEntry(zval *val, zend_class_entry *ce); |
||||
|
||||
// Gets or creates a Descriptor* for the given class entry, upb_msgdef, or
|
||||
// upb_fielddef. The returned Descriptor* will live for the entire request,
|
||||
// so no ref is necessary to keep it alive.
|
||||
Descriptor* Descriptor_GetFromClassEntry(zend_class_entry *ce); |
||||
Descriptor* Descriptor_GetFromMessageDef(const upb_msgdef *m); |
||||
Descriptor* Descriptor_GetFromFieldDef(const upb_fielddef *f); |
||||
|
||||
#endif // PHP_PROTOBUF_DEF_H_
|
@ -0,0 +1,62 @@ |
||||
<?php |
||||
|
||||
$cwd = dirname($argv[0]) . "/../../../src"; |
||||
chdir($cwd); |
||||
|
||||
$cmd = "grep -r -l 'Generated by the protocol buffer' * | grep 'php$' | grep -v Internal"; |
||||
$handle = popen($cmd, 'r'); |
||||
$filenames = explode("\n", stream_get_contents($handle)); |
||||
array_pop($filenames); // empty string after last '\n' |
||||
$filenames[] = "Google/Protobuf/DescriptorPool.php"; |
||||
$output = "../ext/google/protobuf2/bundled_php.c"; |
||||
|
||||
function stripSuffix($str, $suffix) { |
||||
return substr($str, 0, strlen($str) - strlen($suffix)); |
||||
} |
||||
|
||||
function toClassName($filename) { |
||||
# Google/Protobuf/BoolValue.php -> Google\\Protobuf\\BoolValue |
||||
$ret = stripSuffix($filename, ".php"); |
||||
return str_replace("/", "\\\\", $ret); |
||||
} |
||||
|
||||
function toCSymbolName($filename) { |
||||
# Google/Protobuf/BoolValue.php -> Google__Protobuf__BoolValue |
||||
$ret = stripSuffix($filename, ".php"); |
||||
return str_replace("/", "__", $ret); |
||||
} |
||||
|
||||
$f = fopen($output, "w"); |
||||
|
||||
fwrite($f, "#include \"bundled_php.h\"\n"); |
||||
fwrite($f, "#include \"stdlib.h\"\n"); |
||||
|
||||
foreach ($filenames as $filename) { |
||||
print("Reading $filename...\n"); |
||||
$contents = file_get_contents($filename); |
||||
$contents = substr($contents, 5); // Strip <?php |
||||
$c_symbol_name = toCSymbolName($filename); |
||||
fwrite($f, "static const char {$c_symbol_name}[] = {"); |
||||
for ($i = 0; $i < strlen($contents); $i++) { |
||||
if ($i % 10 == 0) { |
||||
fwrite($f, "\n"); |
||||
} |
||||
fprintf($f, " 0x%02x,", ord($contents[$i])); |
||||
} |
||||
fwrite($f, "0};\n"); |
||||
} |
||||
|
||||
fwrite($f, "static BundledPhp_File php[] = {\n"); |
||||
foreach ($filenames as $filename) { |
||||
$class_name = toClassName($filename); |
||||
$c_symbol_name = toCSymbolName($filename); |
||||
fwrite($f, " {\"$class_name\", $c_symbol_name},\n"); |
||||
} |
||||
|
||||
fwrite($f, " {NULL, NULL}\n"); |
||||
fwrite($f, "};\n"); |
||||
fwrite($f, "BundledPhp_File *bundled_files = &php[0];\n"); |
||||
fclose($f); |
||||
|
||||
print("Wrote $output\n"); |
||||
?> |
@ -0,0 +1,590 @@ |
||||
// 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 "map.h" |
||||
|
||||
#include <Zend/zend_API.h> |
||||
#include <Zend/zend_interfaces.h> |
||||
|
||||
#include <ext/spl/spl_iterators.h> |
||||
|
||||
#include "arena.h" |
||||
#include "convert.h" |
||||
#include "php-upb.h" |
||||
#include "protobuf.h" |
||||
|
||||
static void MapFieldIter_make(zval *val, zval *map_field); |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// MapField
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct { |
||||
zend_object std; |
||||
zval arena; |
||||
upb_map *map; |
||||
upb_fieldtype_t key_type; |
||||
upb_fieldtype_t val_type; |
||||
const Descriptor* desc; // When values are messages.
|
||||
} MapField; |
||||
|
||||
zend_class_entry *MapField_class_entry; |
||||
static zend_object_handlers MapField_object_handlers; |
||||
|
||||
// PHP Object Handlers /////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* MapField_create() |
||||
* |
||||
* PHP class entry function to allocate and initialize a new MapField |
||||
* object. |
||||
*/ |
||||
static zend_object* MapField_create(zend_class_entry *class_type) { |
||||
MapField *intern = emalloc(sizeof(MapField)); |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &MapField_object_handlers; |
||||
Arena_Init(&intern->arena); |
||||
intern->map = NULL; |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
return &intern->std; |
||||
} |
||||
|
||||
/**
|
||||
* MapField_dtor() |
||||
* |
||||
* Object handler to destroy a MapField. This releases all resources |
||||
* associated with the message. Note that it is possible to access a destroyed |
||||
* object from PHP in rare cases. |
||||
*/ |
||||
static void MapField_destructor(zend_object* obj) { |
||||
MapField* intern = (MapField*)obj; |
||||
ObjCache_Delete(intern->map); |
||||
zval_ptr_dtor(&intern->arena); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
static zval *Map_GetPropertyPtrPtr(zval *object, zval *member, int type, |
||||
void **cache_slot) { |
||||
return NULL; // We don't offer direct references to our properties.
|
||||
} |
||||
|
||||
static HashTable *map_get_properties(zval *object TSRMLS_DC) { |
||||
return NULL; // We do not have a properties table.
|
||||
} |
||||
|
||||
// C Functions from map.h //////////////////////////////////////////////////////
|
||||
|
||||
// These are documented in the header file.
|
||||
|
||||
void MapField_GetPhpWrapper(zval *val, upb_map *map, const upb_fielddef *f, |
||||
zval *arena) { |
||||
if (!map) { |
||||
ZVAL_NULL(val); |
||||
return; |
||||
} |
||||
|
||||
if (!ObjCache_Get(map, val)) { |
||||
const upb_msgdef *ent = upb_fielddef_msgsubdef(f); |
||||
const upb_fielddef *key_f = upb_msgdef_itof(ent, 1); |
||||
const upb_fielddef *val_f = upb_msgdef_itof(ent, 2); |
||||
MapField *intern = emalloc(sizeof(MapField)); |
||||
zend_object_std_init(&intern->std, MapField_class_entry); |
||||
intern->std.handlers = &MapField_object_handlers; |
||||
ZVAL_COPY(&intern->arena, arena); |
||||
intern->map = map; |
||||
intern->key_type = upb_fielddef_type(key_f); |
||||
intern->val_type = upb_fielddef_type(val_f); |
||||
intern->desc = Descriptor_GetFromFieldDef(val_f); |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
ObjCache_Add(intern->map, &intern->std); |
||||
ZVAL_OBJ(val, &intern->std); |
||||
} |
||||
} |
||||
|
||||
upb_map *MapField_GetUpbMap(zval *val, const upb_fielddef *f, upb_arena *arena) { |
||||
const upb_msgdef *ent = upb_fielddef_msgsubdef(f); |
||||
const upb_fielddef *key_f = upb_msgdef_itof(ent, 1); |
||||
const upb_fielddef *val_f = upb_msgdef_itof(ent, 2); |
||||
upb_fieldtype_t key_type = upb_fielddef_type(key_f); |
||||
upb_fieldtype_t val_type = upb_fielddef_type(val_f); |
||||
const Descriptor *desc = Descriptor_GetFromFieldDef(val_f); |
||||
|
||||
if (Z_ISREF_P(val)) { |
||||
ZVAL_DEREF(val); |
||||
} |
||||
|
||||
if (Z_TYPE_P(val) == IS_ARRAY) { |
||||
upb_map *map = upb_map_new(arena, key_type, val_type); |
||||
HashTable *table = HASH_OF(val); |
||||
HashPosition pos; |
||||
|
||||
zend_hash_internal_pointer_reset_ex(table, &pos); |
||||
|
||||
while (true) { |
||||
zval php_key; |
||||
zval *php_val; |
||||
upb_msgval upb_key; |
||||
upb_msgval upb_val; |
||||
|
||||
zend_hash_get_current_key_zval_ex(table, &php_key, &pos); |
||||
php_val = zend_hash_get_current_data_ex(table, &pos); |
||||
|
||||
if (!php_val) return map; |
||||
|
||||
if (!Convert_PhpToUpb(&php_key, &upb_key, key_type, NULL, arena) || |
||||
!Convert_PhpToUpbAutoWrap(php_val, &upb_val, val_type, desc, arena)) { |
||||
return NULL; |
||||
} |
||||
|
||||
upb_map_set(map, upb_key, upb_val, arena); |
||||
zend_hash_move_forward_ex(table, &pos); |
||||
zval_dtor(&php_key); |
||||
} |
||||
} else if (Z_TYPE_P(val) == IS_OBJECT && |
||||
Z_OBJCE_P(val) == MapField_class_entry) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(val); |
||||
|
||||
if (intern->key_type != key_type || intern->val_type != val_type || |
||||
intern->desc != desc) { |
||||
php_error_docref(NULL, E_USER_ERROR, "Wrong type for this map field."); |
||||
return NULL; |
||||
} |
||||
|
||||
upb_arena_fuse(arena, Arena_Get(&intern->arena)); |
||||
return intern->map; |
||||
} else { |
||||
php_error_docref(NULL, E_USER_ERROR, "Must be a map"); |
||||
return NULL; |
||||
} |
||||
} |
||||
|
||||
// MapField PHP methods ////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* MapField::__construct() |
||||
* |
||||
* Constructs an instance of MapField. |
||||
* @param long Key type. |
||||
* @param long Value type. |
||||
* @param string Message/Enum class (message/enum value types only). |
||||
*/ |
||||
PHP_METHOD(MapField, __construct) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
zend_long key_type, val_type; |
||||
zend_class_entry* klass = NULL; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll|C", &key_type, &val_type, |
||||
&klass) != SUCCESS) { |
||||
return; |
||||
} |
||||
|
||||
intern->key_type = pbphp_dtype_to_type(key_type); |
||||
intern->val_type = pbphp_dtype_to_type(val_type); |
||||
intern->desc = Descriptor_GetFromClassEntry(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."); |
||||
} |
||||
|
||||
if (intern->val_type == UPB_TYPE_MESSAGE && klass == NULL) { |
||||
php_error_docref(NULL, E_USER_ERROR, |
||||
"Message/enum type must have concrete class."); |
||||
return; |
||||
} |
||||
|
||||
intern->map = upb_map_new(arena, intern->key_type, intern->val_type); |
||||
ObjCache_Add(intern->map, &intern->std); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::offsetExists() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* isset($map[$idx]); |
||||
* empty($map[$idx]); |
||||
* |
||||
* @param long The index to be checked. |
||||
* @return bool True if the element at the given index exists. |
||||
*/ |
||||
PHP_METHOD(MapField, offsetExists) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
zval *key; |
||||
upb_msgval upb_key; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS || |
||||
!Convert_PhpToUpb(key, &upb_key, intern->key_type, intern->desc, NULL)) { |
||||
return; |
||||
} |
||||
|
||||
RETURN_BOOL(upb_map_get(intern->map, upb_key, NULL)); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::offsetGet() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* $x = $map[$idx]; |
||||
* |
||||
* @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(MapField, offsetGet) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
zval *key; |
||||
zval ret; |
||||
upb_msgval upb_key, upb_val; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS || |
||||
!Convert_PhpToUpb(key, &upb_key, intern->key_type, intern->desc, NULL)) { |
||||
return; |
||||
} |
||||
|
||||
if (!upb_map_get(intern->map, upb_key, &upb_val)) { |
||||
zend_error(E_USER_ERROR, "Given key doesn't exist."); |
||||
return; |
||||
} |
||||
|
||||
Convert_UpbToPhp(upb_val, &ret, intern->val_type, intern->desc, &intern->arena); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::offsetSet() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* $map[$idx] = $x; |
||||
* |
||||
* @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(MapField, offsetSet) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
zval *key, *val; |
||||
upb_msgval upb_key, upb_val; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &key, &val) != SUCCESS || |
||||
!Convert_PhpToUpb(key, &upb_key, intern->key_type, NULL, NULL) || |
||||
!Convert_PhpToUpb(val, &upb_val, intern->val_type, intern->desc, arena)) { |
||||
return; |
||||
} |
||||
|
||||
upb_map_set(intern->map, upb_key, upb_val, arena); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::offsetUnset() |
||||
* |
||||
* Implements the ArrayAccess interface. Invoked when PHP code calls: |
||||
* |
||||
* unset($map[$idx]); |
||||
* |
||||
* @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 MapField. |
||||
*/ |
||||
PHP_METHOD(MapField, offsetUnset) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
zval *key; |
||||
upb_msgval upb_key; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &key) != SUCCESS || |
||||
!Convert_PhpToUpb(key, &upb_key, intern->key_type, NULL, NULL)) { |
||||
return; |
||||
} |
||||
|
||||
upb_map_delete(intern->map, upb_key); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::count() |
||||
* |
||||
* Implements the Countable interface. Invoked when PHP code calls: |
||||
* |
||||
* $len = count($map); |
||||
* Return the number of stored elements. |
||||
* This will also be called for: count($map) |
||||
* @return long The number of stored elements. |
||||
*/ |
||||
PHP_METHOD(MapField, count) { |
||||
MapField *intern = (MapField*)Z_OBJ_P(getThis()); |
||||
|
||||
if (zend_parse_parameters_none() == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
RETURN_LONG(upb_map_size(intern->map)); |
||||
} |
||||
|
||||
/**
|
||||
* MapField::getIterator() |
||||
* |
||||
* Implements the IteratorAggregate interface. Invoked when PHP code calls: |
||||
* |
||||
* foreach ($arr) {} |
||||
* |
||||
* @return object Beginning iterator. |
||||
*/ |
||||
PHP_METHOD(MapField, getIterator) { |
||||
zval ret; |
||||
MapFieldIter_make(&ret, getThis()); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
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 MapField_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) |
||||
PHP_ME(MapField, getIterator, arginfo_void, ZEND_ACC_PUBLIC) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// MapFieldIter
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct { |
||||
zend_object std; |
||||
zval map_field; |
||||
size_t position; |
||||
} MapFieldIter; |
||||
|
||||
zend_class_entry *MapFieldIter_class_entry; |
||||
static zend_object_handlers MapFieldIter_object_handlers; |
||||
|
||||
/**
|
||||
* MapFieldIter_create() |
||||
* |
||||
* PHP class entry function to allocate and initialize a new MapFieldIter |
||||
* object. |
||||
*/ |
||||
zend_object* MapFieldIter_create(zend_class_entry *class_type) { |
||||
MapFieldIter *intern = emalloc(sizeof(MapFieldIter)); |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &MapFieldIter_object_handlers; |
||||
ZVAL_NULL(&intern->map_field); |
||||
intern->position = 0; |
||||
// Skip object_properties_init(), we don't allow derived classes.
|
||||
return &intern->std; |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter_dtor() |
||||
* |
||||
* Object handler to destroy a MapFieldIter. This releases all resources |
||||
* associated with the message. Note that it is possible to access a destroyed |
||||
* object from PHP in rare cases. |
||||
*/ |
||||
static void map_field_iter_dtor(zend_object* obj) { |
||||
MapFieldIter* intern = (MapFieldIter*)obj; |
||||
zval_ptr_dtor(&intern->map_field); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter_make() |
||||
* |
||||
* Function to create a MapFieldIter directly from C. |
||||
*/ |
||||
static void MapFieldIter_make(zval *val, zval *map_field) { |
||||
MapFieldIter *iter; |
||||
ZVAL_OBJ(val, |
||||
MapFieldIter_class_entry->create_object(MapFieldIter_class_entry)); |
||||
iter = (MapFieldIter*)Z_OBJ_P(val); |
||||
ZVAL_COPY(&iter->map_field, map_field); |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PHP MapFieldIter Methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* When a user writes: |
||||
* |
||||
* foreach($arr as $key => $val) {} |
||||
* |
||||
* PHP translates this into: |
||||
* |
||||
* $iter = $arr->getIterator(); |
||||
* for ($iter->rewind(); $iter->valid(); $iter->next()) { |
||||
* $key = $iter->key(); |
||||
* $val = $iter->current(); |
||||
* } |
||||
*/ |
||||
|
||||
/**
|
||||
* MapFieldIter::rewind() |
||||
* |
||||
* Implements the Iterator interface. Sets the iterator to the first element. |
||||
*/ |
||||
PHP_METHOD(MapFieldIter, rewind) { |
||||
MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis()); |
||||
MapField *map_field = (MapField*)Z_OBJ_P(&intern->map_field); |
||||
intern->position = UPB_MAP_BEGIN; |
||||
upb_mapiter_next(map_field->map, &intern->position); |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter::current() |
||||
* |
||||
* Implements the Iterator interface. Returns the current value. |
||||
*/ |
||||
PHP_METHOD(MapFieldIter, current) { |
||||
MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis()); |
||||
MapField *field = (MapField*)Z_OBJ_P(&intern->map_field); |
||||
upb_msgval upb_val = upb_mapiter_value(field->map, intern->position); |
||||
zval ret; |
||||
Convert_UpbToPhp(upb_val, &ret, field->val_type, field->desc, &field->arena); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter::key() |
||||
* |
||||
* Implements the Iterator interface. Returns the current key. |
||||
*/ |
||||
PHP_METHOD(MapFieldIter, key) { |
||||
MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis()); |
||||
MapField *field = (MapField*)Z_OBJ_P(&intern->map_field); |
||||
upb_msgval upb_key = upb_mapiter_key(field->map, intern->position); |
||||
zval ret; |
||||
Convert_UpbToPhp(upb_key, &ret, field->key_type, NULL, NULL); |
||||
RETURN_ZVAL(&ret, 0, 1); |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter::next() |
||||
* |
||||
* Implements the Iterator interface. Advances to the next element. |
||||
*/ |
||||
PHP_METHOD(MapFieldIter, next) { |
||||
MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis()); |
||||
MapField *field = (MapField*)Z_OBJ_P(&intern->map_field); |
||||
upb_mapiter_next(field->map, &intern->position); |
||||
} |
||||
|
||||
/**
|
||||
* MapFieldIter::valid() |
||||
* |
||||
* Implements the Iterator interface. Returns true if this is a valid element. |
||||
*/ |
||||
PHP_METHOD(MapFieldIter, valid) { |
||||
MapFieldIter *intern = (MapFieldIter*)Z_OBJ_P(getThis()); |
||||
MapField *field = (MapField*)Z_OBJ_P(&intern->map_field); |
||||
bool done = upb_mapiter_done(field->map, intern->position); |
||||
RETURN_BOOL(!done); |
||||
} |
||||
|
||||
static zend_function_entry map_field_iter_methods[] = { |
||||
PHP_ME(MapFieldIter, rewind, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapFieldIter, current, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapFieldIter, key, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapFieldIter, next, arginfo_void, ZEND_ACC_PUBLIC) |
||||
PHP_ME(MapFieldIter, valid, arginfo_void, ZEND_ACC_PUBLIC) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Module init.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Map_ModuleInit() |
||||
* |
||||
* Called when the C extension is loaded to register all types. |
||||
*/ |
||||
|
||||
void Map_ModuleInit() { |
||||
zend_class_entry tmp_ce; |
||||
zend_object_handlers *h; |
||||
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\MapField", |
||||
MapField_methods); |
||||
|
||||
MapField_class_entry = zend_register_internal_class(&tmp_ce); |
||||
zend_class_implements(MapField_class_entry, 3, spl_ce_ArrayAccess, |
||||
zend_ce_aggregate, spl_ce_Countable); |
||||
MapField_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
MapField_class_entry->create_object = MapField_create; |
||||
|
||||
h = &MapField_object_handlers; |
||||
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
||||
h->dtor_obj = MapField_destructor; |
||||
h->get_properties = map_get_properties; |
||||
h->get_property_ptr_ptr = Map_GetPropertyPtrPtr; |
||||
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\MapFieldIter", |
||||
map_field_iter_methods); |
||||
|
||||
MapFieldIter_class_entry = zend_register_internal_class(&tmp_ce); |
||||
zend_class_implements(MapFieldIter_class_entry, 1, zend_ce_iterator); |
||||
MapFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
MapFieldIter_class_entry->ce_flags |= ZEND_ACC_FINAL; |
||||
MapFieldIter_class_entry->create_object = MapFieldIter_create; |
||||
|
||||
h = &MapFieldIter_object_handlers; |
||||
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
||||
h->dtor_obj = map_field_iter_dtor; |
||||
} |
@ -0,0 +1,60 @@ |
||||
// 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 PHP_PROTOBUF_MAP_H_ |
||||
#define PHP_PROTOBUF_MAP_H_ |
||||
|
||||
#include <php.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
void Map_ModuleInit(); |
||||
|
||||
// Gets a upb_map* for the PHP object |val|:
|
||||
// * If |val| is a RepeatedField object, we first check its type and verify
|
||||
// that that the elements have the correct type for |f|. If so, we return the
|
||||
// wrapped upb_map*. We also make sure that this map's arena is fused to
|
||||
// |arena|, so the returned upb_map is guaranteed to live as long as
|
||||
// |arena|.
|
||||
// * If |val| is a PHP Map, we attempt to create a new upb_map using
|
||||
// |arena| and add all of the PHP elements to it.
|
||||
//
|
||||
// If an error occurs, we raise a PHP error and return NULL.
|
||||
upb_map *MapField_GetUpbMap(zval *val, const upb_fielddef *f, upb_arena *arena); |
||||
|
||||
// Creates a PHP MapField object for the given upb_map* and |f| and returns it
|
||||
// in |val|. The PHP object will keep a reference to this |arena| to ensure the
|
||||
// underlying array data stays alive.
|
||||
//
|
||||
// If |map| is NULL, this will return a PHP null object.
|
||||
void MapField_GetPhpWrapper(zval *val, upb_map *arr, const upb_fielddef *f, |
||||
zval *arena); |
||||
|
||||
#endif // PHP_PROTOBUF_MAP_H_
|
@ -0,0 +1,841 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2014 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 "message.h" |
||||
|
||||
#include <inttypes.h> |
||||
#include <php.h> |
||||
#include <stdlib.h> |
||||
|
||||
// This is not self-contained: it must be after other Zend includes.
|
||||
#include <Zend/zend_exceptions.h> |
||||
|
||||
#include "arena.h" |
||||
#include "array.h" |
||||
#include "convert.h" |
||||
#include "def.h" |
||||
#include "map.h" |
||||
#include "php-upb.h" |
||||
#include "protobuf.h" |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Message
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
typedef struct { |
||||
zend_object std; |
||||
zval arena; |
||||
const Descriptor* desc; |
||||
upb_msg *msg; |
||||
} Message; |
||||
|
||||
zend_class_entry *message_ce; |
||||
static zend_object_handlers message_object_handlers; |
||||
|
||||
// PHP Object Handlers /////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Message_create() |
||||
* |
||||
* PHP class entry function to allocate and initialize a new Message object. |
||||
*/ |
||||
static zend_object* Message_create(zend_class_entry *class_type) { |
||||
Message *intern = emalloc(sizeof(Message)); |
||||
// XXX(haberman): verify whether we actually want to take this route.
|
||||
class_type->default_properties_count = 0; |
||||
zend_object_std_init(&intern->std, class_type); |
||||
intern->std.handlers = &message_object_handlers; |
||||
Arena_Init(&intern->arena); |
||||
return &intern->std; |
||||
} |
||||
|
||||
/**
|
||||
* Message_dtor() |
||||
* |
||||
* Object handler to destroy a Message. This releases all resources associated |
||||
* with the message. Note that it is possible to access a destroyed object from |
||||
* PHP in rare cases. |
||||
*/ |
||||
static void Message_dtor(zend_object* obj) { |
||||
Message* intern = (Message*)obj; |
||||
ObjCache_Delete(intern->msg); |
||||
zval_dtor(&intern->arena); |
||||
zend_object_std_dtor(&intern->std); |
||||
} |
||||
|
||||
/**
|
||||
* get_field() |
||||
* |
||||
* Helper function to look up a field given a member name (as a string). |
||||
*/ |
||||
static const upb_fielddef *get_field(Message *msg, zval *member) { |
||||
const upb_msgdef *m = msg->desc->msgdef; |
||||
const upb_fielddef *f = |
||||
upb_msgdef_ntof(m, Z_STRVAL_P(member), Z_STRLEN_P(member)); |
||||
|
||||
if (!f) { |
||||
zend_throw_exception_ex(NULL, 0, "No such property %s.", |
||||
ZSTR_VAL(msg->desc->class_entry->name)); |
||||
} |
||||
|
||||
return f; |
||||
} |
||||
|
||||
/**
|
||||
* Message_read_property() |
||||
* |
||||
* Object handler for reading a property in PHP. Called when PHP code does: |
||||
* |
||||
* $x = $message->foobar; |
||||
* |
||||
* Note that all properties of generated messages are private, so this should |
||||
* only be possible to invoke from generated code, which has accessors like: |
||||
* |
||||
* public function getOptionalInt32() |
||||
* { |
||||
* return $this->optional_int32; |
||||
* } |
||||
* |
||||
* We lookup the field and return the scalar, RepeatedField, or MapField for |
||||
* this field. |
||||
*/ |
||||
static zval *Message_read_property(zval *obj, zval *member, int type, |
||||
void **cache_slot, zval *rv) { |
||||
Message* intern = (Message*)Z_OBJ_P(obj); |
||||
const upb_fielddef *f = get_field(intern, member); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
|
||||
if (!f) return NULL; |
||||
|
||||
if (upb_fielddef_ismap(f)) { |
||||
upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena); |
||||
MapField_GetPhpWrapper(rv, msgval.map, f, &intern->arena); |
||||
} else if (upb_fielddef_isseq(f)) { |
||||
upb_mutmsgval msgval = upb_msg_mutable(intern->msg, f, arena); |
||||
RepeatedField_GetPhpWrapper(rv, msgval.array, f, &intern->arena); |
||||
} else { |
||||
upb_msgval msgval = upb_msg_get(intern->msg, f); |
||||
const Descriptor *subdesc = Descriptor_GetFromFieldDef(f); |
||||
Convert_UpbToPhp(msgval, rv, upb_fielddef_type(f), subdesc, &intern->arena); |
||||
} |
||||
|
||||
return rv; |
||||
} |
||||
|
||||
/**
|
||||
* Message_write_property() |
||||
* |
||||
* Object handler for writing a property in PHP. Called when PHP code does: |
||||
* |
||||
* $message->foobar = $x; |
||||
* |
||||
* Note that all properties of generated messages are private, so this should |
||||
* only be possible to invoke from generated code, which has accessors like: |
||||
* |
||||
* public function setOptionalInt32($var) |
||||
* { |
||||
* GPBUtil::checkInt32($var); |
||||
* $this->optional_int32 = $var; |
||||
* |
||||
* return $this; |
||||
* } |
||||
* |
||||
* The C extension version of checkInt32() doesn't actually check anything, so |
||||
* we perform all checking and conversion in this function. |
||||
*/ |
||||
static void Message_write_property(zval *obj, zval *member, zval *val, |
||||
void **cache_slot) { |
||||
Message* intern = (Message*)Z_OBJ_P(obj); |
||||
const upb_fielddef *f = get_field(intern, member); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
upb_msgval msgval; |
||||
|
||||
if (!f) return; |
||||
|
||||
if (upb_fielddef_ismap(f)) { |
||||
msgval.map_val = MapField_GetUpbMap(val, f, arena); |
||||
if (!msgval.map_val) return; |
||||
} else if (upb_fielddef_isseq(f)) { |
||||
msgval.array_val = RepeatedField_GetUpbArray(val, f, arena); |
||||
if (!msgval.array_val) return; |
||||
} else { |
||||
upb_fieldtype_t type = upb_fielddef_type(f); |
||||
const Descriptor *subdesc = Descriptor_GetFromFieldDef(f); |
||||
bool ok = Convert_PhpToUpb(val, &msgval, type, subdesc, arena); |
||||
if (!ok) return; |
||||
} |
||||
|
||||
upb_msg_set(intern->msg, f, msgval, arena); |
||||
} |
||||
|
||||
/**
|
||||
* Message_get_property_ptr_ptr() |
||||
* |
||||
* Object handler for the get_property_ptr_ptr event in PHP. This returns a |
||||
* reference to our internal properties. We don't support this, so we return |
||||
* NULL. |
||||
*/ |
||||
static zval *Message_get_property_ptr_ptr(zval *object, zval *member, int type, |
||||
void **cache_slot) { |
||||
return NULL; // We do not have a properties table.
|
||||
} |
||||
|
||||
/**
|
||||
* Message_get_properties() |
||||
* |
||||
* Object handler for the get_properties event in PHP. This returns a HashTable |
||||
* of our internal properties. We don't support this, so we return NULL. |
||||
*/ |
||||
static HashTable* Message_get_properties(zval* object TSRMLS_DC) { |
||||
return NULL; // We don't offer direct references to our properties.
|
||||
} |
||||
|
||||
// C Functions from message.h. /////////////////////////////////////////////////
|
||||
|
||||
// These are documented in the header file.
|
||||
|
||||
void Message_GetPhpWrapper(zval *val, const Descriptor *desc, upb_msg *msg, |
||||
zval *arena) { |
||||
if (!msg) { |
||||
ZVAL_NULL(val); |
||||
return; |
||||
} |
||||
|
||||
if (!ObjCache_Get(msg, val)) { |
||||
Message *intern = emalloc(sizeof(Message)); |
||||
// XXX(haberman): verify whether we actually want to take this route.
|
||||
desc->class_entry->default_properties_count = 0; |
||||
zend_object_std_init(&intern->std, desc->class_entry); |
||||
intern->std.handlers = &message_object_handlers; |
||||
ZVAL_COPY(&intern->arena, arena); |
||||
intern->desc = desc; |
||||
intern->msg = msg; |
||||
ZVAL_OBJ(val, &intern->std); |
||||
ObjCache_Add(intern->msg, &intern->std); |
||||
} |
||||
} |
||||
|
||||
bool Message_GetUpbMessage(zval *val, const Descriptor *desc, upb_arena *arena, |
||||
upb_msg **msg) { |
||||
PBPHP_ASSERT(desc); |
||||
|
||||
if (Z_ISREF_P(val)) { |
||||
ZVAL_DEREF(val); |
||||
} |
||||
|
||||
if (Z_TYPE_P(val) == IS_NULL) { |
||||
*msg = NULL; |
||||
return true; |
||||
} |
||||
|
||||
if (Z_TYPE_P(val) == IS_OBJECT && |
||||
instanceof_function(Z_OBJCE_P(val), desc->class_entry)) { |
||||
Message *intern = (Message*)Z_OBJ_P(val); |
||||
upb_arena_fuse(arena, Arena_Get(&intern->arena)); |
||||
*msg = intern->msg; |
||||
return true; |
||||
} else { |
||||
zend_throw_exception_ex(NULL, 0, "Given value is not an instance of %s.", |
||||
ZSTR_VAL(desc->class_entry->name)); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
// Message PHP methods /////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Message_InitFromPhp() |
||||
* |
||||
* Helper method to handle the initialization of a message from a PHP value, eg. |
||||
* |
||||
* $m = new TestMessage([ |
||||
* 'optional_int32' => -42, |
||||
* 'optional_bool' => true, |
||||
* 'optional_string' => 'a', |
||||
* 'optional_enum' => TestEnum::ONE, |
||||
* 'optional_message' => new Sub([ |
||||
* 'a' => 33 |
||||
* ]), |
||||
* 'repeated_int32' => [-42, -52], |
||||
* 'repeated_enum' => [TestEnum::ZERO, TestEnum::ONE], |
||||
* 'repeated_message' => [new Sub(['a' => 34]), |
||||
* new Sub(['a' => 35])], |
||||
* 'map_int32_int32' => [-62 => -62], |
||||
* 'map_int32_enum' => [1 => TestEnum::ONE], |
||||
* 'map_int32_message' => [1 => new Sub(['a' => 36])], |
||||
* ]); |
||||
* |
||||
* The initializer must be an array. |
||||
*/ |
||||
bool Message_InitFromPhp(upb_msg *msg, const upb_msgdef *m, zval *init, |
||||
upb_arena *arena) { |
||||
HashTable* table = HASH_OF(init); |
||||
HashPosition pos; |
||||
|
||||
if (Z_ISREF_P(init)) { |
||||
ZVAL_DEREF(init); |
||||
} |
||||
|
||||
if (Z_TYPE_P(init) != IS_ARRAY) { |
||||
zend_throw_exception_ex(NULL, 0, |
||||
"Initializer for a message %s must be an array.", |
||||
upb_msgdef_fullname(m)); |
||||
return false; |
||||
} |
||||
|
||||
zend_hash_internal_pointer_reset_ex(table, &pos); |
||||
|
||||
while (true) { // Iterate over key/value pairs.
|
||||
zval key; |
||||
zval *val; |
||||
const upb_fielddef *f; |
||||
upb_msgval msgval; |
||||
|
||||
zend_hash_get_current_key_zval_ex(table, &key, &pos); |
||||
val = zend_hash_get_current_data_ex(table, &pos); |
||||
|
||||
if (!val) return true; // Finished iteration.
|
||||
|
||||
if (Z_ISREF_P(val)) { |
||||
ZVAL_DEREF(val); |
||||
} |
||||
|
||||
f = upb_msgdef_ntof(m, Z_STRVAL_P(&key), Z_STRLEN_P(&key)); |
||||
|
||||
if (!f) { |
||||
zend_throw_exception_ex(NULL, 0, |
||||
"No such field %s", Z_STRVAL_P(&key)); |
||||
return false; |
||||
} |
||||
|
||||
if (upb_fielddef_ismap(f)) { |
||||
msgval.map_val = MapField_GetUpbMap(val, f, arena); |
||||
if (!msgval.map_val) return false; |
||||
} else if (upb_fielddef_isseq(f)) { |
||||
msgval.array_val = RepeatedField_GetUpbArray(val, f, arena); |
||||
if (!msgval.array_val) return false; |
||||
} else { |
||||
const Descriptor *desc = Descriptor_GetFromFieldDef(f); |
||||
upb_fieldtype_t type = upb_fielddef_type(f); |
||||
if (!Convert_PhpToUpbAutoWrap(val, &msgval, type, desc, arena)) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
upb_msg_set(msg, f, msgval, arena); |
||||
zend_hash_move_forward_ex(table, &pos); |
||||
zval_dtor(&key); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::__construct() |
||||
* |
||||
* Constructor for Message. |
||||
* @param array Map of initial values ['k' = val] |
||||
*/ |
||||
PHP_METHOD(Message, __construct) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
const Descriptor* desc = Descriptor_GetFromClassEntry(Z_OBJCE_P(getThis())); |
||||
const upb_msgdef *msgdef = desc->msgdef; |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
zval *init_arr; |
||||
|
||||
intern->desc = desc; |
||||
intern->msg = upb_msg_new(msgdef, arena); |
||||
ObjCache_Add(intern->msg, &intern->std); |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &init_arr) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
if (init_arr) { |
||||
Message_InitFromPhp(intern->msg, desc->msgdef, init_arr, arena); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::discardUnknownFields() |
||||
* |
||||
* Discards any unknown fields for this message or any submessages. |
||||
*/ |
||||
PHP_METHOD(Message, discardUnknownFields) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
upb_msg_discardunknown(intern->msg, intern->desc->msgdef, 64); |
||||
} |
||||
|
||||
/**
|
||||
* Message::clear() |
||||
* |
||||
* Clears all fields of this message. |
||||
*/ |
||||
PHP_METHOD(Message, clear) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
upb_msg_clear(intern->msg, intern->desc->msgdef); |
||||
} |
||||
|
||||
/**
|
||||
* Message::mergeFrom() |
||||
* |
||||
* Merges from the given message, which must be of the same class as us. |
||||
* @param object Message to merge from. |
||||
*/ |
||||
PHP_METHOD(Message, mergeFrom) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
Message* from; |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef); |
||||
zval* value; |
||||
char *pb; |
||||
size_t size; |
||||
bool ok; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &value, |
||||
intern->desc->class_entry) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
from = (Message*)Z_OBJ_P(value); |
||||
|
||||
// Should be guaranteed since we passed the class type to
|
||||
// zend_parse_parameters().
|
||||
PBPHP_ASSERT(from->desc == intern->desc); |
||||
|
||||
// TODO(haberman): use a temp arena for this once we can make upb_decode()
|
||||
// copy strings.
|
||||
pb = upb_encode(from->msg, l, arena, &size); |
||||
|
||||
if (!pb) { |
||||
zend_throw_exception_ex(NULL, 0, "Max nesting exceeded"); |
||||
return; |
||||
} |
||||
|
||||
ok = upb_decode(pb, size, intern->msg, l, arena); |
||||
PBPHP_ASSERT(ok); |
||||
} |
||||
|
||||
/**
|
||||
* Message::mergeFromString() |
||||
* |
||||
* Merges from the given string. |
||||
* @param string Binary protobuf data to merge. |
||||
*/ |
||||
PHP_METHOD(Message, mergeFromString) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
char *data = NULL; |
||||
char *data_copy = NULL; |
||||
zend_long data_len; |
||||
const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &data, &data_len) == |
||||
FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
// TODO(haberman): avoid this copy when we can make the decoder copy.
|
||||
data_copy = upb_arena_malloc(arena, data_len); |
||||
memcpy(data_copy, data, data_len); |
||||
|
||||
if (!upb_decode(data_copy, data_len, intern->msg, l, arena)) { |
||||
zend_throw_exception_ex(NULL, 0, "Error occurred during parsing"); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::serializeToString() |
||||
* |
||||
* Serializes this message instance to protobuf data. |
||||
* @return string Serialized protobuf data. |
||||
*/ |
||||
PHP_METHOD(Message, serializeToString) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
const upb_msglayout *l = upb_msgdef_layout(intern->desc->msgdef); |
||||
upb_arena *tmp_arena = upb_arena_new(); |
||||
char *data; |
||||
size_t size; |
||||
|
||||
data = upb_encode(intern->msg, l, tmp_arena, &size); |
||||
|
||||
if (!data) { |
||||
zend_throw_exception_ex(NULL, 0, "Error occurred during serialization"); |
||||
upb_arena_free(tmp_arena); |
||||
return; |
||||
} |
||||
|
||||
RETVAL_STRINGL(data, size); |
||||
upb_arena_free(tmp_arena); |
||||
} |
||||
|
||||
/**
|
||||
* Message::mergeFromJsonString() |
||||
* |
||||
* Merges the JSON data parsed from the given string. |
||||
* @param string Serialized JSON data. |
||||
*/ |
||||
PHP_METHOD(Message, mergeFromJsonString) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
char *data = NULL; |
||||
char *data_copy = NULL; |
||||
zend_long data_len; |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
upb_status status; |
||||
zend_bool ignore_json_unknown = false; |
||||
int options = 0; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b", &data, &data_len, |
||||
&ignore_json_unknown) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
// TODO(haberman): avoid this copy when we can make the decoder copy.
|
||||
data_copy = upb_arena_malloc(arena, data_len + 1); |
||||
memcpy(data_copy, data, data_len); |
||||
data_copy[data_len] = '\0'; |
||||
|
||||
if (ignore_json_unknown) { |
||||
options |= UPB_JSONDEC_IGNOREUNKNOWN; |
||||
} |
||||
|
||||
upb_status_clear(&status); |
||||
if (!upb_json_decode(data_copy, data_len, intern->msg, intern->desc->msgdef, |
||||
DescriptorPool_GetSymbolTable(), options, arena, |
||||
&status)) { |
||||
zend_throw_exception_ex(NULL, 0, "Error occurred during parsing: %s", |
||||
upb_status_errmsg(&status)); |
||||
return; |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::serializeToJsonString() |
||||
* |
||||
* Serializes this object to JSON. |
||||
* @return string Serialized JSON data. |
||||
*/ |
||||
PHP_METHOD(Message, serializeToJsonString) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
size_t size; |
||||
int options = 0; |
||||
char buf[1024]; |
||||
zend_bool preserve_proto_fieldnames = false; |
||||
upb_status status; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", |
||||
&preserve_proto_fieldnames) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
if (preserve_proto_fieldnames) { |
||||
options |= UPB_JSONENC_PROTONAMES; |
||||
} |
||||
|
||||
upb_status_clear(&status); |
||||
size = upb_json_encode(intern->msg, intern->desc->msgdef, |
||||
DescriptorPool_GetSymbolTable(), options, buf, |
||||
sizeof(buf), &status); |
||||
|
||||
if (!upb_ok(&status)) { |
||||
zend_throw_exception_ex(NULL, 0, |
||||
"Error occurred during JSON serialization: %s", |
||||
upb_status_errmsg(&status)); |
||||
return; |
||||
} |
||||
|
||||
if (size >= sizeof(buf)) { |
||||
char *buf2 = malloc(size + 1); |
||||
upb_json_encode(intern->msg, intern->desc->msgdef, |
||||
DescriptorPool_GetSymbolTable(), options, buf2, size + 1, |
||||
&status); |
||||
RETVAL_STRINGL(buf2, size); |
||||
free(buf2); |
||||
} else { |
||||
RETVAL_STRINGL(buf, size); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::readWrapperValue() |
||||
* |
||||
* Returns an unboxed value for the given field. This is called from generated |
||||
* methods for wrapper fields, eg. |
||||
* |
||||
* public function getDoubleValueUnwrapped() |
||||
* { |
||||
* return $this->readWrapperValue("double_value"); |
||||
* } |
||||
* |
||||
* @return Unwrapped field value or null. |
||||
*/ |
||||
PHP_METHOD(Message, readWrapperValue) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
char* member; |
||||
const upb_fielddef *f; |
||||
zend_long size; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &member, &size) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
f = upb_msgdef_ntof(intern->desc->msgdef, member, size); |
||||
|
||||
if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) { |
||||
zend_throw_exception_ex(NULL, 0, "Message %s has no field %s", |
||||
upb_msgdef_fullname(intern->desc->msgdef), member); |
||||
return; |
||||
} |
||||
|
||||
if (upb_msg_has(intern->msg, f)) { |
||||
const upb_msg *wrapper = upb_msg_get(intern->msg, f).msg_val; |
||||
const upb_msgdef *m = upb_fielddef_msgsubdef(f); |
||||
const upb_fielddef *val_f = upb_msgdef_itof(m, 1); |
||||
const upb_fieldtype_t val_type = upb_fielddef_type(val_f); |
||||
upb_msgval msgval = upb_msg_get(wrapper, val_f); |
||||
zval ret; |
||||
Convert_UpbToPhp(msgval, &ret, val_type, NULL, &intern->arena); |
||||
RETURN_ZVAL(&ret, 1, 0); |
||||
} else { |
||||
RETURN_NULL(); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::writeWrapperValue() |
||||
* |
||||
* Sets the given wrapper field to the given unboxed value. This is called from |
||||
* generated methods for wrapper fields, eg. |
||||
* |
||||
* |
||||
* public function setDoubleValueUnwrapped($var) |
||||
* { |
||||
* $this->writeWrapperValue("double_value", $var); |
||||
* return $this; |
||||
* } |
||||
* |
||||
* @param Unwrapped field value or null. |
||||
*/ |
||||
PHP_METHOD(Message, writeWrapperValue) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
char* member; |
||||
const upb_fielddef *f; |
||||
upb_msgval msgval; |
||||
zend_long size; |
||||
zval* val; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &member, &size, &val) == |
||||
FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
f = upb_msgdef_ntof(intern->desc->msgdef, member, size); |
||||
|
||||
if (!f || !upb_msgdef_iswrapper(upb_fielddef_msgsubdef(f))) { |
||||
zend_throw_exception_ex(NULL, 0, "Message %s has no field %s", |
||||
upb_msgdef_fullname(intern->desc->msgdef), member); |
||||
return; |
||||
} |
||||
|
||||
if (Z_ISREF_P(val)) { |
||||
ZVAL_DEREF(val); |
||||
} |
||||
|
||||
if (Z_TYPE_P(val) == IS_NULL) { |
||||
upb_msg_clearfield(intern->msg, f); |
||||
} else { |
||||
const upb_msgdef *m = upb_fielddef_msgsubdef(f); |
||||
const upb_fielddef *val_f = upb_msgdef_itof(m, 1); |
||||
upb_fieldtype_t val_type = upb_fielddef_type(val_f); |
||||
upb_msg *wrapper; |
||||
|
||||
if (!Convert_PhpToUpb(val, &msgval, val_type, NULL, arena)) { |
||||
return; // Error is already set.
|
||||
} |
||||
|
||||
wrapper = upb_msg_mutable(intern->msg, f, arena).msg; |
||||
upb_msg_set(wrapper, val_f, msgval, arena); |
||||
} |
||||
} |
||||
|
||||
/**
|
||||
* Message::whichOneof() |
||||
* |
||||
* Given a oneof name, returns the name of the field that is set for this oneof, |
||||
* or otherwise the empty string. |
||||
* |
||||
* @return string The field name in this oneof that is currently set. |
||||
*/ |
||||
PHP_METHOD(Message, whichOneof) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
const upb_oneofdef* oneof; |
||||
const upb_fielddef* field; |
||||
char* name; |
||||
zend_long len; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &len) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
oneof = upb_msgdef_ntoo(intern->desc->msgdef, name, len); |
||||
|
||||
if (!oneof) { |
||||
zend_throw_exception_ex(NULL, 0, "Message %s has no oneof %s", |
||||
upb_msgdef_fullname(intern->desc->msgdef), name); |
||||
return; |
||||
} |
||||
|
||||
field = upb_msg_whichoneof(intern->msg, oneof); |
||||
RETURN_STRING(field ? upb_fielddef_name(field) : ""); |
||||
} |
||||
|
||||
/**
|
||||
* Message::readOneof() |
||||
* |
||||
* Returns the contents of the given oneof field, given a field number. Called |
||||
* from generated code methods such as: |
||||
* |
||||
* public function getDoubleValueOneof() |
||||
* { |
||||
* return $this->readOneof(10); |
||||
* } |
||||
* |
||||
* @return object The oneof's field value. |
||||
*/ |
||||
PHP_METHOD(Message, readOneof) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
zend_long field_num; |
||||
const upb_fielddef* f; |
||||
zval ret; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &field_num) == FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
f = upb_msgdef_itof(intern->desc->msgdef, field_num); |
||||
|
||||
if (!f || !upb_fielddef_realcontainingoneof(f)) { |
||||
php_error_docref(NULL, E_USER_ERROR, |
||||
"Internal error, no such oneof field %d\n", |
||||
(int)field_num); |
||||
} |
||||
|
||||
{ |
||||
upb_msgval msgval = upb_msg_get(intern->msg, f); |
||||
const Descriptor *subdesc = Descriptor_GetFromFieldDef(f); |
||||
Convert_UpbToPhp(msgval, &ret, upb_fielddef_type(f), subdesc, |
||||
&intern->arena); |
||||
} |
||||
|
||||
RETURN_ZVAL(&ret, 1, 0); |
||||
} |
||||
|
||||
/**
|
||||
* Message::writeOneof() |
||||
* |
||||
* Sets the contents of the given oneof field, given a field number. Called |
||||
* from generated code methods such as: |
||||
* |
||||
* public function setDoubleValueOneof($var) |
||||
* { |
||||
* GPBUtil::checkMessage($var, \Google\Protobuf\DoubleValue::class); |
||||
* $this->writeOneof(10, $var); |
||||
* |
||||
* return $this; |
||||
* } |
||||
* |
||||
* The C extension version of GPBUtil::check*() does nothing, so we perform |
||||
* all type checking and conversion here. |
||||
* |
||||
* @param integer The field number we are setting. |
||||
* @param object The field value we want to set. |
||||
*/ |
||||
PHP_METHOD(Message, writeOneof) { |
||||
Message* intern = (Message*)Z_OBJ_P(getThis()); |
||||
zend_long field_num; |
||||
const upb_fielddef* f; |
||||
upb_arena *arena = Arena_Get(&intern->arena); |
||||
upb_msgval msgval; |
||||
zval* val; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &field_num, &val) == |
||||
FAILURE) { |
||||
return; |
||||
} |
||||
|
||||
f = upb_msgdef_itof(intern->desc->msgdef, field_num); |
||||
|
||||
if (!Convert_PhpToUpb(val, &msgval, upb_fielddef_type(f), |
||||
Descriptor_GetFromFieldDef(f), arena)) { |
||||
return; |
||||
} |
||||
|
||||
upb_msg_set(intern->msg, f, msgval, arena); |
||||
} |
||||
|
||||
static zend_function_entry Message_methods[] = { |
||||
PHP_ME(Message, clear, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, discardUnknownFields, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, serializeToString, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, mergeFromString, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, serializeToJsonString, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, mergeFromJsonString, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, mergeFrom, NULL, ZEND_ACC_PUBLIC) |
||||
PHP_ME(Message, readWrapperValue, NULL, ZEND_ACC_PROTECTED) |
||||
PHP_ME(Message, writeWrapperValue, NULL, ZEND_ACC_PROTECTED) |
||||
PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED) |
||||
PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED) |
||||
PHP_ME(Message, whichOneof, NULL, ZEND_ACC_PROTECTED) |
||||
PHP_ME(Message, __construct, NULL, ZEND_ACC_PROTECTED) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
/**
|
||||
* Message_ModuleInit() |
||||
* |
||||
* Called when the C extension is loaded to register all types. |
||||
*/ |
||||
void Message_ModuleInit() { |
||||
zend_class_entry tmp_ce; |
||||
zend_object_handlers *h = &message_object_handlers; |
||||
|
||||
INIT_CLASS_ENTRY(tmp_ce, "Google\\Protobuf\\Internal\\Message", |
||||
Message_methods); |
||||
|
||||
message_ce = zend_register_internal_class(&tmp_ce); |
||||
message_ce->create_object = Message_create; |
||||
|
||||
memcpy(h, &std_object_handlers, sizeof(zend_object_handlers)); |
||||
h->dtor_obj = Message_dtor; |
||||
h->read_property = Message_read_property; |
||||
h->write_property = Message_write_property; |
||||
h->get_properties = Message_get_properties; |
||||
h->get_property_ptr_ptr = Message_get_property_ptr_ptr; |
||||
} |
@ -0,0 +1,59 @@ |
||||
// 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 PHP_PROTOBUF_MESSAGE_H_ |
||||
#define PHP_PROTOBUF_MESSAGE_H_ |
||||
|
||||
#include <stdbool.h> |
||||
|
||||
#include "def.h" |
||||
|
||||
// Registers the PHP Message class.
|
||||
void Message_ModuleInit(); |
||||
|
||||
// Gets a upb_msg* for the PHP object |val|, which must either be a Message
|
||||
// object or 'null'. Returns true and stores the message in |msg| if the
|
||||
// conversion succeeded (we can't return upb_msg* because null->NULL is a valid
|
||||
// conversion). Returns false and raises a PHP error if this isn't a Message
|
||||
// object or null, or if the Message object doesn't match this Descriptor.
|
||||
//
|
||||
// The given |arena| will be fused to this message's arena.
|
||||
bool Message_GetUpbMessage(zval *val, const Descriptor *desc, upb_arena *arena, |
||||
upb_msg **msg); |
||||
|
||||
// Gets or creates a PHP Message object to wrap the given upb_msg* and |desc|
|
||||
// and returns it in |val|. The PHP object will keep a reference to this |arena|
|
||||
// to ensure the underlying message data stays alive.
|
||||
//
|
||||
// If |msg| is NULL, this will return a PHP null.
|
||||
void Message_GetPhpWrapper(zval *val, const Descriptor *desc, upb_msg *msg, |
||||
zval *arena); |
||||
|
||||
#endif // PHP_PROTOBUF_MESSAGE_H_
|
@ -0,0 +1,226 @@ |
||||
// 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 "names.h" |
||||
|
||||
#include <stdlib.h> |
||||
|
||||
#include "protobuf.h" |
||||
|
||||
/* stringsink *****************************************************************/ |
||||
|
||||
typedef struct { |
||||
char *ptr; |
||||
size_t len, size; |
||||
} stringsink; |
||||
|
||||
static size_t stringsink_string(stringsink *sink, const char *ptr, size_t len) { |
||||
size_t new_size = sink->size; |
||||
|
||||
while (sink->len + len > new_size) { |
||||
new_size *= 2; |
||||
} |
||||
|
||||
if (new_size != sink->size) { |
||||
sink->ptr = realloc(sink->ptr, new_size); |
||||
sink->size = new_size; |
||||
} |
||||
|
||||
memcpy(sink->ptr + sink->len, ptr, len); |
||||
sink->len += len; |
||||
|
||||
return len; |
||||
} |
||||
|
||||
static void stringsink_init(stringsink *sink) { |
||||
sink->size = 32; |
||||
sink->ptr = malloc(sink->size); |
||||
PBPHP_ASSERT(sink->ptr != NULL); |
||||
sink->len = 0; |
||||
} |
||||
|
||||
static void stringsink_uninit(stringsink *sink) { free(sink->ptr); } |
||||
|
||||
/* def name -> classname ******************************************************/ |
||||
|
||||
const char *const kReservedNames[] = { |
||||
"abstract", "and", "array", "as", "break", |
||||
"callable", "case", "catch", "class", "clone", |
||||
"const", "continue", "declare", "default", "die", |
||||
"do", "echo", "else", "elseif", "empty", |
||||
"enddeclare", "endfor", "endforeach", "endif", "endswitch", |
||||
"endwhile", "eval", "exit", "extends", "final", |
||||
"for", "foreach", "function", "global", "goto", |
||||
"if", "implements", "include", "include_once", "instanceof", |
||||
"insteadof", "interface", "isset", "list", "namespace", |
||||
"new", "or", "print", "private", "protected", |
||||
"public", "require", "require_once", "return", "static", |
||||
"switch", "throw", "trait", "try", "unset", |
||||
"use", "var", "while", "xor", "int", |
||||
"float", "bool", "string", "true", "false", |
||||
"null", "void", "iterable", NULL}; |
||||
|
||||
bool is_reserved_name(const char* name) { |
||||
int i; |
||||
for (i = 0; kReservedNames[i]; i++) { |
||||
if (strcmp(kReservedNames[i], name) == 0) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
static char nolocale_tolower(char ch) { |
||||
if (ch >= 'A' && ch <= 'Z') { |
||||
return ch - ('A' - 'a'); |
||||
} else { |
||||
return ch; |
||||
} |
||||
} |
||||
|
||||
static char nolocale_toupper(char ch) { |
||||
if (ch >= 'a' && ch <= 'z') { |
||||
return ch - ('a' - 'A'); |
||||
} else { |
||||
return ch; |
||||
} |
||||
} |
||||
|
||||
static bool is_reserved(const char *segment, int length) { |
||||
bool result; |
||||
char* lower = calloc(1, length + 1); |
||||
memcpy(lower, segment, length); |
||||
int i = 0; |
||||
while(lower[i]) { |
||||
lower[i] = nolocale_tolower(lower[i]); |
||||
i++; |
||||
} |
||||
lower[length] = 0; |
||||
result = is_reserved_name(lower); |
||||
free(lower); |
||||
return result; |
||||
} |
||||
|
||||
static void fill_prefix(const char *segment, int length, |
||||
const char *prefix_given, |
||||
const char *package_name, |
||||
stringsink *classname) { |
||||
if (prefix_given != NULL && strcmp(prefix_given, "") != 0) { |
||||
stringsink_string(classname, prefix_given, strlen(prefix_given)); |
||||
} else { |
||||
if (is_reserved(segment, length)) { |
||||
if (package_name != NULL && |
||||
strcmp("google.protobuf", package_name) == 0) { |
||||
stringsink_string(classname, "GPB", 3); |
||||
} else { |
||||
stringsink_string(classname, "PB", 2); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void fill_segment(const char *segment, int length, |
||||
stringsink *classname, bool use_camel) { |
||||
if (use_camel && (segment[0] < 'A' || segment[0] > 'Z')) { |
||||
char first = nolocale_toupper(segment[0]); |
||||
stringsink_string(classname, &first, 1); |
||||
stringsink_string(classname, segment + 1, length - 1); |
||||
} else { |
||||
stringsink_string(classname, segment, length); |
||||
} |
||||
} |
||||
|
||||
static void fill_namespace(const char *package, const char *php_namespace, |
||||
stringsink *classname) { |
||||
if (php_namespace != NULL) { |
||||
if (strlen(php_namespace) != 0) { |
||||
stringsink_string(classname, php_namespace, strlen(php_namespace)); |
||||
stringsink_string(classname, "\\", 1); |
||||
} |
||||
} else if (package != NULL) { |
||||
int i = 0, j = 0; |
||||
size_t package_len = strlen(package); |
||||
while (i < package_len) { |
||||
j = i; |
||||
while (j < package_len && package[j] != '.') { |
||||
j++; |
||||
} |
||||
fill_prefix(package + i, j - i, "", package, classname); |
||||
fill_segment(package + i, j - i, classname, true); |
||||
stringsink_string(classname, "\\", 1); |
||||
i = j + 1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void fill_classname(const char *fullname, |
||||
const char *package, |
||||
const char *prefix, |
||||
stringsink *classname) { |
||||
int classname_start = 0; |
||||
if (package != NULL) { |
||||
size_t package_len = strlen(package); |
||||
classname_start = package_len == 0 ? 0 : package_len + 1; |
||||
} |
||||
size_t fullname_len = strlen(fullname); |
||||
|
||||
int i = classname_start, j; |
||||
while (i < fullname_len) { |
||||
j = i; |
||||
while (j < fullname_len && fullname[j] != '.') { |
||||
j++; |
||||
} |
||||
fill_prefix(fullname + i, j - i, prefix, package, classname); |
||||
fill_segment(fullname + i, j - i, classname, false); |
||||
if (j != fullname_len) { |
||||
stringsink_string(classname, "\\", 1); |
||||
} |
||||
i = j + 1; |
||||
} |
||||
} |
||||
|
||||
char *GetPhpClassname(const upb_filedef *file, const char *fullname) { |
||||
// Prepend '.' to package name to make it absolute. In the 5 additional
|
||||
// bytes allocated, one for '.', one for trailing 0, and 3 for 'GPB' if
|
||||
// given message is google.protobuf.Empty.
|
||||
const char *package = upb_filedef_package(file); |
||||
const char *php_namespace = upb_filedef_phpnamespace(file); |
||||
const char *prefix = upb_filedef_phpprefix(file); |
||||
char *ret; |
||||
stringsink namesink; |
||||
stringsink_init(&namesink); |
||||
|
||||
fill_namespace(package, php_namespace, &namesink); |
||||
fill_classname(fullname, package, prefix, &namesink); |
||||
stringsink_string(&namesink, "\0", 1); |
||||
ret = strdup(namesink.ptr); |
||||
stringsink_uninit(&namesink); |
||||
return ret; |
||||
} |
@ -0,0 +1,40 @@ |
||||
// 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 PHP_PROTOBUF_NAMES_H_ |
||||
#define PHP_PROTOBUF_NAMES_H_ |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
// Translates a protobuf symbol name (eg. foo.bar.Baz) into a PHP class name
|
||||
// (eg. \Foo\Bar\Baz).
|
||||
char *GetPhpClassname(const upb_filedef *file, const char *fullname); |
||||
|
||||
#endif // PHP_PROTOBUF_NAMES_H_
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,349 @@ |
||||
// 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 <php.h> |
||||
#include <Zend/zend_interfaces.h> |
||||
|
||||
#include "arena.h" |
||||
#include "array.h" |
||||
#include "bundled_php.h" |
||||
#include "convert.h" |
||||
#include "def.h" |
||||
#include "map.h" |
||||
#include "message.h" |
||||
#include "names.h" |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Module "globals"
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Despite the name, module "globals" are really thread-locals:
|
||||
// * PROTOBUF_G(var) accesses the thread-local variable for 'var'. Either:
|
||||
// * PROTOBUF_G(var) -> protobuf_globals.var (Non-ZTS / non-thread-safe)
|
||||
// * PROTOBUF_G(var) -> <Zend magic> (ZTS / thread-safe builds)
|
||||
|
||||
#define PROTOBUF_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(protobuf, v) |
||||
|
||||
ZEND_BEGIN_MODULE_GLOBALS(protobuf) |
||||
// Set by the user to make the descriptor pool persist between requests.
|
||||
zend_bool keep_descriptor_pool_after_request; |
||||
|
||||
// Currently we make the generated pool a "global", which means that if a user
|
||||
// does explicitly create threads within their request, the other threads will
|
||||
// get different results from DescriptorPool::getGeneratedPool(). We require
|
||||
// that all descriptors are loaded from the main thread.
|
||||
zval generated_pool; |
||||
|
||||
// A upb_symtab that we are saving for the next request so that we don't have
|
||||
// to rebuild it from scratch. When keep_descriptor_pool_after_request==true,
|
||||
// we steal the upb_symtab from the global DescriptorPool object just before
|
||||
// destroying it.
|
||||
upb_symtab *saved_symtab; |
||||
|
||||
// Object cache (see interface in protobuf.h).
|
||||
HashTable object_cache; |
||||
|
||||
// Name cache (see interface in protobuf.h).
|
||||
HashTable name_msg_cache; |
||||
HashTable name_enum_cache; |
||||
ZEND_END_MODULE_GLOBALS(protobuf) |
||||
|
||||
ZEND_DECLARE_MODULE_GLOBALS(protobuf) |
||||
|
||||
const zval *get_generated_pool() { |
||||
return &PROTOBUF_G(generated_pool); |
||||
} |
||||
|
||||
// This is a PHP extension (not a Zend extension). What follows is a summary of
|
||||
// a PHP extension's lifetime and when various handlers are called.
|
||||
//
|
||||
// * PHP_GINIT_FUNCTION(protobuf) / PHP_GSHUTDOWN_FUNCTION(protobuf)
|
||||
// are the constructor/destructor for the globals. The sequence over the
|
||||
// course of a process lifetime is:
|
||||
//
|
||||
// # Process startup
|
||||
// GINIT(<Main Thread Globals>)
|
||||
// MINIT
|
||||
//
|
||||
// foreach request:
|
||||
// RINIT
|
||||
// # Request is processed here.
|
||||
// RSHUTDOWN
|
||||
//
|
||||
// foreach thread:
|
||||
// GINIT(<This Thread Globals>)
|
||||
// # Code for the thread runs here.
|
||||
// GSHUTDOWN(<This Thread Globals>)
|
||||
//
|
||||
// # Process Shutdown
|
||||
// #
|
||||
// # These should be running per the docs, but I have not been able to
|
||||
// # actually get the process-wide shutdown functions to run.
|
||||
// #
|
||||
// # MSHUTDOWN
|
||||
// # GSHUTDOWN(<Main Thread Globals>)
|
||||
//
|
||||
// * Threads can be created either explicitly by the user, inside a request,
|
||||
// or implicitly by the runtime, to process multiple requests concurrently.
|
||||
// If the latter is being used, then the "foreach thread" block above
|
||||
// actually looks like this:
|
||||
//
|
||||
// foreach thread:
|
||||
// GINIT(<This Thread Globals>)
|
||||
// # A non-main thread will only receive requests when using a threaded
|
||||
// # MPM with Apache
|
||||
// foreach request:
|
||||
// RINIT
|
||||
// # Request is processed here.
|
||||
// RSHUTDOWN
|
||||
// GSHUTDOWN(<This Thread Globals>)
|
||||
//
|
||||
// That said, it appears that few people use threads with PHP:
|
||||
// * The pthread package documented at
|
||||
// https://www.php.net/manual/en/class.thread.php nas not been released
|
||||
// since 2016, and the current release fails to compile against any PHP
|
||||
// newer than 7.0.33.
|
||||
// * The GitHub master branch supports 7.2+, but this has not been released
|
||||
// to PECL.
|
||||
// * Its owner has disavowed it as "broken by design" and "in an untenable
|
||||
// position for the future": https://github.com/krakjoe/pthreads/issues/929
|
||||
// * The only way to use PHP with requests in different threads is to use the
|
||||
// Apache 2 mod_php with the "worker" MPM. But this is explicitly
|
||||
// discouraged by the documentation: https://serverfault.com/a/231660
|
||||
|
||||
static PHP_GSHUTDOWN_FUNCTION(protobuf) { |
||||
if (protobuf_globals->saved_symtab) { |
||||
upb_symtab_free(protobuf_globals->saved_symtab); |
||||
} |
||||
} |
||||
|
||||
static PHP_GINIT_FUNCTION(protobuf) { |
||||
ZVAL_NULL(&protobuf_globals->generated_pool); |
||||
protobuf_globals->saved_symtab = NULL; |
||||
} |
||||
|
||||
/**
|
||||
* PHP_RINIT_FUNCTION(protobuf) |
||||
* |
||||
* This function is run at the beginning of processing each request. |
||||
*/ |
||||
static PHP_RINIT_FUNCTION(protobuf) { |
||||
// Create the global generated pool.
|
||||
// Reuse the symtab (if any) left to us by the last request.
|
||||
upb_symtab *symtab = PROTOBUF_G(saved_symtab); |
||||
DescriptorPool_CreateWithSymbolTable(&PROTOBUF_G(generated_pool), symtab); |
||||
|
||||
// Set up autoloader for bundled sources.
|
||||
zend_eval_string("spl_autoload_register('protobuf_internal_loadbundled');", |
||||
NULL, "autoload_register.php"); |
||||
|
||||
zend_hash_init(&PROTOBUF_G(object_cache), 64, NULL, NULL, 0); |
||||
zend_hash_init(&PROTOBUF_G(name_msg_cache), 64, NULL, NULL, 0); |
||||
zend_hash_init(&PROTOBUF_G(name_enum_cache), 64, NULL, NULL, 0); |
||||
|
||||
return SUCCESS; |
||||
} |
||||
|
||||
/**
|
||||
* PHP_RSHUTDOWN_FUNCTION(protobuf) |
||||
* |
||||
* This function is run at the end of processing each request. |
||||
*/ |
||||
static PHP_RSHUTDOWN_FUNCTION(protobuf) { |
||||
// Preserve the symtab if requested.
|
||||
if (PROTOBUF_G(keep_descriptor_pool_after_request)) { |
||||
zval *zv = &PROTOBUF_G(generated_pool); |
||||
PROTOBUF_G(saved_symtab) = DescriptorPool_Steal(zv); |
||||
} |
||||
|
||||
zval_dtor(&PROTOBUF_G(generated_pool)); |
||||
zend_hash_destroy(&PROTOBUF_G(object_cache)); |
||||
zend_hash_destroy(&PROTOBUF_G(name_msg_cache)); |
||||
zend_hash_destroy(&PROTOBUF_G(name_enum_cache)); |
||||
|
||||
return SUCCESS; |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Bundled PHP sources
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// We bundle PHP sources for well-known types into the C extension. There is no
|
||||
// need to implement these in C.
|
||||
|
||||
static PHP_FUNCTION(protobuf_internal_loadbundled) { |
||||
char *name = NULL; |
||||
zend_long size; |
||||
BundledPhp_File *file; |
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &size) != SUCCESS) { |
||||
return; |
||||
} |
||||
|
||||
for (file = bundled_files; file->filename; file++) { |
||||
if (strcmp(file->filename, name) == 0) { |
||||
zend_eval_string((char*)file->contents, NULL, (char*)file->filename); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_load_bundled_source, 0, 0, 1) |
||||
ZEND_ARG_INFO(0, class_name) |
||||
ZEND_END_ARG_INFO() |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Object Cache.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void ObjCache_Add(const void *upb_obj, zend_object *php_obj) { |
||||
zend_ulong k = (zend_ulong)upb_obj; |
||||
zend_hash_index_add_ptr(&PROTOBUF_G(object_cache), k, php_obj); |
||||
} |
||||
|
||||
void ObjCache_Delete(const void *upb_obj) { |
||||
if (upb_obj) { |
||||
zend_ulong k = (zend_ulong)upb_obj; |
||||
int ret = zend_hash_index_del(&PROTOBUF_G(object_cache), k); |
||||
PBPHP_ASSERT(ret == SUCCESS); |
||||
} |
||||
} |
||||
|
||||
bool ObjCache_Get(const void *upb_obj, zval *val) { |
||||
zend_ulong k = (zend_ulong)upb_obj; |
||||
zend_object *obj = zend_hash_index_find_ptr(&PROTOBUF_G(object_cache), k); |
||||
|
||||
if (obj) { |
||||
GC_ADDREF(obj); |
||||
ZVAL_OBJ(val, obj); |
||||
return true; |
||||
} else { |
||||
ZVAL_NULL(val); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Name Cache.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void NameMap_AddMessage(const upb_msgdef *m) { |
||||
char *k = GetPhpClassname(upb_msgdef_file(m), upb_msgdef_fullname(m)); |
||||
zend_hash_str_add_ptr(&PROTOBUF_G(name_msg_cache), k, strlen(k), (void*)m); |
||||
free(k); |
||||
} |
||||
|
||||
void NameMap_AddEnum(const upb_enumdef *e) { |
||||
char *k = GetPhpClassname(upb_enumdef_file(e), upb_enumdef_fullname(e)); |
||||
zend_hash_str_add_ptr(&PROTOBUF_G(name_enum_cache), k, strlen(k), (void*)e); |
||||
free(k); |
||||
} |
||||
|
||||
const upb_msgdef *NameMap_GetMessage(zend_class_entry *ce) { |
||||
const upb_msgdef *ret = |
||||
zend_hash_find_ptr(&PROTOBUF_G(name_msg_cache), ce->name); |
||||
|
||||
if (!ret && ce->create_object) { |
||||
zval tmp; |
||||
zval zv; |
||||
ZVAL_OBJ(&tmp, ce->create_object(ce)); |
||||
zend_call_method_with_0_params(&tmp, ce, NULL, "__construct", &zv); |
||||
zval_ptr_dtor(&tmp); |
||||
zval_ptr_dtor(&zv); |
||||
ret = zend_hash_find_ptr(&PROTOBUF_G(name_msg_cache), ce->name); |
||||
} |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
const upb_enumdef *NameMap_GetEnum(zend_class_entry *ce) { |
||||
const upb_enumdef *ret = |
||||
zend_hash_find_ptr(&PROTOBUF_G(name_enum_cache), ce->name); |
||||
return ret; |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Module init.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
zend_function_entry protobuf_functions[] = { |
||||
PHP_FE(protobuf_internal_loadbundled, arginfo_load_bundled_source) |
||||
ZEND_FE_END |
||||
}; |
||||
|
||||
static const zend_module_dep protobuf_deps[] = { |
||||
ZEND_MOD_OPTIONAL("date") |
||||
ZEND_MOD_END |
||||
}; |
||||
|
||||
PHP_INI_BEGIN() |
||||
STD_PHP_INI_ENTRY("protobuf.keep_descriptor_pool_after_request", "0", |
||||
PHP_INI_SYSTEM, OnUpdateBool, |
||||
keep_descriptor_pool_after_request, zend_protobuf_globals, |
||||
protobuf_globals) |
||||
PHP_INI_END() |
||||
|
||||
static PHP_MINIT_FUNCTION(protobuf) { |
||||
REGISTER_INI_ENTRIES(); |
||||
Arena_ModuleInit(); |
||||
Array_ModuleInit(); |
||||
Convert_ModuleInit(); |
||||
Def_ModuleInit(); |
||||
Map_ModuleInit(); |
||||
Message_ModuleInit(); |
||||
return SUCCESS; |
||||
} |
||||
|
||||
static PHP_MSHUTDOWN_FUNCTION(protobuf) { |
||||
return SUCCESS; |
||||
} |
||||
|
||||
zend_module_entry protobuf_module_entry = { |
||||
STANDARD_MODULE_HEADER_EX, |
||||
NULL, |
||||
protobuf_deps, |
||||
"protobuf", // 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
|
||||
"3.13.0", // extension version
|
||||
PHP_MODULE_GLOBALS(protobuf), // globals descriptor
|
||||
PHP_GINIT(protobuf), // globals ctor
|
||||
PHP_GSHUTDOWN(protobuf), // globals dtor
|
||||
NULL, // post deactivate
|
||||
STANDARD_MODULE_PROPERTIES_EX |
||||
}; |
||||
|
||||
ZEND_GET_MODULE(protobuf) |
@ -0,0 +1,89 @@ |
||||
// 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 PHP_PROTOBUF_H_ |
||||
#define PHP_PROTOBUF_H_ |
||||
|
||||
#include <php.h> |
||||
#include <stdbool.h> |
||||
|
||||
#include "php-upb.h" |
||||
|
||||
const zval *get_generated_pool(); |
||||
|
||||
#if PHP_VERSION_ID < 70300 |
||||
#define GC_ADDREF(h) ++GC_REFCOUNT(h) |
||||
#define GC_DELREF(h) --GC_REFCOUNT(h) |
||||
#endif |
||||
|
||||
// ptr -> PHP object cache. This is a weak map that caches lazily-created
|
||||
// wrapper objects around upb types:
|
||||
// * upb_msg* -> Message
|
||||
// * upb_array* -> RepeatedField
|
||||
// * upb_map*, -> MapField
|
||||
// * upb_msgdef* -> Descriptor
|
||||
// * upb_enumdef* -> EnumDescriptor
|
||||
// * zend_class_entry* -> Descriptor
|
||||
//
|
||||
// Each wrapped object should add itself to the map when it is constructed, and
|
||||
// remove itself from the map when it is destroyed. This is how we ensure that
|
||||
// the map only contains live objects. The map is weak so it does not actually
|
||||
// take references to the cached objects.
|
||||
void ObjCache_Add(const void *key, zend_object *php_obj); |
||||
void ObjCache_Delete(const void *key); |
||||
bool ObjCache_Get(const void *key, zval *val); |
||||
|
||||
// PHP class name map. This is necessary because the pb_name->php_class_name
|
||||
// transformation is non-reversible, so when we need to look up a msgdef or
|
||||
// enumdef by PHP class, we can't turn the class name into a pb_name.
|
||||
// * php_class_name -> upb_msgdef*
|
||||
// * php_class_name -> upb_enumdef*
|
||||
void NameMap_AddMessage(const upb_msgdef *m); |
||||
void NameMap_AddEnum(const upb_enumdef *m); |
||||
const upb_msgdef *NameMap_GetMessage(zend_class_entry *ce); |
||||
const upb_enumdef *NameMap_GetEnum(zend_class_entry *ce); |
||||
|
||||
// We need our own assert() because PHP takes control of NDEBUG in its headers.
|
||||
#ifdef PBPHP_ENABLE_ASSERTS |
||||
#define PBPHP_ASSERT(x) \ |
||||
do { \
|
||||
if (!(x)) { \
|
||||
fprintf(stderr, "Assertion failure at %s:%d %s", __FILE__, __LINE__, \
|
||||
#x); \ |
||||
abort(); \
|
||||
} \
|
||||
} while (false) |
||||
#else |
||||
#define PBPHP_ASSERT(x) \ |
||||
do { \
|
||||
} while (false && (x)) |
||||
#endif |
||||
|
||||
#endif // PHP_PROTOBUF_H_
|
Loading…
Reference in new issue