Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F627934
phongo_bson.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
43 KB
Referenced Files
None
Subscribers
None
phongo_bson.c
View Options
/*
* Copyright 2014-present MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
"bson/bson.h"
#include
<php.h>
#if PHP_VERSION_ID >= 80100
#include
<Zend/zend_enum.h>
#endif
#include
<Zend/zend_interfaces.h>
#include
"php_array_api.h"
#include
"php_phongo.h"
#include
"phongo_bson.h"
#include
"phongo_error.h"
#include
"BSON/Binary.h"
#include
"BSON/DBPointer.h"
#include
"BSON/Decimal128.h"
#include
"BSON/Document.h"
#include
"BSON/Int64.h"
#include
"BSON/Javascript.h"
#include
"BSON/ObjectId.h"
#include
"BSON/PackedArray.h"
#include
"BSON/Regex.h"
#include
"BSON/Symbol.h"
#include
"BSON/Timestamp.h"
#include
"BSON/UTCDateTime.h"
#undef MONGOC_LOG_DOMAIN
#define MONGOC_LOG_DOMAIN "PHONGO-BSON"
#define PHONGO_BSON_STATE_ZCHILD(state) (&((php_phongo_bson_state*) (state))->zchild)
#define PHONGO_FIELD_PATH_EXPANSION 8
#define PHONGO_TYPEMAP_FIELD_STR_ARRAY "array"
#define PHONGO_TYPEMAP_FIELD_STR_DOCUMENT "document"
#define PHONGO_TYPEMAP_FIELD_STR_ROOT "root"
#define PHONGO_TYPEMAP_TYPE_STR_ARRAY "array"
#define PHONGO_TYPEMAP_TYPE_STR_BSON "bson"
#define PHONGO_TYPEMAP_TYPE_STR_OBJECT "object"
#define PHONGO_TYPEMAP_TYPE_STR_STDCLASS "stdclass"
/* Forward declarations */
static
bool
php_phongo_bson_visit_document
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
const
bson_t
*
v_document
,
void
*
data
);
static
bool
php_phongo_bson_visit_array
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
const
bson_t
*
v_array
,
void
*
data
);
static
inline
bool
phongo_is_class_instantiatable
(
const
zend_class_entry
*
ce
)
{
if
(
ce
->
ce_flags
&
(
ZEND_ACC_EXPLICIT_ABSTRACT_CLASS
|
ZEND_ACC_IMPLICIT_ABSTRACT_CLASS
|
ZEND_ACC_INTERFACE
|
ZEND_ACC_TRAIT
))
{
return
false
;
}
#if PHP_VERSION_ID >= 80100
if
(
ce
->
ce_flags
&
ZEND_ACC_ENUM
)
{
return
false
;
}
#endif
/* PHP_VERSION_ID < 80100 */
return
true
;
}
/* Path builder */
char
*
php_phongo_field_path_as_string
(
php_phongo_field_path
*
field_path
)
{
size_t
length
=
1
;
/* NULL character */
size_t
i
;
char
*
path
;
char
*
ptr
;
if
(
!
field_path
)
{
return
estrdup
(
""
);
}
if
(
!
field_path
->
elements
)
{
return
estrdup
(
""
);
}
for
(
i
=
0
;
i
<=
field_path
->
size
;
i
++
)
{
if
(
!
field_path
->
elements
[
i
])
{
continue
;
}
length
+=
(
1
+
strlen
(
field_path
->
elements
[
i
]));
}
path
=
emalloc
(
length
);
ptr
=
path
;
for
(
i
=
0
;
i
<=
field_path
->
size
;
i
++
)
{
if
(
!
field_path
->
elements
[
i
])
{
continue
;
}
strcpy
(
ptr
,
field_path
->
elements
[
i
]);
ptr
+=
strlen
(
field_path
->
elements
[
i
]);
ptr
[
0
]
=
'.'
;
ptr
++
;
}
ptr
[
-1
]
=
'\0'
;
return
path
;
}
php_phongo_field_path
*
php_phongo_field_path_alloc
(
bool
owns_elements
)
{
php_phongo_field_path
*
tmp
=
ecalloc
(
1
,
sizeof
(
php_phongo_field_path
));
tmp
->
ref_count
=
1
;
tmp
->
owns_elements
=
owns_elements
;
return
tmp
;
}
void
php_phongo_field_path_free
(
php_phongo_field_path
*
field_path
)
{
if
(
field_path
->
owns_elements
)
{
size_t
i
;
for
(
i
=
0
;
i
<
field_path
->
size
;
i
++
)
{
efree
(
field_path
->
elements
[
i
]);
}
}
if
(
field_path
->
elements
)
{
efree
(
field_path
->
elements
);
}
if
(
field_path
->
element_types
)
{
efree
(
field_path
->
element_types
);
}
efree
(
field_path
);
}
static
void
php_phongo_field_path_ensure_allocation
(
php_phongo_field_path
*
field_path
,
size_t
level
)
{
if
(
level
>=
field_path
->
allocated_size
)
{
size_t
i
;
field_path
->
allocated_size
=
field_path
->
size
+
PHONGO_FIELD_PATH_EXPANSION
;
field_path
->
elements
=
erealloc
(
field_path
->
elements
,
sizeof
(
char
*
)
*
field_path
->
allocated_size
);
field_path
->
element_types
=
erealloc
(
field_path
->
element_types
,
sizeof
(
php_phongo_bson_field_path_item_types
)
*
field_path
->
allocated_size
);
for
(
i
=
level
;
i
<
field_path
->
allocated_size
;
i
++
)
{
field_path
->
elements
[
i
]
=
NULL
;
field_path
->
element_types
[
i
]
=
PHONGO_FIELD_PATH_ITEM_NONE
;
}
}
}
void
php_phongo_field_path_write_item_at_current_level
(
php_phongo_field_path
*
field_path
,
const
char
*
element
)
{
php_phongo_field_path_ensure_allocation
(
field_path
,
field_path
->
size
);
if
(
field_path
->
owns_elements
)
{
field_path
->
elements
[
field_path
->
size
]
=
estrdup
(
element
);
}
else
{
field_path
->
elements
[
field_path
->
size
]
=
(
char
*
)
element
;
}
}
void
php_phongo_field_path_write_type_at_current_level
(
php_phongo_field_path
*
field_path
,
php_phongo_bson_field_path_item_types
element_type
)
{
php_phongo_field_path_ensure_allocation
(
field_path
,
field_path
->
size
);
field_path
->
element_types
[
field_path
->
size
]
=
element_type
;
}
bool
php_phongo_field_path_push
(
php_phongo_field_path
*
field_path
,
const
char
*
element
,
php_phongo_bson_field_path_item_types
element_type
)
{
php_phongo_field_path_write_item_at_current_level
(
field_path
,
element
);
php_phongo_field_path_write_type_at_current_level
(
field_path
,
element_type
);
field_path
->
size
++
;
return
true
;
}
bool
php_phongo_field_path_pop
(
php_phongo_field_path
*
field_path
)
{
php_phongo_field_path_ensure_allocation
(
field_path
,
field_path
->
size
);
field_path
->
elements
[
field_path
->
size
]
=
NULL
;
field_path
->
element_types
[
field_path
->
size
]
=
PHONGO_FIELD_PATH_ITEM_NONE
;
field_path
->
size
--
;
field_path
->
elements
[
field_path
->
size
]
=
NULL
;
field_path
->
element_types
[
field_path
->
size
]
=
PHONGO_FIELD_PATH_ITEM_NONE
;
return
true
;
}
inline
static
bool
php_phongo_bson_state_is_initialized
(
php_phongo_bson_state
*
state
)
{
return
state
->
field_path
!=
NULL
;
}
static
void
php_phongo_bson_state_ctor
(
php_phongo_bson_state
*
state
)
{
state
->
field_path
=
php_phongo_field_path_alloc
(
false
);
}
static
void
php_phongo_bson_state_copy_ctor
(
php_phongo_bson_state
*
dst
,
php_phongo_bson_state
*
src
)
{
dst
->
map
=
src
->
map
;
if
(
src
->
field_path
)
{
src
->
field_path
->
ref_count
++
;
}
dst
->
field_path
=
src
->
field_path
;
}
static
void
php_phongo_bson_state_dtor
(
php_phongo_bson_state
*
state
)
{
if
(
state
->
field_path
)
{
state
->
field_path
->
ref_count
--
;
if
(
state
->
field_path
->
ref_count
<
1
)
{
php_phongo_field_path_free
(
state
->
field_path
);
}
state
->
field_path
=
NULL
;
}
}
static
void
php_phongo_bson_visit_corrupt
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
void
*
data
ARG_UNUSED
)
{
mongoc_log
(
MONGOC_LOG_LEVEL_WARNING
,
MONGOC_LOG_DOMAIN
,
"Corrupt BSON data detected!"
);
}
static
void
php_phongo_bson_visit_unsupported_type
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
uint32_t
v_type_code
,
void
*
data
ARG_UNUSED
)
{
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
char
*
path_string
;
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
path_string
=
php_phongo_field_path_as_string
(
state
->
field_path
);
phongo_throw_exception
(
PHONGO_ERROR_UNEXPECTED_VALUE
,
"Detected unknown BSON type 0x%02hhx for field path
\"
%s
\"
. Are you using the latest driver?"
,
(
unsigned
char
)
v_type_code
,
path_string
);
efree
(
path_string
);
}
static
bool
php_phongo_bson_visit_double
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
double
v_double
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
if
(
state
->
is_visiting_array
)
{
add_next_index_double
(
retval
,
v_double
);
}
else
{
add_assoc_double
(
retval
,
key
,
v_double
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_utf8
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
size_t
v_utf8_len
,
const
char
*
v_utf8
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
if
(
state
->
is_visiting_array
)
{
ADD_NEXT_INDEX_STRINGL
(
retval
,
v_utf8
,
v_utf8_len
);
}
else
{
ADD_ASSOC_STRING_EX
(
retval
,
key
,
strlen
(
key
),
v_utf8
,
v_utf8_len
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_binary
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
bson_subtype_t
v_subtype
,
size_t
v_binary_len
,
const
uint8_t
*
v_binary
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
if
(
v_subtype
==
0x80
&&
strcmp
(
key
,
PHONGO_ODM_FIELD_NAME
)
==
0
)
{
zend_string
*
zs_classname
=
zend_string_init
((
const
char
*
)
v_binary
,
v_binary_len
,
0
);
zend_class_entry
*
found_ce
=
zend_fetch_class
(
zs_classname
,
ZEND_FETCH_CLASS_AUTO
|
ZEND_FETCH_CLASS_SILENT
);
zend_string_release
(
zs_classname
);
if
(
found_ce
&&
phongo_is_class_instantiatable
(
found_ce
)
&&
instanceof_function
(
found_ce
,
php_phongo_persistable_ce
))
{
((
php_phongo_bson_state
*
)
data
)
->
odm_ce
=
found_ce
;
}
}
{
zval
zchild
;
if
(
!
phongo_binary_new
(
&
zchild
,
(
const
char
*
)
v_binary
,
v_binary_len
,
v_subtype
))
{
return
true
;
}
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_undefined
(
const
bson_iter_t
*
iter
,
const
char
*
key
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
object_init_ex
(
&
zchild
,
php_phongo_undefined_ce
);
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_oid
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
const
bson_oid_t
*
v_oid
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
if
(
!
phongo_objectid_new
(
&
zchild
,
v_oid
))
{
return
true
;
}
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_bool
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
bool
v_bool
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
if
(
state
->
is_visiting_array
)
{
add_next_index_bool
(
retval
,
v_bool
);
}
else
{
add_assoc_bool
(
retval
,
key
,
v_bool
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_date_time
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
int64_t
msec_since_epoch
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
if
(
!
phongo_utcdatetime_new
(
&
zchild
,
msec_since_epoch
))
{
return
true
;
}
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_decimal128
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
const
bson_decimal128_t
*
decimal
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
if
(
!
phongo_decimal128_new
(
&
zchild
,
decimal
))
{
return
true
;
}
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_null
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
if
(
state
->
is_visiting_array
)
{
add_next_index_null
(
retval
);
}
else
{
add_assoc_null
(
retval
,
key
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_regex
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
const
char
*
v_regex
,
const
char
*
v_options
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
if
(
!
phongo_regex_new
(
&
zchild
,
v_regex
,
v_options
))
{
return
true
;
}
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_symbol
(
const
bson_iter_t
*
iter
,
const
char
*
key
,
size_t
v_symbol_len
,
const
char
*
v_symbol
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
if
(
!
phongo_symbol_new
(
&
zchild
,
v_symbol
,
v_symbol_len
))
{
return
true
;
}
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_code
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
size_t
v_code_len
,
const
char
*
v_code
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
if
(
!
phongo_javascript_new
(
&
zchild
,
v_code
,
v_code_len
,
NULL
))
{
return
true
;
}
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_dbpointer
(
const
bson_iter_t
*
iter
,
const
char
*
key
,
size_t
namespace_len
,
const
char
*
namespace
,
const
bson_oid_t
*
oid
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
if
(
!
phongo_dbpointer_new
(
&
zchild
,
namespace
,
namespace_len
,
oid
))
{
return
true
;
}
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_codewscope
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
size_t
v_code_len
,
const
char
*
v_code
,
const
bson_t
*
v_scope
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
if
(
!
phongo_javascript_new
(
&
zchild
,
v_code
,
v_code_len
,
v_scope
))
{
return
true
;
}
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_int32
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
int32_t
v_int32
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
if
(
state
->
is_visiting_array
)
{
add_next_index_long
(
retval
,
v_int32
);
}
else
{
add_assoc_long
(
retval
,
key
,
v_int32
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_timestamp
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
uint32_t
v_timestamp
,
uint32_t
v_increment
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
if
(
!
phongo_timestamp_new
(
&
zchild
,
v_increment
,
v_timestamp
))
{
return
true
;
}
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_int64
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
int64_t
v_int64
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
if
(
state
->
is_visiting_array
)
{
if
(
state
->
map
.
int64_as_object
)
{
ADD_NEXT_INDEX_INT64_OBJ
(
retval
,
v_int64
);
}
else
{
ADD_NEXT_INDEX_INT64
(
retval
,
v_int64
);
}
}
else
{
if
(
state
->
map
.
int64_as_object
)
{
ADD_ASSOC_INT64_OBJ
(
retval
,
key
,
v_int64
);
}
else
{
ADD_ASSOC_INT64
(
retval
,
key
,
v_int64
);
}
}
return
false
;
}
static
bool
php_phongo_bson_visit_maxkey
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
object_init_ex
(
&
zchild
,
php_phongo_maxkey_ce
);
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
bool
php_phongo_bson_visit_minkey
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
php_phongo_bson_state
*
state
=
(
php_phongo_bson_state
*
)
data
;
zval
zchild
;
object_init_ex
(
&
zchild
,
php_phongo_minkey_ce
);
if
(
state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
zchild
);
}
php_phongo_field_path_write_item_at_current_level
(
state
->
field_path
,
key
);
return
false
;
}
static
const
bson_visitor_t
php_bson_visitors
=
{
NULL
/* php_phongo_bson_visit_before*/
,
NULL
/*php_phongo_bson_visit_after*/
,
php_phongo_bson_visit_corrupt
,
php_phongo_bson_visit_double
,
php_phongo_bson_visit_utf8
,
php_phongo_bson_visit_document
,
php_phongo_bson_visit_array
,
php_phongo_bson_visit_binary
,
php_phongo_bson_visit_undefined
,
php_phongo_bson_visit_oid
,
php_phongo_bson_visit_bool
,
php_phongo_bson_visit_date_time
,
php_phongo_bson_visit_null
,
php_phongo_bson_visit_regex
,
php_phongo_bson_visit_dbpointer
,
php_phongo_bson_visit_code
,
php_phongo_bson_visit_symbol
,
php_phongo_bson_visit_codewscope
,
php_phongo_bson_visit_int32
,
php_phongo_bson_visit_timestamp
,
php_phongo_bson_visit_int64
,
php_phongo_bson_visit_maxkey
,
php_phongo_bson_visit_minkey
,
php_phongo_bson_visit_unsupported_type
,
php_phongo_bson_visit_decimal128
,
{
NULL
}
};
static
inline
bool
map_element_matches_field_path
(
php_phongo_field_path_map_element
*
map_element
,
php_phongo_field_path
*
current
)
{
size_t
i
;
if
(
map_element
->
entry
->
size
!=
current
->
size
)
{
return
false
;
}
for
(
i
=
0
;
i
<
current
->
size
;
i
++
)
{
if
(
strcmp
(
map_element
->
entry
->
elements
[
i
],
"$"
)
==
0
)
{
continue
;
}
if
(
strcmp
(
map_element
->
entry
->
elements
[
i
],
current
->
elements
[
i
])
!=
0
)
{
return
false
;
}
}
return
true
;
}
static
php_phongo_field_path_map_element
*
map_find_field_path_entry
(
php_phongo_bson_state
*
state
)
{
size_t
i
;
/* Loop over all field path mappings, and for each, try to see whether it matches the current path */
for
(
i
=
0
;
i
<
state
->
map
.
field_paths
.
size
;
i
++
)
{
if
(
map_element_matches_field_path
(
state
->
map
.
field_paths
.
map
[
i
],
state
->
field_path
))
{
return
state
->
map
.
field_paths
.
map
[
i
];
}
}
return
NULL
;
}
static
void
php_phongo_handle_field_path_entry_for_compound_type
(
php_phongo_bson_state
*
state
,
php_phongo_bson_typemap_element
*
element
)
{
php_phongo_field_path_map_element
*
entry
=
map_find_field_path_entry
(
state
);
if
(
entry
)
{
state
->
field_type
.
type
=
entry
->
node
.
type
;
state
->
field_type
.
ce
=
entry
->
node
.
ce
;
}
else
{
state
->
field_type
.
type
=
element
->
type
;
state
->
field_type
.
ce
=
element
->
ce
;
}
}
static
bool
php_phongo_bson_visit_document
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
const
bson_t
*
v_document
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
bson_iter_t
child
;
php_phongo_bson_state
*
parent_state
=
(
php_phongo_bson_state
*
)
data
;
php_phongo_bson_state
state
;
php_phongo_field_path_push
(
parent_state
->
field_path
,
key
,
PHONGO_FIELD_PATH_ITEM_DOCUMENT
);
PHONGO_BSON_INIT_STATE
(
state
);
php_phongo_bson_state_copy_ctor
(
&
state
,
parent_state
);
/* Check for entries in the fieldPath type map key, and use them to
* override the default ones for this type */
php_phongo_handle_field_path_entry_for_compound_type
(
&
state
,
&
state
.
map
.
document
);
/* Only traverse BSON document if we're not returning a raw BSON structure */
if
(
state
.
field_type
.
type
!=
PHONGO_TYPEMAP_BSON
)
{
if
(
!
bson_iter_init
(
&
child
,
v_document
))
{
php_phongo_bson_state_dtor
(
&
state
);
return
false
;
}
array_init
(
&
state
.
zchild
);
if
(
bson_iter_visit_all
(
&
child
,
&
php_bson_visitors
,
&
state
)
||
child
.
err_off
)
{
/* Iteration stopped prematurely due to corruption or a failed
* visitor. Free state.zchild, which we just initialized, and return
* true to stop iteration for our parent context. */
zval_ptr_dtor
(
&
state
.
zchild
);
php_phongo_bson_state_dtor
(
&
state
);
return
true
;
}
}
/* If php_phongo_bson_visit_binary() finds an ODM class, it should
* supersede a default type map and named document class. */
if
(
state
.
odm_ce
&&
state
.
field_type
.
type
==
PHONGO_TYPEMAP_NONE
)
{
state
.
field_type
.
type
=
PHONGO_TYPEMAP_CLASS
;
}
switch
(
state
.
field_type
.
type
)
{
case
PHONGO_TYPEMAP_BSON
:
{
php_phongo_document_t
*
intern
;
object_init_ex
(
&
state
.
zchild
,
php_phongo_document_ce
);
intern
=
Z_DOCUMENT_OBJ_P
(
&
state
.
zchild
);
intern
->
bson
=
bson_copy
(
v_document
);
break
;
}
case
PHONGO_TYPEMAP_NATIVE_ARRAY
:
/* Do nothing, item will be added later */
break
;
case
PHONGO_TYPEMAP_CLASS
:
{
zval
obj
;
zend_class_entry
*
obj_ce
=
state
.
odm_ce
?
state
.
odm_ce
:
state
.
field_type
.
ce
;
object_init_ex
(
&
obj
,
obj_ce
);
zend_call_method_with_1_params
(
PHONGO_COMPAT_OBJ_P
(
&
obj
),
NULL
,
NULL
,
BSON_UNSERIALIZE_FUNC_NAME
,
NULL
,
&
state
.
zchild
);
zval_ptr_dtor
(
&
state
.
zchild
);
ZVAL_COPY_VALUE
(
&
state
.
zchild
,
&
obj
);
break
;
}
case
PHONGO_TYPEMAP_NATIVE_OBJECT
:
default
:
convert_to_object
(
&
state
.
zchild
);
}
if
(
parent_state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
state
.
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
state
.
zchild
);
}
php_phongo_bson_state_dtor
(
&
state
);
php_phongo_field_path_pop
(
parent_state
->
field_path
);
return
false
;
}
static
bool
php_phongo_bson_visit_array
(
const
bson_iter_t
*
iter
ARG_UNUSED
,
const
char
*
key
,
const
bson_t
*
v_array
,
void
*
data
)
{
zval
*
retval
=
PHONGO_BSON_STATE_ZCHILD
(
data
);
bson_iter_t
child
;
php_phongo_bson_state
*
parent_state
=
(
php_phongo_bson_state
*
)
data
;
php_phongo_bson_state
state
;
php_phongo_field_path_push
(
parent_state
->
field_path
,
key
,
PHONGO_FIELD_PATH_ITEM_ARRAY
);
PHONGO_BSON_INIT_STATE
(
state
);
php_phongo_bson_state_copy_ctor
(
&
state
,
parent_state
);
/* Check for entries in the fieldPath type map key, and use them to
* override the default ones for this type */
php_phongo_handle_field_path_entry_for_compound_type
(
&
state
,
&
state
.
map
.
array
);
/* Only traverse BSON array if we're not returning a raw BSON structure */
if
(
state
.
field_type
.
type
!=
PHONGO_TYPEMAP_BSON
)
{
if
(
!
bson_iter_init
(
&
child
,
v_array
))
{
php_phongo_bson_state_dtor
(
&
state
);
return
false
;
}
/* Note that we are visiting an array, so element visitors know to use
* add_next_index() (i.e. disregard BSON keys) instead of add_assoc()
* when building the PHP array.
*/
state
.
is_visiting_array
=
true
;
array_init
(
&
state
.
zchild
);
if
(
bson_iter_visit_all
(
&
child
,
&
php_bson_visitors
,
&
state
)
||
child
.
err_off
)
{
/* Iteration stopped prematurely due to corruption or a failed
* visitor. Free state.zchild, which we just initialized, and return
* true to stop iteration for our parent context. */
zval_ptr_dtor
(
&
state
.
zchild
);
php_phongo_bson_state_dtor
(
&
state
);
return
true
;
}
}
switch
(
state
.
field_type
.
type
)
{
case
PHONGO_TYPEMAP_BSON
:
{
php_phongo_packedarray_t
*
intern
;
object_init_ex
(
&
state
.
zchild
,
php_phongo_packedarray_ce
);
intern
=
Z_PACKEDARRAY_OBJ_P
(
&
state
.
zchild
);
intern
->
bson
=
bson_copy
(
v_array
);
break
;
}
case
PHONGO_TYPEMAP_CLASS
:
{
zval
obj
;
object_init_ex
(
&
obj
,
state
.
field_type
.
ce
);
zend_call_method_with_1_params
(
PHONGO_COMPAT_OBJ_P
(
&
obj
),
NULL
,
NULL
,
BSON_UNSERIALIZE_FUNC_NAME
,
NULL
,
&
state
.
zchild
);
zval_ptr_dtor
(
&
state
.
zchild
);
ZVAL_COPY_VALUE
(
&
state
.
zchild
,
&
obj
);
break
;
}
case
PHONGO_TYPEMAP_NATIVE_OBJECT
:
convert_to_object
(
&
state
.
zchild
);
break
;
case
PHONGO_TYPEMAP_NATIVE_ARRAY
:
default
:
/* Do nothing, will be added later */
break
;
}
if
(
parent_state
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
state
.
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
state
.
zchild
);
}
php_phongo_bson_state_dtor
(
&
state
);
php_phongo_field_path_pop
(
parent_state
->
field_path
);
return
false
;
}
/* Converts a BSON document to a PHP value using the default typemap. */
bool
php_phongo_bson_to_zval
(
const
bson_t
*
b
,
zval
*
zv
)
{
bool
retval
;
php_phongo_bson_state
state
;
PHONGO_BSON_INIT_STATE
(
state
);
retval
=
php_phongo_bson_to_zval_ex
(
b
,
&
state
);
ZVAL_ZVAL
(
zv
,
&
state
.
zchild
,
1
,
1
);
return
retval
;
}
/* Converts BSON data to a PHP value using the default typemap. */
bool
php_phongo_bson_data_to_zval
(
const
unsigned
char
*
data
,
int
data_len
,
zval
*
zv
)
{
bool
retval
;
php_phongo_bson_state
state
;
PHONGO_BSON_INIT_STATE
(
state
);
retval
=
php_phongo_bson_data_to_zval_ex
(
data
,
data_len
,
&
state
);
ZVAL_ZVAL
(
zv
,
&
state
.
zchild
,
1
,
1
);
return
retval
;
}
/* Converts a BSON value to a zval, returning BSON objects and arrays as
* standard PHP types instead of Document or PackedArray instances.
*
* On success, the zval will be populated and true will be returned. On error,
* an exception will have been thrown and false will be returned. */
bool
phongo_bson_value_to_zval_legacy
(
const
bson_value_t
*
value
,
zval
*
zv
)
{
if
(
value
->
value_type
==
BSON_TYPE_ARRAY
||
value
->
value_type
==
BSON_TYPE_DOCUMENT
)
{
bson_t
bson
=
BSON_INITIALIZER
;
php_phongo_bson_state
state
;
zval
*
return_value
;
bool
retval
=
false
;
/* Use php_phongo_bson_to_zval_ex internally to convert arrays and documents */
PHONGO_BSON_INIT_STATE
(
state
);
state
.
map
.
root
.
type
=
PHONGO_TYPEMAP_NATIVE_ARRAY
;
bson_append_value
(
&
bson
,
"data"
,
4
,
value
);
if
(
!
php_phongo_bson_to_zval_ex
(
&
bson
,
&
state
))
{
/* Exception already thrown */
goto
cleanup
;
}
retval
=
true
;
return_value
=
php_array_fetchc
(
&
state
.
zchild
,
"data"
);
if
(
return_value
)
{
ZVAL_ZVAL
(
zv
,
return_value
,
1
,
0
);
}
cleanup
:
zval_ptr_dtor
(
&
state
.
zchild
);
return
retval
;
}
return
phongo_bson_value_to_zval
(
value
,
zv
);
}
/* Converts a BSON value to a zval.
*
* On success, the zval will be populated and true will be returned. On error,
* an exception will have been thrown and false will be returned. */
bool
phongo_bson_value_to_zval
(
const
bson_value_t
*
value
,
zval
*
zv
)
{
bson_t
bson
=
BSON_INITIALIZER
;
switch
(
value
->
value_type
)
{
case
BSON_TYPE_INT32
:
ZVAL_LONG
(
zv
,
value
->
value
.
v_int32
);
return
true
;
case
BSON_TYPE_INT64
:
phongo_int64_new
(
zv
,
value
->
value
.
v_int64
);
return
true
;
case
BSON_TYPE_DOUBLE
:
ZVAL_DOUBLE
(
zv
,
value
->
value
.
v_double
);
return
true
;
case
BSON_TYPE_BOOL
:
ZVAL_BOOL
(
zv
,
value
->
value
.
v_bool
);
return
true
;
case
BSON_TYPE_NULL
:
ZVAL_NULL
(
zv
);
return
true
;
case
BSON_TYPE_UTF8
:
ZVAL_STRINGL
(
zv
,
value
->
value
.
v_utf8
.
str
,
value
->
value
.
v_utf8
.
len
);
return
true
;
case
BSON_TYPE_BINARY
:
return
phongo_binary_new
(
zv
,
(
char
*
)
value
->
value
.
v_binary
.
data
,
value
->
value
.
v_binary
.
data_len
,
value
->
value
.
v_binary
.
subtype
);
case
BSON_TYPE_OID
:
return
phongo_objectid_new
(
zv
,
&
value
->
value
.
v_oid
);
case
BSON_TYPE_DATE_TIME
:
return
phongo_utcdatetime_new
(
zv
,
value
->
value
.
v_datetime
);
case
BSON_TYPE_REGEX
:
return
phongo_regex_new
(
zv
,
value
->
value
.
v_regex
.
regex
,
value
->
value
.
v_regex
.
options
);
case
BSON_TYPE_DBPOINTER
:
return
phongo_dbpointer_new
(
zv
,
value
->
value
.
v_dbpointer
.
collection
,
value
->
value
.
v_dbpointer
.
collection_len
,
&
value
->
value
.
v_dbpointer
.
oid
);
case
BSON_TYPE_CODE
:
return
phongo_javascript_new
(
zv
,
value
->
value
.
v_code
.
code
,
value
->
value
.
v_code
.
code_len
,
NULL
);
case
BSON_TYPE_CODEWSCOPE
:
if
(
!
bson_init_static
(
&
bson
,
value
->
value
.
v_codewscope
.
scope_data
,
value
->
value
.
v_codewscope
.
scope_len
))
{
phongo_throw_exception
(
PHONGO_ERROR_UNEXPECTED_VALUE
,
"Invalid BSON received for BSON_TYPE_CODEWSCOPE"
);
return
false
;
}
return
phongo_javascript_new
(
zv
,
value
->
value
.
v_codewscope
.
code
,
value
->
value
.
v_codewscope
.
code_len
,
&
bson
);
case
BSON_TYPE_SYMBOL
:
return
phongo_symbol_new
(
zv
,
value
->
value
.
v_symbol
.
symbol
,
value
->
value
.
v_symbol
.
len
);
case
BSON_TYPE_TIMESTAMP
:
return
phongo_timestamp_new
(
zv
,
value
->
value
.
v_timestamp
.
increment
,
value
->
value
.
v_timestamp
.
timestamp
);
case
BSON_TYPE_DECIMAL128
:
return
phongo_decimal128_new
(
zv
,
&
value
->
value
.
v_decimal128
);
case
BSON_TYPE_MAXKEY
:
return
object_init_ex
(
zv
,
php_phongo_maxkey_ce
)
==
SUCCESS
;
case
BSON_TYPE_MINKEY
:
return
object_init_ex
(
zv
,
php_phongo_minkey_ce
)
==
SUCCESS
;
case
BSON_TYPE_EOD
:
ZVAL_NULL
(
zv
);
return
true
;
case
BSON_TYPE_UNDEFINED
:
return
object_init_ex
(
zv
,
php_phongo_undefined_ce
)
==
SUCCESS
;
case
BSON_TYPE_ARRAY
:
if
(
!
bson_init_static
(
&
bson
,
value
->
value
.
v_doc
.
data
,
value
->
value
.
v_doc
.
data_len
))
{
phongo_throw_exception
(
PHONGO_ERROR_UNEXPECTED_VALUE
,
"Invalid BSON received for BSON_TYPE_ARRAY"
);
return
false
;
}
return
phongo_packedarray_new
(
zv
,
&
bson
,
true
);
case
BSON_TYPE_DOCUMENT
:
if
(
!
bson_init_static
(
&
bson
,
value
->
value
.
v_doc
.
data
,
value
->
value
.
v_doc
.
data_len
))
{
phongo_throw_exception
(
PHONGO_ERROR_UNEXPECTED_VALUE
,
"Invalid BSON received for BSON_TYPE_DOCUMENT"
);
return
false
;
}
return
phongo_document_new
(
zv
,
&
bson
,
true
);
default
:
ZVAL_UNDEF
(
zv
);
phongo_throw_exception
(
PHONGO_ERROR_UNEXPECTED_VALUE
,
"Detected unsupported BSON type %d"
,
value
->
value_type
);
return
false
;
}
}
/* Converts a BSON document to a PHP value according to the typemap specified in
* the state argument.
*
* On success, the result will be set on the state argument and true will be
* returned. On error, an exception will have been thrown and false will be
* returned.
*
* Note: the result zval in the state argument will always be initialized for
* PHP 5.x so that the caller may always zval_ptr_dtor() it. The zval is left
* as-is on PHP 7; however, it should have the type undefined if the state
* was initialized to zero.
*/
bool
php_phongo_bson_to_zval_ex
(
const
bson_t
*
b
,
php_phongo_bson_state
*
state
)
{
bson_iter_t
iter
;
bool
retval
=
false
;
bool
must_dtor_state
=
false
;
if
(
!
php_phongo_bson_state_is_initialized
(
state
))
{
php_phongo_bson_state_ctor
(
state
);
must_dtor_state
=
true
;
}
// Handle BSON root type early to avoid creating an iterator and visiting elements
if
(
state
->
map
.
root
.
type
==
PHONGO_TYPEMAP_BSON
)
{
zval
obj
;
bson_t
**
bson
;
if
(
state
->
is_visiting_array
)
{
object_init_ex
(
&
obj
,
php_phongo_packedarray_ce
);
bson
=
&
Z_PACKEDARRAY_OBJ_P
(
&
obj
)
->
bson
;
}
else
{
object_init_ex
(
&
obj
,
php_phongo_document_ce
);
bson
=
&
Z_DOCUMENT_OBJ_P
(
&
obj
)
->
bson
;
}
*
bson
=
bson_copy
(
b
);
zval_ptr_dtor
(
&
state
->
zchild
);
ZVAL_COPY_VALUE
(
&
state
->
zchild
,
&
obj
);
retval
=
true
;
goto
cleanup
;
}
if
(
!
bson_iter_init
(
&
iter
,
b
))
{
phongo_throw_exception
(
PHONGO_ERROR_UNEXPECTED_VALUE
,
"Could not initialize BSON iterator"
);
goto
cleanup
;
}
/* We initialize an array because it will either be returned as-is (native
* array in type map), passed to bsonUnserialize() (ODM class), or used to
* initialize a stdClass object (native object in type map). */
array_init
(
&
state
->
zchild
);
if
(
bson_iter_visit_all
(
&
iter
,
&
php_bson_visitors
,
state
)
||
iter
.
err_off
)
{
/* Iteration stopped prematurely due to corruption or a failed visitor.
* While we free the reader, state->zchild should be left as-is, since
* the calling code may want to zval_ptr_dtor() it. If an exception has
* been thrown already (due to an unsupported BSON type for example,
* don't overwrite with a generic exception message. */
if
(
!
EG
(
exception
))
{
char
*
path
=
php_phongo_field_path_as_string
(
state
->
field_path
);
phongo_throw_exception
(
PHONGO_ERROR_UNEXPECTED_VALUE
,
"Detected corrupt BSON data for field path '%s' at offset %d"
,
path
,
iter
.
err_off
);
efree
(
path
);
}
goto
cleanup
;
}
/* If the root document is an array, default to a native array type.
* If php_phongo_bson_visit_binary() found an ODM class, it should supersede
* a default type map and named root class. */
if
(
state
->
map
.
root
.
type
==
PHONGO_TYPEMAP_NONE
)
{
if
(
state
->
is_visiting_array
)
{
state
->
map
.
root
.
type
=
PHONGO_TYPEMAP_NATIVE_ARRAY
;
}
else
if
(
state
->
odm_ce
)
{
state
->
map
.
root
.
type
=
PHONGO_TYPEMAP_CLASS
;
}
}
switch
(
state
->
map
.
root
.
type
)
{
case
PHONGO_TYPEMAP_NATIVE_ARRAY
:
/* Nothing to do here */
break
;
case
PHONGO_TYPEMAP_CLASS
:
{
zval
obj
;
zend_class_entry
*
obj_ce
=
state
->
odm_ce
?
state
->
odm_ce
:
state
->
map
.
root
.
ce
;
object_init_ex
(
&
obj
,
obj_ce
);
zend_call_method_with_1_params
(
PHONGO_COMPAT_OBJ_P
(
&
obj
),
NULL
,
NULL
,
BSON_UNSERIALIZE_FUNC_NAME
,
NULL
,
&
state
->
zchild
);
zval_ptr_dtor
(
&
state
->
zchild
);
ZVAL_COPY_VALUE
(
&
state
->
zchild
,
&
obj
);
break
;
}
case
PHONGO_TYPEMAP_NATIVE_OBJECT
:
default
:
convert_to_object
(
&
state
->
zchild
);
}
retval
=
true
;
cleanup
:
if
(
must_dtor_state
)
{
php_phongo_bson_state_dtor
(
state
);
}
return
retval
;
}
/* Converts a BSON document to a PHP value according to the typemap specified in
* the state argument.
*
* On success, the result will be set on the state argument and true will be
* returned. On error, an exception will have been thrown and false will be
* returned.
*
* Note: the result zval in the state argument will always be initialized for
* PHP 5.x so that the caller may always zval_ptr_dtor() it. The zval is left
* as-is on PHP 7; however, it should have the type undefined if the state
* was initialized to zero.
*/
bool
php_phongo_bson_data_to_zval_ex
(
const
unsigned
char
*
data
,
int
data_len
,
php_phongo_bson_state
*
state
)
{
bson_reader_t
*
reader
=
NULL
;
const
bson_t
*
b
;
bool
eof
=
false
;
bool
retval
=
false
;
reader
=
bson_reader_new_from_data
(
data
,
data_len
);
if
(
!
(
b
=
bson_reader_read
(
reader
,
NULL
)))
{
phongo_throw_exception
(
PHONGO_ERROR_UNEXPECTED_VALUE
,
"Could not read document from BSON reader"
);
goto
cleanup
;
}
retval
=
php_phongo_bson_to_zval_ex
(
b
,
state
);
if
(
bson_reader_read
(
reader
,
&
eof
)
||
!
eof
)
{
phongo_throw_exception
(
PHONGO_ERROR_UNEXPECTED_VALUE
,
"Reading document did not exhaust input buffer"
);
retval
=
false
;
goto
cleanup
;
}
cleanup
:
if
(
reader
)
{
bson_reader_destroy
(
reader
);
}
return
retval
;
}
/* Fetches a zend_class_entry for the given class name and checks that it is
* also instantiatable and implements a specified interface. Returns the class
* on success; otherwise, NULL is returned and an exception is thrown. */
static
zend_class_entry
*
php_phongo_bson_state_fetch_class
(
const
char
*
classname
,
int
classname_len
,
zend_class_entry
*
interface_ce
)
{
zend_string
*
zs_classname
=
zend_string_init
(
classname
,
classname_len
,
0
);
zend_class_entry
*
found_ce
=
zend_fetch_class
(
zs_classname
,
ZEND_FETCH_CLASS_AUTO
|
ZEND_FETCH_CLASS_SILENT
);
zend_string_release
(
zs_classname
);
if
(
!
found_ce
)
{
phongo_throw_exception
(
PHONGO_ERROR_INVALID_ARGUMENT
,
"Class %s does not exist"
,
classname
);
}
else
if
(
!
phongo_is_class_instantiatable
(
found_ce
))
{
phongo_throw_exception
(
PHONGO_ERROR_INVALID_ARGUMENT
,
"%s %s is not instantiatable"
,
zend_get_object_type_uc
(
found_ce
),
classname
);
}
else
if
(
!
instanceof_function
(
found_ce
,
interface_ce
))
{
phongo_throw_exception
(
PHONGO_ERROR_INVALID_ARGUMENT
,
"Class %s does not implement %s"
,
classname
,
ZSTR_VAL
(
interface_ce
->
name
));
}
else
{
return
found_ce
;
}
return
NULL
;
}
/* Parses a BSON type (i.e. array, document, or root). On success, the type and
* type_ce output arguments will be assigned and true will be returned;
* otherwise, false is returned and an exception is thrown. */
static
bool
php_phongo_bson_state_parse_type
(
zval
*
options
,
const
char
*
name
,
php_phongo_bson_typemap_element
*
element
)
{
char
*
type
;
int
type_len
;
zend_bool
type_free
=
0
;
bool
retval
=
true
;
type
=
php_array_fetch_string
(
options
,
name
,
&
type_len
,
&
type_free
);
if
(
!
type_len
)
{
goto
cleanup
;
}
if
(
!
strcasecmp
(
type
,
PHONGO_TYPEMAP_TYPE_STR_ARRAY
))
{
element
->
type
=
PHONGO_TYPEMAP_NATIVE_ARRAY
;
element
->
ce
=
NULL
;
}
else
if
(
!
strcasecmp
(
type
,
PHONGO_TYPEMAP_TYPE_STR_BSON
))
{
element
->
type
=
PHONGO_TYPEMAP_BSON
;
element
->
ce
=
NULL
;
}
else
if
(
!
strcasecmp
(
type
,
PHONGO_TYPEMAP_TYPE_STR_STDCLASS
)
||
!
strcasecmp
(
type
,
PHONGO_TYPEMAP_TYPE_STR_OBJECT
))
{
element
->
type
=
PHONGO_TYPEMAP_NATIVE_OBJECT
;
element
->
ce
=
NULL
;
}
else
{
if
((
element
->
ce
=
php_phongo_bson_state_fetch_class
(
type
,
type_len
,
php_phongo_unserializable_ce
)))
{
element
->
type
=
PHONGO_TYPEMAP_CLASS
;
}
else
{
/* Exception already thrown */
retval
=
false
;
}
}
cleanup
:
if
(
type_free
)
{
efree
(
type
);
}
return
retval
;
}
static
void
field_path_map_element_set_info
(
php_phongo_field_path_map_element
*
element
,
php_phongo_bson_typemap_element
*
typemap_element
)
{
element
->
node
.
type
=
typemap_element
->
type
;
element
->
node
.
ce
=
typemap_element
->
ce
;
}
static
void
map_add_field_path_element
(
php_phongo_bson_typemap
*
map
,
php_phongo_field_path_map_element
*
element
)
{
/* Make sure we have allocated enough */
if
(
map
->
field_paths
.
allocated_size
<
map
->
field_paths
.
size
+
1
)
{
map
->
field_paths
.
allocated_size
+=
PHONGO_FIELD_PATH_EXPANSION
;
map
->
field_paths
.
map
=
erealloc
(
map
->
field_paths
.
map
,
sizeof
(
php_phongo_field_path_map_element
)
*
map
->
field_paths
.
allocated_size
);
}
map
->
field_paths
.
map
[
map
->
field_paths
.
size
]
=
element
;
map
->
field_paths
.
size
++
;
}
static
php_phongo_field_path_map_element
*
field_path_map_element_alloc
(
void
)
{
php_phongo_field_path_map_element
*
tmp
=
ecalloc
(
1
,
sizeof
(
php_phongo_field_path_map_element
));
tmp
->
entry
=
php_phongo_field_path_alloc
(
true
);
return
tmp
;
}
static
void
field_path_map_element_dtor
(
php_phongo_field_path_map_element
*
element
)
{
php_phongo_field_path_free
(
element
->
entry
);
efree
(
element
);
}
static
bool
php_phongo_bson_state_add_field_path
(
php_phongo_bson_typemap
*
map
,
char
*
field_path_original
,
php_phongo_bson_typemap_element
*
typemap_element
)
{
char
*
ptr
=
NULL
;
char
*
segment_end
=
NULL
;
php_phongo_field_path_map_element
*
field_path_map_element
;
if
(
field_path_original
[
0
]
==
'.'
)
{
phongo_throw_exception
(
PHONGO_ERROR_INVALID_ARGUMENT
,
"A 'fieldPaths' key may not start with a '.'"
);
return
false
;
}
if
(
field_path_original
[
strlen
(
field_path_original
)
-
1
]
==
'.'
)
{
phongo_throw_exception
(
PHONGO_ERROR_INVALID_ARGUMENT
,
"A 'fieldPaths' key may not end with a '.'"
);
return
false
;
}
field_path_map_element
=
field_path_map_element_alloc
();
ptr
=
field_path_original
;
/* Loop over all the segments. A segment is delimited by a "." */
while
((
segment_end
=
strchr
(
ptr
,
'.'
))
!=
NULL
)
{
char
*
tmp
=
NULL
;
/* Bail out if we have an empty segment */
if
(
ptr
==
segment_end
)
{
field_path_map_element_dtor
(
field_path_map_element
);
phongo_throw_exception
(
PHONGO_ERROR_INVALID_ARGUMENT
,
"A 'fieldPaths' key may not have an empty segment"
);
return
false
;
}
tmp
=
calloc
(
1
,
segment_end
-
ptr
+
1
);
memcpy
(
tmp
,
ptr
,
segment_end
-
ptr
);
php_phongo_field_path_push
(
field_path_map_element
->
entry
,
tmp
,
PHONGO_FIELD_PATH_ITEM_NONE
);
free
(
tmp
);
ptr
=
segment_end
+
1
;
}
/* Add the last (or single) element */
php_phongo_field_path_push
(
field_path_map_element
->
entry
,
ptr
,
PHONGO_FIELD_PATH_ITEM_NONE
);
field_path_map_element_set_info
(
field_path_map_element
,
typemap_element
);
map_add_field_path_element
(
map
,
field_path_map_element
);
return
true
;
}
void
php_phongo_bson_typemap_dtor
(
php_phongo_bson_typemap
*
map
)
{
size_t
i
;
if
(
map
->
field_paths
.
map
)
{
for
(
i
=
0
;
i
<
map
->
field_paths
.
size
;
i
++
)
{
field_path_map_element_dtor
(
map
->
field_paths
.
map
[
i
]);
}
efree
(
map
->
field_paths
.
map
);
}
map
->
field_paths
.
map
=
NULL
;
}
/* Loops over each element in the fieldPaths array (if exists, and is an
* array), and then checks whether each element is a valid type mapping */
static
bool
php_phongo_bson_state_parse_fieldpaths
(
zval
*
typemap
,
php_phongo_bson_typemap
*
map
)
{
zval
*
fieldpaths
=
NULL
;
HashTable
*
ht_data
;
if
(
!
php_array_existsc
(
typemap
,
"fieldPaths"
))
{
return
true
;
}
fieldpaths
=
php_array_fetchc_array
(
typemap
,
"fieldPaths"
);
if
(
!
fieldpaths
)
{
phongo_throw_exception
(
PHONGO_ERROR_INVALID_ARGUMENT
,
"The 'fieldPaths' element is not an array"
);
return
false
;
}
ht_data
=
HASH_OF
(
fieldpaths
);
{
zend_string
*
string_key
=
NULL
;
zend_ulong
num_key
=
0
;
zval
*
property
;
ZEND_HASH_FOREACH_KEY_VAL
(
ht_data
,
num_key
,
string_key
,
property
)
{
php_phongo_bson_typemap_element
element
;
if
(
!
string_key
)
{
phongo_throw_exception
(
PHONGO_ERROR_INVALID_ARGUMENT
,
"The 'fieldPaths' element is not an associative array"
);
return
false
;
}
if
(
strcmp
(
ZSTR_VAL
(
string_key
),
""
)
==
0
)
{
phongo_throw_exception
(
PHONGO_ERROR_INVALID_ARGUMENT
,
"The 'fieldPaths' element may not be an empty string"
);
return
false
;
}
if
(
!
php_phongo_bson_state_parse_type
(
fieldpaths
,
ZSTR_VAL
(
string_key
),
&
element
))
{
return
false
;
}
if
(
!
php_phongo_bson_state_add_field_path
(
map
,
ZSTR_VAL
(
string_key
),
&
element
))
{
return
false
;
}
}
ZEND_HASH_FOREACH_END
();
}
return
true
;
}
/* Applies the array argument to a typemap struct. Returns true on success;
* otherwise, false is returned an an exception is thrown. */
bool
php_phongo_bson_typemap_to_state
(
zval
*
typemap
,
php_phongo_bson_typemap
*
map
)
{
if
(
!
typemap
)
{
return
true
;
}
if
(
!
php_phongo_bson_state_parse_type
(
typemap
,
PHONGO_TYPEMAP_FIELD_STR_ARRAY
,
&
map
->
array
)
||
!
php_phongo_bson_state_parse_type
(
typemap
,
PHONGO_TYPEMAP_FIELD_STR_DOCUMENT
,
&
map
->
document
)
||
!
php_phongo_bson_state_parse_type
(
typemap
,
PHONGO_TYPEMAP_FIELD_STR_ROOT
,
&
map
->
root
)
||
!
php_phongo_bson_state_parse_fieldpaths
(
typemap
,
map
))
{
/* Exception should already have been thrown */
return
false
;
}
return
true
;
}
bool
php_phongo_bson_to_json
(
zval
*
return_value
,
const
bson_t
*
bson
,
php_phongo_json_mode_t
mode
)
{
char
*
json
=
NULL
;
size_t
json_len
;
if
(
mode
==
PHONGO_JSON_MODE_LEGACY
)
{
json
=
bson_as_json
(
bson
,
&
json_len
);
}
else
if
(
mode
==
PHONGO_JSON_MODE_CANONICAL
)
{
json
=
bson_as_canonical_extended_json
(
bson
,
&
json_len
);
}
else
if
(
mode
==
PHONGO_JSON_MODE_RELAXED
)
{
json
=
bson_as_relaxed_extended_json
(
bson
,
&
json_len
);
}
if
(
!
json
)
{
phongo_throw_exception
(
PHONGO_ERROR_UNEXPECTED_VALUE
,
"Could not convert BSON document to a JSON string"
);
return
false
;
}
ZVAL_STRINGL
(
return_value
,
json
,
json_len
);
bson_free
(
json
);
return
true
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sun, Nov 15, 3:01 AM (17 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
151741
Default Alt Text
phongo_bson.c (43 KB)
Attached To
Mode
R114 php-mongodb
Attached
Detach File
Event Timeline