Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F622686
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
40 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>
#include
<Zend/zend_interfaces.h>
#include
"php_array_api.h"
#include
"php_phongo.h"
#include
"phongo_bson.h"
#include
"phongo_error.h"
#undef MONGOC_LOG_DOMAIN
#define MONGOC_LOG_DOMAIN "PHONGO-BSON"
#define PHONGO_IS_CLASS_INSTANTIATABLE(ce) \
(!(ce->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)))
#define PHONGO_BSON_STATE_ZCHILD(state) (&((php_phongo_bson_state*) (state))->zchild)
#define PHONGO_FIELD_PATH_EXPANSION 8
/* 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_document
,
void
*
data
);
/* 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
void
php_phongo_bson_new_binary_from_binary_and_type
(
zval
*
object
,
const
char
*
data
,
size_t
data_len
,
bson_subtype_t
type
)
/* {{{ */
{
php_phongo_binary_t
*
intern
;
object_init_ex
(
object
,
php_phongo_binary_ce
);
intern
=
Z_BINARY_OBJ_P
(
object
);
intern
->
data
=
estrndup
(
data
,
data_len
);
intern
->
data_len
=
data_len
;
intern
->
type
=
(
uint8_t
)
type
;
}
/* }}} */
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
=
found_ce
;
}
}
{
zval
zchild
;
php_phongo_bson_new_binary_from_binary_and_type
(
&
zchild
,
(
const
char
*
)
v_binary
,
v_binary_len
,
v_subtype
);
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
void
php_phongo_objectid_new_from_oid
(
zval
*
object
,
const
bson_oid_t
*
oid
)
/* {{{ */
{
php_phongo_objectid_t
*
intern
;
object_init_ex
(
object
,
php_phongo_objectid_ce
);
intern
=
Z_OBJECTID_OBJ_P
(
object
);
bson_oid_to_string
(
oid
,
intern
->
oid
);
intern
->
initialized
=
true
;
}
/* }}} */
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
;
php_phongo_objectid_new_from_oid
(
&
zchild
,
v_oid
);
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
void
php_phongo_bson_new_utcdatetime_from_epoch
(
zval
*
object
,
int64_t
msec_since_epoch
)
/* {{{ */
{
php_phongo_utcdatetime_t
*
intern
;
object_init_ex
(
object
,
php_phongo_utcdatetime_ce
);
intern
=
Z_UTCDATETIME_OBJ_P
(
object
);
intern
->
milliseconds
=
msec_since_epoch
;
intern
->
initialized
=
true
;
}
/* }}} */
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
;
php_phongo_bson_new_utcdatetime_from_epoch
(
&
zchild
,
msec_since_epoch
);
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
void
php_phongo_bson_new_decimal128
(
zval
*
object
,
const
bson_decimal128_t
*
decimal
)
/* {{{ */
{
php_phongo_decimal128_t
*
intern
;
object_init_ex
(
object
,
php_phongo_decimal128_ce
);
intern
=
Z_DECIMAL128_OBJ_P
(
object
);
memcpy
(
&
intern
->
decimal
,
decimal
,
sizeof
(
bson_decimal128_t
));
intern
->
initialized
=
true
;
}
/* }}} */
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
;
php_phongo_bson_new_decimal128
(
&
zchild
,
decimal
);
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
void
php_phongo_bson_new_regex_from_regex_and_options
(
zval
*
object
,
const
char
*
pattern
,
const
char
*
flags
)
/* {{{ */
{
php_phongo_regex_t
*
intern
;
object_init_ex
(
object
,
php_phongo_regex_ce
);
intern
=
Z_REGEX_OBJ_P
(
object
);
intern
->
pattern_len
=
strlen
(
pattern
);
intern
->
pattern
=
estrndup
(
pattern
,
intern
->
pattern_len
);
intern
->
flags_len
=
strlen
(
flags
);
intern
->
flags
=
estrndup
(
flags
,
intern
->
flags_len
);
}
/* }}} */
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
;
php_phongo_bson_new_regex_from_regex_and_options
(
&
zchild
,
v_regex
,
v_options
);
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
void
php_phongo_bson_new_symbol
(
zval
*
object
,
const
char
*
symbol
,
size_t
symbol_len
)
/* {{{ */
{
php_phongo_symbol_t
*
intern
;
object_init_ex
(
object
,
php_phongo_symbol_ce
);
intern
=
Z_SYMBOL_OBJ_P
(
object
);
intern
->
symbol
=
estrndup
(
symbol
,
symbol_len
);
intern
->
symbol_len
=
symbol_len
;
}
/* }}} */
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
;
php_phongo_bson_new_symbol
(
&
zchild
,
v_symbol
,
v_symbol_len
);
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_new_javascript_from_javascript_and_scope
(
zval
*
object
,
const
char
*
code
,
size_t
code_len
,
const
bson_t
*
scope
)
/* {{{ */
{
php_phongo_javascript_t
*
intern
;
if
(
scope
)
{
php_phongo_bson_state
state
;
bool
valid_scope
;
PHONGO_BSON_INIT_STATE
(
state
);
valid_scope
=
php_phongo_bson_to_zval_ex
(
bson_get_data
(
scope
),
scope
->
len
,
&
state
);
zval_ptr_dtor
(
&
state
.
zchild
);
if
(
!
valid_scope
)
{
return
false
;
}
}
object_init_ex
(
object
,
php_phongo_javascript_ce
);
intern
=
Z_JAVASCRIPT_OBJ_P
(
object
);
intern
->
code
=
estrndup
(
code
,
code_len
);
intern
->
code_len
=
code_len
;
intern
->
scope
=
scope
?
bson_copy
(
scope
)
:
NULL
;
return
true
;
}
/* }}} */
static
bool
php_phongo_bson_new_javascript_from_javascript
(
zval
*
object
,
const
char
*
code
,
size_t
code_len
)
/* {{{ */
{
return
php_phongo_bson_new_javascript_from_javascript_and_scope
(
object
,
code
,
code_len
,
NULL
);
}
/* }}} */
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
(
!
php_phongo_bson_new_javascript_from_javascript
(
&
zchild
,
v_code
,
v_code_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
void
php_phongo_bson_new_dbpointer
(
zval
*
object
,
const
char
*
ref
,
size_t
ref_len
,
const
bson_oid_t
*
oid
)
/* {{{ */
{
php_phongo_dbpointer_t
*
intern
;
object_init_ex
(
object
,
php_phongo_dbpointer_ce
);
intern
=
Z_DBPOINTER_OBJ_P
(
object
);
intern
->
ref
=
estrndup
(
ref
,
ref_len
);
intern
->
ref_len
=
ref_len
;
bson_oid_to_string
(
oid
,
intern
->
id
);
}
/* }}} */
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
;
php_phongo_bson_new_dbpointer
(
&
zchild
,
namespace
,
namespace_len
,
oid
);
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
(
!
php_phongo_bson_new_javascript_from_javascript_and_scope
(
&
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
;
php_phongo_bson_new_timestamp_from_increment_and_timestamp
(
&
zchild
,
v_increment
,
v_timestamp
);
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
)
{
ADD_NEXT_INDEX_INT64
(
retval
,
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_types
*
type
,
zend_class_entry
**
ce
)
{
php_phongo_field_path_map_element
*
entry
=
map_find_field_path_entry
(
state
);
if
(
entry
)
{
switch
(
entry
->
node_type
)
{
case
PHONGO_TYPEMAP_NATIVE_ARRAY
:
case
PHONGO_TYPEMAP_NATIVE_OBJECT
:
*
type
=
entry
->
node_type
;
break
;
case
PHONGO_TYPEMAP_CLASS
:
*
type
=
entry
->
node_type
;
*
ce
=
entry
->
node_ce
;
break
;
default
:
/* Do nothing - pacify compiler */
break
;
}
}
}
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_field_path_push
(
parent_state
->
field_path
,
key
,
PHONGO_FIELD_PATH_ITEM_DOCUMENT
);
if
(
bson_iter_init
(
&
child
,
v_document
))
{
php_phongo_bson_state
state
;
PHONGO_BSON_INIT_STATE
(
state
);
php_phongo_bson_state_copy_ctor
(
&
state
,
parent_state
);
array_init
(
&
state
.
zchild
);
if
(
!
bson_iter_visit_all
(
&
child
,
&
php_bson_visitors
,
&
state
)
&&
!
child
.
err_off
)
{
/* 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_type
,
&
state
.
map
.
document
);
/* If php_phongo_bson_visit_binary() finds an ODM class, it should
* supersede a default type map and named document class. */
if
(
state
.
odm
&&
state
.
map
.
document_type
==
PHONGO_TYPEMAP_NONE
)
{
state
.
map
.
document_type
=
PHONGO_TYPEMAP_CLASS
;
}
switch
(
state
.
map
.
document_type
)
{
case
PHONGO_TYPEMAP_NATIVE_ARRAY
:
if
(((
php_phongo_bson_state
*
)
data
)
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
state
.
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
state
.
zchild
);
}
break
;
case
PHONGO_TYPEMAP_CLASS
:
{
zval
obj
;
object_init_ex
(
&
obj
,
state
.
odm
?
state
.
odm
:
state
.
map
.
document
);
zend_call_method_with_1_params
(
PHONGO_COMPAT_OBJ_P
(
&
obj
),
NULL
,
NULL
,
BSON_UNSERIALIZE_FUNC_NAME
,
NULL
,
&
state
.
zchild
);
if
(((
php_phongo_bson_state
*
)
data
)
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
obj
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
obj
);
}
zval_ptr_dtor
(
&
state
.
zchild
);
break
;
}
case
PHONGO_TYPEMAP_NATIVE_OBJECT
:
default
:
convert_to_object
(
&
state
.
zchild
);
if
(((
php_phongo_bson_state
*
)
data
)
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
state
.
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
state
.
zchild
);
}
}
}
else
{
/* 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
;
}
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_field_path_push
(
parent_state
->
field_path
,
key
,
PHONGO_FIELD_PATH_ITEM_ARRAY
);
if
(
bson_iter_init
(
&
child
,
v_array
))
{
php_phongo_bson_state
state
;
PHONGO_BSON_INIT_STATE
(
state
);
php_phongo_bson_state_copy_ctor
(
&
state
,
parent_state
);
/* 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
)
{
/* 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_type
,
&
state
.
map
.
array
);
switch
(
state
.
map
.
array_type
)
{
case
PHONGO_TYPEMAP_CLASS
:
{
zval
obj
;
object_init_ex
(
&
obj
,
state
.
map
.
array
);
zend_call_method_with_1_params
(
PHONGO_COMPAT_OBJ_P
(
&
obj
),
NULL
,
NULL
,
BSON_UNSERIALIZE_FUNC_NAME
,
NULL
,
&
state
.
zchild
);
if
(((
php_phongo_bson_state
*
)
data
)
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
obj
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
obj
);
}
zval_ptr_dtor
(
&
state
.
zchild
);
break
;
}
case
PHONGO_TYPEMAP_NATIVE_OBJECT
:
convert_to_object
(
&
state
.
zchild
);
if
(((
php_phongo_bson_state
*
)
data
)
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
state
.
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
state
.
zchild
);
}
break
;
case
PHONGO_TYPEMAP_NATIVE_ARRAY
:
default
:
if
(((
php_phongo_bson_state
*
)
data
)
->
is_visiting_array
)
{
add_next_index_zval
(
retval
,
&
state
.
zchild
);
}
else
{
ADD_ASSOC_ZVAL
(
retval
,
key
,
&
state
.
zchild
);
}
break
;
}
}
else
{
/* 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
;
}
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
unsigned
char
*
data
,
int
data_len
,
zval
*
zv
)
/* {{{ */
{
bool
retval
;
php_phongo_bson_state
state
;
PHONGO_BSON_INIT_STATE
(
state
);
retval
=
php_phongo_bson_to_zval_ex
(
data
,
data_len
,
&
state
);
ZVAL_ZVAL
(
zv
,
&
state
.
zchild
,
1
,
1
);
return
retval
;
}
/* }}} */
/* Converts a BSON value to a ZVAL. */
bool
php_phongo_bson_value_to_zval
(
const
bson_value_t
*
value
,
zval
*
zv
)
/* {{{ */
{
bson_t
bson
=
BSON_INITIALIZER
;
php_phongo_bson_state
state
;
zval
*
return_value
;
bool
retval
=
false
;
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_get_data
(
&
bson
),
bson
.
len
,
&
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
;
}
/* }}} */
/* 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
unsigned
char
*
data
,
int
data_len
,
php_phongo_bson_state
*
state
)
/* {{{ */
{
bson_reader_t
*
reader
=
NULL
;
bson_iter_t
iter
;
const
bson_t
*
b
;
bool
eof
=
false
;
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
;
}
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
;
}
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 php_phongo_bson_visit_binary() finds an ODM class, it should supersede
* a default type map and named root class. */
if
(
state
->
odm
&&
state
->
map
.
root_type
==
PHONGO_TYPEMAP_NONE
)
{
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
;
object_init_ex
(
&
obj
,
state
->
odm
?
state
->
odm
:
state
->
map
.
root
);
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
(
bson_reader_read
(
reader
,
&
eof
)
||
!
eof
)
{
phongo_throw_exception
(
PHONGO_ERROR_UNEXPECTED_VALUE
,
"Reading document did not exhaust input buffer"
);
goto
cleanup
;
}
retval
=
true
;
cleanup
:
if
(
reader
)
{
bson_reader_destroy
(
reader
);
}
if
(
must_dtor_state
)
{
php_phongo_bson_state_dtor
(
state
);
}
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
,
"Class %s is not instantiatable"
,
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_types
*
type
,
zend_class_entry
**
type_ce
)
/* {{{ */
{
char
*
classname
;
int
classname_len
;
zend_bool
classname_free
=
0
;
bool
retval
=
true
;
classname
=
php_array_fetch_string
(
options
,
name
,
&
classname_len
,
&
classname_free
);
if
(
!
classname_len
)
{
goto
cleanup
;
}
if
(
!
strcasecmp
(
classname
,
"array"
))
{
*
type
=
PHONGO_TYPEMAP_NATIVE_ARRAY
;
*
type_ce
=
NULL
;
}
else
if
(
!
strcasecmp
(
classname
,
"stdclass"
)
||
!
strcasecmp
(
classname
,
"object"
))
{
*
type
=
PHONGO_TYPEMAP_NATIVE_OBJECT
;
*
type_ce
=
NULL
;
}
else
{
if
((
*
type_ce
=
php_phongo_bson_state_fetch_class
(
classname
,
classname_len
,
php_phongo_unserializable_ce
)))
{
*
type
=
PHONGO_TYPEMAP_CLASS
;
}
else
{
retval
=
false
;
}
}
cleanup
:
if
(
classname_free
)
{
efree
(
classname
);
}
return
retval
;
}
/* }}} */
static
void
field_path_map_element_set_info
(
php_phongo_field_path_map_element
*
element
,
php_phongo_bson_typemap_types
type
,
zend_class_entry
*
ce
)
{
element
->
node_type
=
type
;
element
->
node_ce
=
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_types
type
,
zend_class_entry
*
ce
)
{
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
,
type
,
ce
);
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
)
{
zend_class_entry
*
map_ce
=
NULL
;
php_phongo_bson_typemap_types
map_type
;
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
),
&
map_type
,
&
map_ce
))
{
return
false
;
}
if
(
!
php_phongo_bson_state_add_field_path
(
map
,
ZSTR_VAL
(
string_key
),
map_type
,
map_ce
))
{
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
,
"array"
,
&
map
->
array_type
,
&
map
->
array
)
||
!
php_phongo_bson_state_parse_type
(
typemap
,
"document"
,
&
map
->
document_type
,
&
map
->
document
)
||
!
php_phongo_bson_state_parse_type
(
typemap
,
"root"
,
&
map
->
root_type
,
&
map
->
root
)
||
!
php_phongo_bson_state_parse_fieldpaths
(
typemap
,
map
))
{
/* Exception should already have been thrown */
return
false
;
}
return
true
;
}
/* }}} */
void
php_phongo_bson_new_timestamp_from_increment_and_timestamp
(
zval
*
object
,
uint32_t
increment
,
uint32_t
timestamp
)
/* {{{ */
{
php_phongo_timestamp_t
*
intern
;
object_init_ex
(
object
,
php_phongo_timestamp_ce
);
intern
=
Z_TIMESTAMP_OBJ_P
(
object
);
intern
->
increment
=
increment
;
intern
->
timestamp
=
timestamp
;
intern
->
initialized
=
true
;
}
/* }}} */
void
php_phongo_bson_new_int64
(
zval
*
object
,
int64_t
integer
)
/* {{{ */
{
php_phongo_int64_t
*
intern
;
object_init_ex
(
object
,
php_phongo_int64_ce
);
intern
=
Z_INT64_OBJ_P
(
object
);
intern
->
integer
=
integer
;
intern
->
initialized
=
true
;
}
/* }}} */
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, Nov 14, 8:26 PM (20 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
137139
Default Alt Text
phongo_bson.c (40 KB)
Attached To
Mode
R114 php-mongodb
Attached
Detach File
Event Timeline