1 /**
2  *     Copyright 2017 Couchbase, Inc.
3  *
4  *   Licensed under the Apache License, Version 2.0 (the "License");
5  *   you may not use this file except in compliance with the License.
6  *   You may obtain a copy of the License at
7  *
8  *       http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *   Unless required by applicable law or agreed to in writing, software
11  *   distributed under the License is distributed on an "AS IS" BASIS,
12  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *   See the License for the specific language governing permissions and
14  *   limitations under the License.
15  */
16 
17 #include "couchbase.h"
18 #include <Zend/zend_alloc.h>
19 
20 #define LOGARGS(lvl) LCB_LOG_##lvl, NULL, "pcbc/analytics_query", __FILE__, __LINE__
21 
22 zend_class_entry *pcbc_analytics_query_ce;
23 
24 /* {{{ proto void AnalyticsQuery::__construct() Should not be called directly */
PHP_METHOD(AnalyticsQuery,__construct)25 PHP_METHOD(AnalyticsQuery, __construct)
26 {
27     throw_pcbc_exception("Accessing private constructor.", LCB_EINVAL);
28 }
29 /* }}} */
30 
31 /* {{{ proto \Couchbase\AnalyticsQuery AnalyticsQuery::fromString(string $statement) */
PHP_METHOD(AnalyticsQuery,fromString)32 PHP_METHOD(AnalyticsQuery, fromString)
33 {
34     char *statement = NULL;
35     pcbc_str_arg_size statement_len = 0;
36     int rv;
37 
38     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &statement, &statement_len);
39     if (rv == FAILURE) {
40         RETURN_NULL();
41     }
42 
43     pcbc_analytics_query_init(return_value, statement, statement_len TSRMLS_CC);
44 } /* }}} */
45 
PHP_METHOD(AnalyticsQuery,rawParam)46 PHP_METHOD(AnalyticsQuery, rawParam)
47 {
48     zval *value;
49     zval *options;
50     char *name = NULL;
51     pcbc_str_arg_size name_len = 0;
52     int rv;
53 
54     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name, &name_len, &value);
55     if (rv == FAILURE) {
56         RETURN_NULL();
57     }
58 
59     PCBC_READ_PROPERTY(options, pcbc_analytics_query_ce, getThis(), "options", 0);
60     PCBC_ADDREF_P(value);
61     add_assoc_zval_ex(options, name, name_len, value);
62 
63     RETURN_ZVAL(getThis(), 1, 0);
64 }
65 
PHP_METHOD(AnalyticsQuery,positionalParams)66 PHP_METHOD(AnalyticsQuery, positionalParams)
67 {
68     zval *params;
69     zval *options;
70     int rv;
71 
72     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &params);
73     if (rv == FAILURE) {
74         RETURN_NULL();
75     }
76 
77     PCBC_READ_PROPERTY(options, pcbc_analytics_query_ce, getThis(), "options", 0);
78     PCBC_ADDREF_P(params);
79     add_assoc_zval(options, "args", params);
80 
81     RETURN_ZVAL(getThis(), 1, 0);
82 }
83 
PHP_METHOD(AnalyticsQuery,namedParams)84 PHP_METHOD(AnalyticsQuery, namedParams)
85 {
86     zval *params;
87     zval *options;
88     int rv;
89 
90     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &params);
91     if (rv == FAILURE) {
92         RETURN_NULL();
93     }
94 
95     PCBC_READ_PROPERTY(options, pcbc_analytics_query_ce, getThis(), "options", 0);
96     {
97 #if PHP_VERSION_ID >= 70000
98         HashTable *ht;
99         zend_ulong num_key;
100         zend_string *string_key = NULL;
101         zval *entry;
102 
103         ht = HASH_OF(params);
104         ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, string_key, entry)
105         {
106             if (string_key) {
107                 char *prefixed_key = NULL;
108                 spprintf(&prefixed_key, 0, "$%s", ZSTR_VAL(string_key));
109                 add_assoc_zval(options, prefixed_key, entry);
110                 PCBC_ADDREF_P(entry);
111                 efree(prefixed_key);
112             }
113         }
114         ZEND_HASH_FOREACH_END();
115 #else
116         HashPosition pos;
117         zval **entry;
118 
119         zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(params), &pos);
120         while (zend_hash_get_current_data_ex(Z_ARRVAL_P(params), (void **)&entry, &pos) == SUCCESS) {
121             if (zend_hash_get_current_key_type_ex(Z_ARRVAL_P(params), &pos) == HASH_KEY_IS_STRING) {
122                 char *key = NULL, *prefixed_key = NULL;
123                 unsigned key_len = 0;
124                 zend_hash_get_current_key_ex(Z_ARRVAL_P(params), &key, &key_len, NULL, 0, &pos);
125                 spprintf(&prefixed_key, 0, "$%s", key);
126                 add_assoc_zval(options, prefixed_key, *entry);
127                 PCBC_ADDREF_P(*entry);
128                 efree(prefixed_key);
129             }
130             zend_hash_move_forward_ex(Z_ARRVAL_P(params), &pos);
131         }
132 #endif
133     }
134     RETURN_ZVAL(getThis(), 1, 0);
135 }
136 
137 ZEND_BEGIN_ARG_INFO_EX(ai_AnalyticsQuery_none, 0, 0, 0)
138 ZEND_END_ARG_INFO()
139 
140 ZEND_BEGIN_ARG_INFO_EX(ai_AnalyticsQuery_fromString, 0, 0, 1)
141 ZEND_ARG_INFO(0, statement)
142 ZEND_END_ARG_INFO()
143 
144 ZEND_BEGIN_ARG_INFO_EX(ai_AnalyticsQuery_params, 0, 0, 1)
145 ZEND_ARG_INFO(0, params)
146 ZEND_END_ARG_INFO()
147 
148 ZEND_BEGIN_ARG_INFO_EX(ai_AnalyticsQuery_rawParam, 0, 0, 2)
149 ZEND_ARG_INFO(0, name)
150 ZEND_ARG_INFO(0, value)
151 ZEND_END_ARG_INFO()
152 
153 // clang-format off
154 zend_function_entry analytics_query_methods[] = {
155     PHP_ME(AnalyticsQuery, __construct, ai_AnalyticsQuery_none, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL | ZEND_ACC_CTOR)
156     PHP_ME(AnalyticsQuery, fromString, ai_AnalyticsQuery_fromString, ZEND_ACC_STATIC | ZEND_ACC_PUBLIC)
157     PHP_ME(AnalyticsQuery, positionalParams, ai_AnalyticsQuery_params, ZEND_ACC_PUBLIC)
158     PHP_ME(AnalyticsQuery, namedParams, ai_AnalyticsQuery_params, ZEND_ACC_PUBLIC)
159     PHP_ME(AnalyticsQuery, rawParam, ai_AnalyticsQuery_rawParam, ZEND_ACC_PUBLIC)
160     PHP_FE_END
161 };
162 // clang-format on
163 
164 zend_object_handlers pcbc_analytics_query_handlers;
165 
pcbc_analytics_query_init(zval * return_value,const char * statement,int statement_len TSRMLS_DC)166 void pcbc_analytics_query_init(zval *return_value, const char *statement, int statement_len TSRMLS_DC)
167 {
168     pcbc_analytics_query_t *query;
169     PCBC_ZVAL options;
170 
171     object_init_ex(return_value, pcbc_analytics_query_ce);
172     query = Z_ANALYTICS_QUERY_OBJ_P(return_value);
173 
174     PCBC_ZVAL_ALLOC(options);
175     array_init(PCBC_P(options));
176     ADD_ASSOC_STRINGL(PCBC_P(options), "statement", statement, statement_len);
177     zend_update_property(pcbc_analytics_query_ce, return_value, ZEND_STRL("options"), PCBC_P(options) TSRMLS_CC);
178     zval_ptr_dtor(&options);
179 }
180 
analytics_query_free_object(pcbc_free_object_arg * object TSRMLS_DC)181 static void analytics_query_free_object(pcbc_free_object_arg *object TSRMLS_DC) /* {{{ */
182 {
183     pcbc_analytics_query_t *obj = Z_ANALYTICS_QUERY_OBJ(object);
184 
185     zend_object_std_dtor(&obj->std TSRMLS_CC);
186 #if PHP_VERSION_ID < 70000
187     efree(obj);
188 #endif
189 } /* }}} */
190 
analytics_query_create_object(zend_class_entry * class_type TSRMLS_DC)191 static pcbc_create_object_retval analytics_query_create_object(zend_class_entry *class_type TSRMLS_DC)
192 {
193     pcbc_analytics_query_t *obj = NULL;
194 
195     obj = PCBC_ALLOC_OBJECT_T(pcbc_analytics_query_t, class_type);
196 
197     zend_object_std_init(&obj->std, class_type TSRMLS_CC);
198     object_properties_init(&obj->std, class_type);
199 
200 #if PHP_VERSION_ID >= 70000
201     obj->std.handlers = &pcbc_analytics_query_handlers;
202     return &obj->std;
203 #else
204     {
205         zend_object_value ret;
206         ret.handle = zend_objects_store_put(obj, (zend_objects_store_dtor_t)zend_objects_destroy_object,
207                                             analytics_query_free_object, NULL TSRMLS_CC);
208         ret.handlers = &pcbc_analytics_query_handlers;
209         return ret;
210     }
211 #endif
212 }
213 
analytics_query_get_debug_info(zval * object,int * is_temp TSRMLS_DC)214 static HashTable *analytics_query_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
215 {
216     pcbc_analytics_query_t *obj = NULL;
217 #if PHP_VERSION_ID >= 70000
218     zval retval;
219 #else
220     zval retval = zval_used_for_init;
221 #endif
222     zval *options;
223 
224     *is_temp = 1;
225     obj = Z_ANALYTICS_QUERY_OBJ_P(object);
226 
227     array_init(&retval);
228     PCBC_READ_PROPERTY(options, pcbc_analytics_query_ce, object, "options", 0);
229     PCBC_ADDREF_P(options);
230     add_assoc_zval(&retval, "options", options);
231 
232     return Z_ARRVAL(retval);
233 } /* }}} */
234 
PHP_MINIT_FUNCTION(AnalyticsQuery)235 PHP_MINIT_FUNCTION(AnalyticsQuery)
236 {
237     zend_class_entry ce;
238 
239     INIT_NS_CLASS_ENTRY(ce, "Couchbase", "AnalyticsQuery", analytics_query_methods);
240     pcbc_analytics_query_ce = zend_register_internal_class(&ce TSRMLS_CC);
241     pcbc_analytics_query_ce->create_object = analytics_query_create_object;
242     PCBC_CE_DISABLE_SERIALIZATION(pcbc_analytics_query_ce);
243 
244     memcpy(&pcbc_analytics_query_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
245     pcbc_analytics_query_handlers.get_debug_info = analytics_query_get_debug_info;
246 #if PHP_VERSION_ID >= 70000
247     pcbc_analytics_query_handlers.free_obj = analytics_query_free_object;
248     pcbc_analytics_query_handlers.offset = XtOffsetOf(pcbc_analytics_query_t, std);
249 #endif
250     zend_declare_property_null(pcbc_analytics_query_ce, ZEND_STRL("options"), ZEND_ACC_PUBLIC TSRMLS_CC);
251     return SUCCESS;
252 }
253