@@ -118,10 +118,152 @@ zend_object *xsl_objects_new(zend_class_entry *class_type)
118
118
intern -> parameter = zend_new_array (0 );
119
119
php_dom_xpath_callbacks_ctor (& intern -> xpath_callbacks );
120
120
121
+ /* Default initialize properties that could not be default initialized at the stub because they depend on library
122
+ * configuration parameters. */
123
+ ZVAL_LONG (xsl_prop_max_template_depth (& intern -> std ), xsltMaxDepth );
124
+ ZVAL_LONG (xsl_prop_max_template_vars (& intern -> std ), xsltMaxVars );
125
+
121
126
return & intern -> std ;
122
127
}
123
128
/* }}} */
124
129
130
+ #if ZEND_DEBUG
131
+ # define XSL_DEFINE_PROP_ACCESSOR (c_name , php_name , prop_index ) \
132
+ zval *xsl_prop_##c_name(zend_object *object) \
133
+ { \
134
+ zend_string *prop_name = ZSTR_INIT_LITERAL(php_name, false); \
135
+ const zend_property_info *prop_info = zend_get_property_info(xsl_xsltprocessor_class_entry, prop_name, 0); \
136
+ zend_string_release_ex(prop_name, false); \
137
+ ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == prop_index); \
138
+ return OBJ_PROP_NUM(object, prop_index); \
139
+ }
140
+ #else
141
+ # define XSL_DEFINE_PROP_ACCESSOR (c_name , php_name , prop_index ) \
142
+ zval *xsl_prop_##c_name(zend_object *object) \
143
+ { \
144
+ return OBJ_PROP_NUM(object, prop_index); \
145
+ }
146
+ #endif
147
+
148
+ XSL_DEFINE_PROP_ACCESSOR (max_template_depth , "maxTemplateDepth" , 2 )
149
+ XSL_DEFINE_PROP_ACCESSOR (max_template_vars , "maxTemplateVars" , 3 )
150
+
151
+ static zval * xsl_objects_write_property_with_validation (zend_object * object , zend_string * member , zval * value , void * * cache_slot , zval * property )
152
+ {
153
+ /* Read old value so we can restore it if necessary. The value is not refcounted as its type is IS_LONG. */
154
+ ZEND_ASSERT (Z_TYPE_P (property ) == IS_LONG );
155
+ zend_long old_property_value = Z_LVAL_P (property );
156
+
157
+ /* Write new property, which will also potentially perform coercions. */
158
+ zend_std_write_property (object , member , value , NULL );
159
+
160
+ /* Validate value *after* coercions have been performed, and restore the old value if necessary. */
161
+ if (UNEXPECTED (Z_LVAL_P (property ) < 0 )) {
162
+ Z_LVAL_P (property ) = old_property_value ;
163
+ zend_value_error ("%s::$%s must be greater than or equal to 0" , ZSTR_VAL (object -> ce -> name ), ZSTR_VAL (member ));
164
+ return & EG (error_zval );
165
+ }
166
+
167
+ return property ;
168
+ }
169
+
170
+ static zval * xsl_objects_write_property (zend_object * object , zend_string * member , zval * value , void * * cache_slot )
171
+ {
172
+ /* Extra validation for maxTemplateDepth and maxTemplateVars */
173
+ if (zend_string_equals_literal (member , "maxTemplateDepth" )) {
174
+ zval * property = xsl_prop_max_template_depth (object );
175
+ return xsl_objects_write_property_with_validation (object , member , value , cache_slot , property );
176
+ } else if (zend_string_equals_literal (member , "maxTemplateVars" )) {
177
+ zval * property = xsl_prop_max_template_vars (object );
178
+ return xsl_objects_write_property_with_validation (object , member , value , cache_slot , property );
179
+ } else {
180
+ return zend_std_write_property (object , member , value , cache_slot );
181
+ }
182
+ }
183
+
184
+ static bool xsl_is_validated_property (const zend_string * member )
185
+ {
186
+ return zend_string_equals_literal (member , "maxTemplateDepth" ) || zend_string_equals_literal (member , "maxTemplateVars" );
187
+ }
188
+
189
+ static zval * xsl_objects_get_property_ptr_ptr (zend_object * object , zend_string * member , int type , void * * cache_slot )
190
+ {
191
+ if (xsl_is_validated_property (member )) {
192
+ return NULL ;
193
+ }
194
+
195
+ return zend_std_get_property_ptr_ptr (object , member , type , cache_slot );
196
+ }
197
+
198
+ static zval * xsl_objects_read_property (zend_object * object , zend_string * member , int type , void * * cache_slot , zval * rv )
199
+ {
200
+ /* read handler is being called as a fallback after get_property_ptr_ptr returned NULL */
201
+ if (type != BP_VAR_IS && type != BP_VAR_R && xsl_is_validated_property (member )) {
202
+ zend_throw_error (NULL , "Indirect modification of %s::$%s is not allowed" , ZSTR_VAL (object -> ce -> name ), ZSTR_VAL (member ));
203
+ return & EG (uninitialized_zval );
204
+ }
205
+
206
+ return zend_std_read_property (object , member , type , cache_slot , rv );
207
+ }
208
+
209
+ static void xsl_objects_unset_property (zend_object * object , zend_string * member , void * * cache_slot )
210
+ {
211
+ if (xsl_is_validated_property (member )) {
212
+ zend_throw_error (NULL , "Cannot unset %s::$%s" , ZSTR_VAL (object -> ce -> name ), ZSTR_VAL (member ));
213
+ return ;
214
+ }
215
+
216
+ zend_std_unset_property (object , member , cache_slot );
217
+ }
218
+
219
+ /* Tries to output an error message where a part was replaced by another string.
220
+ * Returns true if the search string was found and the error message with replacement was outputted.
221
+ * Return false otherwise. */
222
+ static bool xsl_try_output_replaced_error_message (
223
+ void * ctx ,
224
+ const char * msg ,
225
+ va_list args ,
226
+ const char * search ,
227
+ size_t search_len ,
228
+ const char * replace
229
+ )
230
+ {
231
+ const char * msg_replace_location = strstr (msg , search );
232
+ if (msg_replace_location != NULL ) {
233
+ php_libxml_ctx_error (ctx , "%.*s%s%s" , (int ) (msg_replace_location - msg ), msg , replace , msg_replace_location + search_len );
234
+ return true;
235
+ }
236
+ return false;
237
+ }
238
+
239
+ /* Helper macro so the string length doesn't need to be passed separately.
240
+ * Only allows literal strings for `search` and `replace`. */
241
+ #define XSL_TRY_OUTPUT_REPLACED_ERROR_MESSAGE (ctx , msg , args , search , replace ) \
242
+ xsl_try_output_replaced_error_message(ctx, msg, args, "" search, sizeof("" search) - 1, "" replace)
243
+
244
+ /* We want to output PHP-tailored error messages for some libxslt error messages, such that
245
+ * the errors refer to PHP properties instead of libxslt-specific fields. */
246
+ static void xsl_libxslt_error_handler (void * ctx , const char * msg , ...)
247
+ {
248
+ va_list args ;
249
+ va_start (args , msg );
250
+
251
+ if (strcmp (msg , "%s" ) == 0 ) {
252
+ /* Adjust error message to be more descriptive */
253
+ const char * msg_arg = va_arg (args , const char * );
254
+ bool output = XSL_TRY_OUTPUT_REPLACED_ERROR_MESSAGE (ctx , msg_arg , args , "xsltMaxDepth (--maxdepth)" , "$maxTemplateDepth" )
255
+ || XSL_TRY_OUTPUT_REPLACED_ERROR_MESSAGE (ctx , msg_arg , args , "maxTemplateVars (--maxvars)" , "$maxTemplateVars" );
256
+
257
+ if (!output ) {
258
+ php_libxml_ctx_error (ctx , "%s" , msg_arg );
259
+ }
260
+ } else {
261
+ php_libxml_error_handler_va (PHP_LIBXML_ERROR , ctx , msg , args );
262
+ }
263
+
264
+ va_end (args );
265
+ }
266
+
125
267
/* {{{ PHP_MINIT_FUNCTION */
126
268
PHP_MINIT_FUNCTION (xsl )
127
269
{
@@ -130,6 +272,10 @@ PHP_MINIT_FUNCTION(xsl)
130
272
xsl_object_handlers .clone_obj = NULL ;
131
273
xsl_object_handlers .free_obj = xsl_objects_free_storage ;
132
274
xsl_object_handlers .get_gc = xsl_objects_get_gc ;
275
+ xsl_object_handlers .write_property = xsl_objects_write_property ;
276
+ xsl_object_handlers .get_property_ptr_ptr = xsl_objects_get_property_ptr_ptr ;
277
+ xsl_object_handlers .read_property = xsl_objects_read_property ;
278
+ xsl_object_handlers .unset_property = xsl_objects_unset_property ;
133
279
134
280
xsl_xsltprocessor_class_entry = register_class_XSLTProcessor ();
135
281
xsl_xsltprocessor_class_entry -> create_object = xsl_objects_new ;
@@ -145,7 +291,7 @@ PHP_MINIT_FUNCTION(xsl)
145
291
xsltRegisterExtModuleFunction ((const xmlChar * ) "function" ,
146
292
(const xmlChar * ) "http://php.net/xsl" ,
147
293
xsl_ext_function_object_php );
148
- xsltSetGenericErrorFunc (NULL , php_libxml_error_handler );
294
+ xsltSetGenericErrorFunc (NULL , xsl_libxslt_error_handler );
149
295
150
296
register_php_xsl_symbols (module_number );
151
297
0 commit comments