1 /**
2  * C layer of the php-vips binding.
3  */
4 
5 /* Uncomment for some logging.
6 #define VIPS_DEBUG
7  */
8 
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12 
13 #include "php.h"
14 #include "php_ini.h"
15 #include "ext/standard/info.h"
16 #include "SAPI.h"
17 #include "php_vips.h"
18 
19 #include <vips/vips.h>
20 #include <vips/debug.h>
21 #include <vips/vector.h>
22 
23 /* If you declare any globals in php_vips.h uncomment this:
24 ZEND_DECLARE_MODULE_GLOBALS(vips)
25 */
26 
27 /* True global resources - no need for thread safety here */
28 static int le_gobject;
29 
30 /* {{{ PHP_INI
31  */
32 /* Remove comments and fill if you need to have entries in php.ini
33 PHP_INI_BEGIN()
34     STD_PHP_INI_ENTRY("vips.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_vips_globals, vips_globals)
35     STD_PHP_INI_ENTRY("vips.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_vips_globals, vips_globals)
36 PHP_INI_END()
37 */
38 /* }}} */
39 
40 /* {{{ proto static int vips_php_call_array(const char *operation_name, zval *instance, const char *option_string, int argc, zval *argv, zval *return_value)
41    Call any vips operation. */
42 
43 /* Track stuff during a call to a vips operation in one of these.
44  */
45 typedef struct _VipsPhpCall {
46 	/* Parameters.
47 	 */
48 	const char *operation_name;
49 	zval *instance;
50 	const char *option_string;
51 	int argc;
52 	zval *argv;
53 
54 	/* The operation we are calling.
55 	 */
56 	VipsOperation *operation;
57 
58 	/* The num of args this operation needs from php. This does not include the
59 	 * @instance zval.
60 	 */
61 	int args_required;
62 
63 	/* If we've already used the instance zval.
64 	 */
65 	gboolean used_instance;
66 
67 	/* Extra php array of optional args.
68 	 */
69 	zval *options;
70 
71 	/* The first image arg ... the thing we expand constants to match.
72 	 */
73 	VipsImage *match_image;
74 
75 } VipsPhpCall;
76 
77 static void
vips_php_call_free(VipsPhpCall * call)78 vips_php_call_free(VipsPhpCall *call)
79 {
80 	VIPS_DEBUG_MSG("vips_php_call_free:\n");
81 
82 	VIPS_UNREF(call->operation);
83 	g_free(call);
84 }
85 
86 static VipsPhpCall *
vips_php_call_new(const char * operation_name,zval * instance,const char * option_string,int argc,zval * argv)87 vips_php_call_new(const char *operation_name, zval *instance,
88 	const char *option_string, int argc, zval *argv)
89 {
90 	VipsPhpCall *call;
91 
92 	VIPS_DEBUG_MSG("vips_php_call_new: %s\n", operation_name );
93 	VIPS_DEBUG_MSG("    option_string = \"%s\", argc = %d\n",
94 			option_string, argc);
95 
96 	call = g_new0( VipsPhpCall, 1 );
97 	call->operation_name = operation_name;
98 	call->instance = instance;
99 	call->option_string = option_string;
100 	call->argc = argc;
101 	call->argv = argv;
102 
103 	if (!(call->operation = vips_operation_new(operation_name))) {
104 		vips_php_call_free(call);
105 		return NULL;
106 	}
107 
108 	return call;
109 }
110 
111 /* Get a non-reference zval. In php7, zvalues can be references to other zvals
112  * ... chase down a chain of refs to get a real zval.
113  * the ref pointer chain.
114  */
115 static inline zval *
zval_get_nonref(zval * zvalue)116 zval_get_nonref(zval *zvalue)
117 {
118 	while (Z_TYPE_P(zvalue) == IS_REFERENCE)
119 		zvalue = Z_REFVAL_P(zvalue);
120 
121 	return zvalue;
122 }
123 
124 /* First pass over our arguments: find the first image arg and note as
125  * match_image.
126  */
127 static void
vips_php_analyze_arg(VipsPhpCall * call,zval * zvalue)128 vips_php_analyze_arg(VipsPhpCall *call, zval *zvalue)
129 {
130 	zvalue = zval_get_nonref(zvalue);
131 
132 	if (Z_TYPE_P(zvalue) == IS_ARRAY) {
133 		const int n = zend_hash_num_elements(Z_ARRVAL_P(zvalue));
134 
135 		int i;
136 
137 		for (i = 0; i < n; i++) {
138 			zval *item = zend_hash_index_find(Z_ARRVAL_P(zvalue), i);
139 
140 			if (item) {
141 				vips_php_analyze_arg(call, item);
142 			}
143 		}
144 	}
145 	else if (Z_TYPE_P(zvalue) == IS_RESOURCE) {
146 		VipsImage *image;
147 
148 		if( (image = (VipsImage *)zend_fetch_resource(Z_RES_P(zvalue),
149 			"GObject", le_gobject)) != NULL) {
150 			if (!call->match_image) {
151 				call->match_image = image;
152 			}
153 		}
154 	}
155 }
156 
157 static int
vips_php_blob_free(void * buf,void * area)158 vips_php_blob_free(void *buf, void *area)
159 {
160 	g_free(buf);
161 
162 	return 0;
163 }
164 
165 /* Expand a constant (eg. 12, "12" or [1, 2, 3]) into an image using
166  * @match_image as a guide.
167  */
168 static VipsImage *
expand_constant(VipsImage * match_image,zval * constant)169 expand_constant(VipsImage *match_image, zval *constant)
170 {
171 	VipsImage *result;
172 	VipsImage *x;
173 
174 	if (vips_black(&result, 1, 1, NULL)) {
175 		return NULL;
176 	}
177 
178 	constant = zval_get_nonref(constant);
179 
180 	if (Z_TYPE_P(constant) == IS_ARRAY) {
181 		const int n = zend_hash_num_elements(Z_ARRVAL_P(constant));
182 
183 		double *ones;
184 		double *offsets;
185 		int i;
186 
187 		ones = VIPS_ARRAY(result, n, double);
188 		offsets = VIPS_ARRAY(result, n, double);
189 
190 		for (i = 0; i < n; i++) {
191 			zval *ele;
192 
193 			ones[i] = 1.0;
194 
195 			if ((ele = zend_hash_index_find(Z_ARRVAL_P(constant), i)) != NULL) {
196 				offsets[i] = zval_get_double(ele);
197 			}
198 		}
199 
200 		if (vips_linear(result, &x, ones, offsets, n, NULL)) {
201 			return NULL;
202 		}
203 		g_object_unref(result);
204 		result = x;
205 	}
206 	else {
207 		if (vips_linear1(result, &x, 1.0, zval_get_double(constant), NULL)) {
208 			return NULL;
209 		}
210 		g_object_unref(result);
211 		result = x;
212 	}
213 
214 	if (vips_cast(result, &x, match_image->BandFmt, NULL)) {
215 		return NULL;
216 	}
217 	g_object_unref(result);
218 	result = x;
219 
220 	if (vips_embed(result, &x, 0, 0, match_image->Xsize, match_image->Ysize,
221 		"extend", VIPS_EXTEND_COPY,
222 		NULL)) {
223 		return NULL;
224 	}
225 	g_object_unref(result);
226 	result = x;
227 
228 	result->Type = match_image->Type;
229 	result->Xres = match_image->Xres;
230 	result->Yres = match_image->Yres;
231 	result->Xoffset = match_image->Xoffset;
232 	result->Yoffset = match_image->Yoffset;
233 
234 	return result;
235 }
236 
237 /* Is a zval a rectangular 2D array.
238  */
239 static gboolean
is_2D(zval * array)240 is_2D(zval *array)
241 {
242 	int height;
243 	zval *row;
244 	int width;
245 	int y;
246 
247 	array = zval_get_nonref(array);
248 
249 	if (Z_TYPE_P(array) != IS_ARRAY) {
250 		return FALSE;
251 	}
252 
253 	height = zend_hash_num_elements(Z_ARRVAL_P(array));
254 	if ((row = zend_hash_index_find(Z_ARRVAL_P(array), 0)) == NULL ||
255 		!(row = zval_get_nonref(row)) ||
256 		Z_TYPE_P(row) != IS_ARRAY) {
257 		return FALSE;
258 	}
259 	width = zend_hash_num_elements(Z_ARRVAL_P(row));
260 
261 	for (y = 1; y < height; y++) {
262 		if ((row = zend_hash_index_find(Z_ARRVAL_P(array), y)) == NULL ||
263 			!(row = zval_get_nonref(row)) ||
264 			Z_TYPE_P(row) != IS_ARRAY ||
265 			zend_hash_num_elements(Z_ARRVAL_P(row)) != width) {
266 			return FALSE;
267 		}
268 	}
269 
270 	return TRUE;
271 }
272 
273 /* Make a vips matrix image from a 2D zval. @array must have passed is_2D()
274  * before calling this.
275  */
276 static VipsImage *
matrix_from_zval(zval * array)277 matrix_from_zval(zval *array)
278 {
279 	int width;
280 	int height;
281 	zval *row;
282 	VipsImage *mat;
283 	int x, y;
284 
285 	array = zval_get_nonref(array);
286 
287 	height = zend_hash_num_elements(Z_ARRVAL_P(array));
288 	row = zend_hash_index_find(Z_ARRVAL_P(array), 0);
289 	row = zval_get_nonref(row);
290 	g_assert(Z_TYPE_P(row) == IS_ARRAY);
291 	width = zend_hash_num_elements(Z_ARRVAL_P(row));
292 	mat = vips_image_new_matrix(width, height);
293 
294 	for (y = 0; y < height; y++) {
295 		row = zend_hash_index_find(Z_ARRVAL_P(array), y);
296 		row = zval_get_nonref(row);
297 		g_assert(Z_TYPE_P(row) == IS_ARRAY);
298 		g_assert(zend_hash_num_elements(Z_ARRVAL_P(row)) == width);
299 
300 		for (x = 0; x < width; x++) {
301 			zval *ele;
302 
303 			ele = zend_hash_index_find(Z_ARRVAL_P(row), x);
304 			*VIPS_MATRIX(mat, x, y) = zval_get_double(ele);
305 		}
306 	}
307 
308 	return mat;
309 }
310 
311 /* Turn a zval into an image. An image stays an image, a 2D array of numbers
312  * becomes a matrix image, a 1D array or a simple constant is expanded to
313  * match @match_image.
314  */
315 static VipsImage *
imageize(VipsImage * match_image,zval * zvalue)316 imageize(VipsImage *match_image, zval *zvalue)
317 {
318 	VipsImage *image;
319 
320 	zvalue = zval_get_nonref(zvalue);
321 
322 	if (Z_TYPE_P(zvalue) == IS_RESOURCE &&
323 		(image = (VipsImage *)
324 			zend_fetch_resource(Z_RES_P(zvalue), "GObject", le_gobject))) {
325 		return image;
326 	}
327 	else if (is_2D(zvalue)) {
328 		return matrix_from_zval(zvalue);
329 	}
330 	else if (match_image) {
331 		return expand_constant(match_image, zvalue);
332 	}
333 	else {
334 		php_error_docref(NULL, E_WARNING, "not a VipsImage");
335 		return NULL;
336 	}
337 }
338 
339 static int
zval_to_array_image(VipsImage * match_image,zval * zvalue,GValue * gvalue)340 zval_to_array_image(VipsImage *match_image, zval *zvalue, GValue *gvalue)
341 {
342 	VipsImage **arr;
343 	VipsImage *image;
344 	int n;
345 	int i;
346 
347 	zvalue = zval_get_nonref(zvalue);
348 
349 	if (Z_TYPE_P(zvalue) == IS_ARRAY) {
350 		n = zend_hash_num_elements(Z_ARRVAL_P(zvalue));
351 	}
352 	else {
353 		n = 1;
354 	}
355 
356 	vips_value_set_array_image(gvalue, n);
357 	arr = vips_value_get_array_image(gvalue, NULL);
358 
359 	if (Z_TYPE_P(zvalue) == IS_ARRAY) {
360 		for (i = 0; i < n; i++) {
361 			zval *ele;
362 
363 			ele = zend_hash_index_find(Z_ARRVAL_P(zvalue), i);
364 			if (!ele) {
365 				php_error_docref(NULL, E_WARNING, "element missing from array");
366 				return -1;
367 			}
368 
369 			if (!(image = imageize(match_image, ele))) {
370 				return -1;
371 			}
372 
373 			arr[i] = image;
374 			g_object_ref(image);
375 		}
376 	}
377 	else {
378 		if (!(image = imageize(match_image, zvalue))) {
379 			return -1;
380 		}
381 
382 		arr[0] = image;
383 		g_object_ref(image);
384 	}
385 
386 	return 0;
387 }
388 
389 /* Set a gvalue from a php value.
390  *
391  * You must set the type of the gvalue before calling this to hint what kind
392  * of gvalue to make. For example if type is an enum, a zval string will be
393  * used to look up the enum nick.
394  *
395  * If non-NULL, @match_image is used to turn constants into images.
396  */
397 static int
vips_php_zval_to_gval(VipsImage * match_image,zval * zvalue,GValue * gvalue)398 vips_php_zval_to_gval(VipsImage *match_image, zval *zvalue, GValue *gvalue)
399 {
400 	GType type = G_VALUE_TYPE(gvalue);
401 
402 	/* The fundamental type ... eg. G_TYPE_ENUM for a VIPS_TYPE_KERNEL, or
403 	 * G_TYPE_OBJECT for VIPS_TYPE_IMAGE().
404 	 */
405 	GType fundamental = G_TYPE_FUNDAMENTAL(type);
406 
407 	VipsImage *image;
408 	zend_string *zstr;
409 	int enum_value;
410 
411 	switch (fundamental) {
412 		case G_TYPE_STRING:
413 			/* These are GStrings, vips refstrings are handled by boxed, see
414 			 * below.
415 			 */
416 			zstr = zval_get_string(zvalue);
417 			g_value_set_string(gvalue, ZSTR_VAL(zstr));
418 			zend_string_release(zstr);
419 			break;
420 
421 		case G_TYPE_OBJECT:
422 			if (!(image = imageize(match_image, zvalue))) {
423 				return -1;
424 			}
425 			g_value_set_object(gvalue, image);
426 			break;
427 
428 		case G_TYPE_INT:
429 			g_value_set_int(gvalue, zval_get_long(zvalue));
430 			break;
431 
432 		case G_TYPE_UINT64:
433 			g_value_set_uint64(gvalue, zval_get_long(zvalue));
434 			break;
435 
436 		case G_TYPE_BOOLEAN:
437 			g_value_set_boolean(gvalue, zval_get_long(zvalue));
438 			break;
439 
440 		case G_TYPE_ENUM:
441 			zvalue = zval_get_nonref(zvalue);
442 
443 			if (Z_TYPE_P(zvalue) == IS_LONG) {
444 				enum_value = Z_LVAL_P(zvalue);
445 			}
446 			else if (Z_TYPE_P(zvalue) == IS_DOUBLE) {
447 				enum_value = Z_DVAL_P(zvalue);
448 			}
449 			else {
450 				zstr = zval_get_string(zvalue);
451 				enum_value = vips_enum_from_nick("enum", type, ZSTR_VAL(zstr));
452 				if (enum_value < 0) {
453 					zend_string_release(zstr);
454 					return -1;
455 				}
456 				zend_string_release(zstr);
457 			}
458 			g_value_set_enum(gvalue, enum_value);
459 			break;
460 
461 		case G_TYPE_FLAGS:
462 			g_value_set_flags(gvalue, zval_get_long(zvalue));
463 			break;
464 
465 		case G_TYPE_DOUBLE:
466 			g_value_set_double(gvalue, zval_get_double(zvalue));
467 			break;
468 
469 		case G_TYPE_BOXED:
470 			if (type == VIPS_TYPE_REF_STRING) {
471 				zstr = zval_get_string(zvalue);
472 				vips_value_set_ref_string(gvalue, ZSTR_VAL(zstr));
473 				zend_string_release(zstr);
474 			}
475 			else if (type == VIPS_TYPE_BLOB) {
476 				void *buf;
477 
478 				zvalue = zval_get_nonref(zvalue);
479 
480 				zstr = zval_get_string(zvalue);
481 				buf = g_malloc(ZSTR_LEN(zstr));
482 				memcpy(buf, ZSTR_VAL(zstr), ZSTR_LEN(zstr));
483 				zend_string_release(zstr);
484 
485 				vips_value_set_blob(gvalue,
486 					vips_php_blob_free, buf, Z_STRLEN_P(zvalue));
487 			}
488 			else if (type == VIPS_TYPE_ARRAY_INT) {
489 				int *arr;
490 				int n;
491 				int i;
492 
493 				zvalue = zval_get_nonref(zvalue);
494 
495 				if (Z_TYPE_P(zvalue) == IS_ARRAY) {
496 					n = zend_hash_num_elements(Z_ARRVAL_P(zvalue));
497 				}
498 				else {
499 					n = 1;
500 				}
501 
502 				vips_value_set_array_int(gvalue, NULL, n);
503 				arr = vips_value_get_array_int(gvalue, NULL);
504 
505 				if (Z_TYPE_P(zvalue) == IS_ARRAY) {
506 					for (i = 0; i < n; i++) {
507 						zval *ele;
508 
509 						ele = zend_hash_index_find(Z_ARRVAL_P(zvalue), i);
510 						if (ele) {
511 							arr[i] = zval_get_long(ele);
512 						}
513 					}
514 				}
515 				else {
516 					arr[0] = zval_get_long(zvalue);
517 				}
518 			}
519 			else if (type == VIPS_TYPE_ARRAY_DOUBLE) {
520 				double *arr;
521 				int n;
522 				int i;
523 
524 				zvalue = zval_get_nonref(zvalue);
525 
526 				if (Z_TYPE_P(zvalue) == IS_ARRAY) {
527 					n = zend_hash_num_elements(Z_ARRVAL_P(zvalue));
528 				}
529 				else {
530 					n = 1;
531 				}
532 
533 				vips_value_set_array_double(gvalue, NULL, n);
534 				arr = vips_value_get_array_double(gvalue, NULL);
535 
536 				if (Z_TYPE_P(zvalue) == IS_ARRAY) {
537 					for (i = 0; i < n; i++) {
538 						zval *ele;
539 
540 						ele = zend_hash_index_find(Z_ARRVAL_P(zvalue), i);
541 						if (ele) {
542 							arr[i] = zval_get_double(ele);
543 						}
544 					}
545 				}
546 				else {
547 					arr[0] = zval_get_double(zvalue);
548 				}
549 			}
550 			else if (type == VIPS_TYPE_ARRAY_IMAGE) {
551 				if (zval_to_array_image(match_image, zvalue, gvalue)) {
552 					return -1;
553 				}
554 			}
555 			else {
556 				g_warning( "%s: unimplemented boxed type %s",
557 					G_STRLOC, g_type_name(type) );
558 			}
559 			break;
560 
561 		default:
562 			g_warning( "%s: unimplemented GType %s",
563 				G_STRLOC, g_type_name(fundamental) );
564 			break;
565 	}
566 
567 	return 0;
568 }
569 
570 static int
vips_php_set_value(VipsPhpCall * call,GParamSpec * pspec,VipsArgumentFlags flags,zval * zvalue)571 vips_php_set_value(VipsPhpCall *call,
572 	GParamSpec *pspec, VipsArgumentFlags flags, zval *zvalue)
573 {
574 	const char *name = g_param_spec_get_name(pspec);
575 	GType pspec_type = G_PARAM_SPEC_VALUE_TYPE(pspec);
576 	GValue gvalue = { 0 };
577 
578 	g_value_init(&gvalue, pspec_type);
579 	if (vips_php_zval_to_gval(call->match_image, zvalue, &gvalue)) {
580 		g_value_unset(&gvalue);
581 		return -1;
582 	}
583 
584 	/* If we are setting a MODIFY VipsArgument with an image, we need to take a
585 	 * copy.
586 	 */
587 	if (g_type_is_a(pspec_type, VIPS_TYPE_IMAGE) &&
588 		(flags & VIPS_ARGUMENT_MODIFY)) {
589 		VipsImage *image;
590 		VipsImage *memory;
591 
592 		VIPS_DEBUG_MSG("vips_php_set_value: copying image\n");
593 
594 		image = (VipsImage *) g_value_get_object(&gvalue);
595 		memory = vips_image_new_memory();
596 		if (vips_image_write(image, memory)) {
597 			g_object_unref(memory);
598 			g_value_unset(&gvalue);
599 			return -1;
600 		}
601 		g_value_unset(&gvalue);
602 		g_value_init(&gvalue, pspec_type);
603 		g_value_set_object(&gvalue, memory);
604 	}
605 
606 #ifdef VIPS_DEBUG
607 {
608 	char *str_value;
609 
610 	str_value = g_strdup_value_contents(&gvalue);
611 	VIPS_DEBUG_MSG("    %s.%s = %s\n", call->operation_name, name, str_value);
612 	g_free(str_value);
613 }
614 #endif/*VIPS_DEBUG*/
615 
616 	g_object_set_property(G_OBJECT(call->operation), name, &gvalue);
617 	g_value_unset(&gvalue);
618 
619 	return 0;
620 }
621 
622 static void *
vips_php_set_required_input(VipsObject * object,GParamSpec * pspec,VipsArgumentClass * argument_class,VipsArgumentInstance * argument_instance,void * a,void * b)623 vips_php_set_required_input(VipsObject *object,
624 	GParamSpec *pspec, VipsArgumentClass *argument_class,
625 	VipsArgumentInstance *argument_instance,
626 	void *a, void *b)
627 {
628 	VipsPhpCall *call = (VipsPhpCall *) a;
629 
630 	if ((argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
631 		(argument_class->flags & VIPS_ARGUMENT_CONSTRUCT) &&
632 		(argument_class->flags & VIPS_ARGUMENT_INPUT) &&
633 		!(argument_class->flags & VIPS_ARGUMENT_DEPRECATED) &&
634 		!argument_instance->assigned) {
635 		zval *arg;
636 
637 		/* If this arg needs an image, we use instance, if we can.
638 		 */
639 		arg = NULL;
640 		if (G_PARAM_SPEC_VALUE_TYPE(pspec) == VIPS_TYPE_IMAGE &&
641 			call->instance &&
642 			!call->used_instance) {
643 			arg = call->instance;
644 			call->used_instance = TRUE;
645 		}
646 		else if (call->args_required < call->argc) {
647 			/* Pick the next zval off the supplied arg list.
648 			 */
649 			arg = &call->argv[call->args_required];
650 			call->args_required += 1;
651 		}
652 
653 		if (arg &&
654 			vips_php_set_value(call, pspec, argument_class->flags, arg)) {
655 			return call;
656 		}
657 	}
658 
659 	return NULL;
660 }
661 
662 /* Set all optional arguments.
663  */
664 static int
vips_php_set_optional_input(VipsPhpCall * call,zval * options)665 vips_php_set_optional_input(VipsPhpCall *call, zval *options)
666 {
667 	zend_string *key;
668 	zval *value;
669 
670 	VIPS_DEBUG_MSG("vips_php_set_optional_input:\n");
671 
672 	options = zval_get_nonref(options);
673 
674 	ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), key, value) {
675 		char *name;
676 		GParamSpec *pspec;
677 		VipsArgumentClass *argument_class;
678 		VipsArgumentInstance *argument_instance;
679 
680 		if (key == NULL) {
681 			continue;
682 		}
683 
684 		name = ZSTR_VAL(key);
685 		if (vips_object_get_argument(VIPS_OBJECT(call->operation), name,
686 			&pspec, &argument_class, &argument_instance)) {
687 			return -1;
688 		}
689 
690 		if (!(argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
691 			(argument_class->flags & VIPS_ARGUMENT_INPUT) &&
692 			!(argument_class->flags & VIPS_ARGUMENT_DEPRECATED) &&
693 			vips_php_set_value(call, pspec, argument_class->flags, value)) {
694 			return -1;
695 		}
696 	} ZEND_HASH_FOREACH_END();
697 
698 	return 0;
699 }
700 
701 /* Set a php zval from a gvalue.
702  */
703 static int
vips_php_gval_to_zval(GValue * gvalue,zval * zvalue)704 vips_php_gval_to_zval(GValue *gvalue, zval *zvalue)
705 {
706 	GType type = G_VALUE_TYPE(gvalue);
707 
708 	/* The fundamental type ... eg. G_TYPE_ENUM for a VIPS_TYPE_KERNEL, or
709 	 * G_TYPE_OBJECT for VIPS_TYPE_IMAGE().
710 	 */
711 	GType fundamental = G_TYPE_FUNDAMENTAL(type);
712 
713 	const char *str;
714 	GObject *gobject;
715 	zend_resource *resource;
716 	int enum_value;
717 
718 	switch (fundamental) {
719 		case G_TYPE_STRING:
720 			/* These are GStrings, vips refstrings are handled by boxed, see
721 			 * below.
722 			 */
723 			str = g_value_get_string(gvalue);
724 			ZVAL_STRING(zvalue, str);
725 			break;
726 
727 		case G_TYPE_OBJECT:
728 			gobject = g_value_get_object(gvalue);
729 			resource = zend_register_resource(gobject, le_gobject);
730 			ZVAL_RES(zvalue, resource);
731 			break;
732 
733 		case G_TYPE_INT:
734 			ZVAL_LONG(zvalue, g_value_get_int(gvalue));
735 			break;
736 
737 		case G_TYPE_UINT64:
738 			ZVAL_LONG(zvalue, g_value_get_uint64(gvalue));
739 			break;
740 
741 		case G_TYPE_BOOLEAN:
742 			ZVAL_LONG(zvalue, g_value_get_boolean(gvalue));
743 			break;
744 
745 		case G_TYPE_ENUM:
746 			enum_value = g_value_get_enum(gvalue);
747 			str = vips_enum_nick(type, enum_value);
748 			ZVAL_STRING(zvalue, str);
749 			break;
750 
751 		case G_TYPE_FLAGS:
752 			ZVAL_LONG(zvalue, g_value_get_flags(gvalue));
753 			break;
754 
755 		case G_TYPE_DOUBLE:
756 			ZVAL_DOUBLE(zvalue, g_value_get_double(gvalue));
757 			break;
758 
759 		case G_TYPE_BOXED:
760 			if (type == VIPS_TYPE_REF_STRING ||
761 				type == VIPS_TYPE_BLOB) {
762 				const char *str;
763 				size_t str_len;
764 
765 				str = vips_value_get_ref_string(gvalue, &str_len);
766 				ZVAL_STRINGL(zvalue, str, str_len);
767 			}
768 			else if (type == VIPS_TYPE_ARRAY_DOUBLE) {
769 				double *arr;
770 				int n;
771 				int i;
772 
773 				arr = vips_value_get_array_double(gvalue, &n);
774 				array_init(zvalue);
775 				for (i = 0; i < n; i++) {
776 					add_next_index_double(zvalue, arr[i]);
777 				}
778 			}
779 			else if (type == VIPS_TYPE_ARRAY_INT) {
780 				int *arr;
781 				int n;
782 				int i;
783 
784 				arr = vips_value_get_array_int(gvalue, &n);
785 				array_init(zvalue);
786 				for (i = 0; i < n; i++) {
787 					add_next_index_long(zvalue, arr[i]);
788 				}
789 			}
790 			else if (type == VIPS_TYPE_ARRAY_IMAGE) {
791 				VipsImage **arr;
792 				int n;
793 				int i;
794 
795 				arr = vips_value_get_array_image(gvalue, &n);
796 				array_init(zvalue);
797 				for (i = 0; i < n; i++) {
798 					zval x;
799 
800 					g_object_ref(arr[i]);
801 					resource = zend_register_resource(arr[i], le_gobject);
802 					ZVAL_RES(&x, resource);
803 					add_next_index_zval(zvalue, &x);
804 				}
805 			}
806 			else {
807 				g_warning( "%s: unimplemented boxed type %s",
808 					G_STRLOC, g_type_name(type));
809 			}
810 			break;
811 
812 		default:
813 			g_warning( "%s: unimplemented GType %s",
814 				G_STRLOC, g_type_name(fundamental));
815 			break;
816 	}
817 
818 	return 0;
819 }
820 
821 static int
vips_php_get_value(VipsPhpCall * call,GParamSpec * pspec,zval * zvalue)822 vips_php_get_value(VipsPhpCall *call, GParamSpec *pspec, zval *zvalue)
823 {
824 	const char *name = g_param_spec_get_name(pspec);
825 	GType pspec_type = G_PARAM_SPEC_VALUE_TYPE(pspec);
826 	GValue gvalue = { 0 };
827 
828 	g_value_init(&gvalue, pspec_type);
829 	g_object_get_property(G_OBJECT(call->operation), name, &gvalue);
830 	if (vips_php_gval_to_zval(&gvalue, zvalue)) {
831 		g_value_unset(&gvalue);
832 		return -1;
833 	}
834 
835 #ifdef VIPS_DEBUG
836 {
837 	char *str_value;
838 
839 	str_value = g_strdup_value_contents(&gvalue);
840 	VIPS_DEBUG_MSG("    %s.%s = %s\n", call->operation_name, name, str_value);
841 	g_free(str_value);
842 }
843 #endif/*VIPS_DEBUG*/
844 
845 	g_value_unset(&gvalue);
846 
847 	return 0;
848 }
849 
850 static void *
vips_php_get_required_output(VipsObject * object,GParamSpec * pspec,VipsArgumentClass * argument_class,VipsArgumentInstance * argument_instance,void * a,void * b)851 vips_php_get_required_output(VipsObject *object,
852 	GParamSpec *pspec, VipsArgumentClass *argument_class,
853 	VipsArgumentInstance *argument_instance,
854 	void *a, void *b)
855 {
856 	VipsPhpCall *call = (VipsPhpCall *) a;
857 	zval *return_value = (zval *) b;
858 
859 	/* We get output objects, and we get input objects that are tagged as
860 	 * MODIFY --- they are copied on set, see above.
861 	 */
862 	if ((argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
863 		(argument_class->flags & (VIPS_ARGUMENT_OUTPUT| VIPS_ARGUMENT_MODIFY)) &&
864 		!(argument_class->flags & VIPS_ARGUMENT_DEPRECATED)) {
865 		const char *name = g_param_spec_get_name(pspec);
866 		zval zvalue;
867 
868 		if (vips_php_get_value(call, pspec, &zvalue)) {
869 			return call;
870 		}
871 		add_assoc_zval(return_value, name, &zvalue);
872 	}
873 
874 	return NULL;
875 }
876 
877 static int
vips_php_get_optional_output(VipsPhpCall * call,zval * options,zval * return_value)878 vips_php_get_optional_output(VipsPhpCall *call, zval *options,
879 	zval *return_value)
880 {
881 	zend_string *key;
882 	zval *value;
883 
884 	VIPS_DEBUG_MSG("vips_php_get_optional_output:\n");
885 
886 	options = zval_get_nonref(options);
887 
888 	ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(options), key, value) {
889 		char *name;
890 		GParamSpec *pspec;
891 		VipsArgumentClass *argument_class;
892 		VipsArgumentInstance *argument_instance;
893 
894 		if (key == NULL) {
895 			continue;
896 		}
897 
898 		/* value should always be TRUE.
899 		 */
900 		value = zval_get_nonref(value);
901 		if (Z_TYPE_P(value) != IS_TRUE) {
902 			continue;
903 		}
904 
905 		name = ZSTR_VAL(key);
906 		if (vips_object_get_argument(VIPS_OBJECT(call->operation), name,
907 			&pspec, &argument_class, &argument_instance)) {
908 			return -1;
909 		}
910 
911 		if (!(argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
912 			(argument_class->flags & VIPS_ARGUMENT_OUTPUT) &&
913 			!(argument_class->flags & VIPS_ARGUMENT_DEPRECATED)) {
914 			zval zvalue;
915 
916 			if (vips_php_get_value(call, pspec, &zvalue)) {
917 				return -1;
918 			}
919 
920 			add_assoc_zval(return_value, name, &zvalue);
921 		}
922 	} ZEND_HASH_FOREACH_END();
923 
924 	return 0;
925 }
926 
927 /* Call any vips operation, with the arguments coming from an array of zval.
928  * argv can have an extra final arg, which is an associative array of
929  * optional arguments.
930  */
931 static int
vips_php_call_array(const char * operation_name,zval * instance,const char * option_string,int argc,zval * argv,zval * return_value)932 vips_php_call_array(const char *operation_name, zval *instance,
933 	const char *option_string, int argc, zval *argv, zval *return_value)
934 {
935 	VipsPhpCall *call;
936 	int i;
937 
938 	VIPS_DEBUG_MSG("vips_php_call_array:\n");
939 
940 	if (!(call = vips_php_call_new(operation_name, instance, option_string,
941 		argc, argv))) {
942 		return -1;
943 	}
944 
945 	/* Some initial analysis of our args. Loop over them all, including the
946 	 * special 'instance' arg.
947 	 */
948 	VIPS_DEBUG_MSG("vips_php_call_array: analyzing input args ...\n");
949 	if (call->instance) {
950 		vips_php_analyze_arg(call, call->instance);
951 	}
952 	for (i = 0; i < argc; i++) {
953 		vips_php_analyze_arg(call, &call->argv[i]);
954 	}
955 
956 	/* Set str options before vargs options, so the user can't
957 	 * override things we set deliberately.
958 	 */
959 	VIPS_DEBUG_MSG("vips_php_call_array: setting args from option_string ...\n");
960 	if (option_string &&
961 		vips_object_set_from_string(VIPS_OBJECT(call->operation),
962 			option_string)) {
963 		vips_object_unref_outputs(VIPS_OBJECT(call->operation));
964 		vips_php_call_free(call);
965 		return -1;
966 	}
967 
968 	/* Set all required input args from argv.
969 	 */
970 	VIPS_DEBUG_MSG("vips_php_call_array: setting required input args ...\n");
971 	if (vips_argument_map(VIPS_OBJECT(call->operation),
972 		vips_php_set_required_input, call, NULL)) {
973 		vips_object_unref_outputs(VIPS_OBJECT(call->operation));
974 		vips_php_call_free(call);
975 		return -1;
976 	}
977 
978 	/* args_required must match argc, or we allow one extra final arg for options.
979 	 */
980 	VIPS_DEBUG_MSG("vips_php_call_array: testing argc ...\n");
981 	if (call->argc == call->args_required + 1) {
982 		/* Make sure it really is an array.
983 		 */
984 		if (zend_parse_parameter(0, call->argc - 1, &call->argv[call->argc - 1],
985 			"a", &call->options) == FAILURE) {
986 			vips_object_unref_outputs(VIPS_OBJECT(call->operation));
987 			vips_php_call_free(call);
988 			return -1;
989 		}
990 	}
991 	else if (call->argc != call->args_required) {
992 		php_error_docref(NULL, E_WARNING,
993 			"operation %s expects %d arguments, but you supplied %d",
994 			call->operation_name, call->args_required, call->argc);
995 		vips_object_unref_outputs(VIPS_OBJECT(call->operation));
996 		vips_php_call_free(call);
997 		return -1;
998 	}
999 
1000 	/* Set all optional arguments.
1001 	 */
1002 	VIPS_DEBUG_MSG("vips_php_call_array: setting optional input args ...\n");
1003 	if (call->options &&
1004 		vips_php_set_optional_input(call, call->options)) {
1005 		vips_object_unref_outputs(VIPS_OBJECT(call->operation));
1006 		vips_php_call_free(call);
1007 		return -1;
1008 	}
1009 
1010 	/* Look up in cache and build.
1011 	 */
1012 	VIPS_DEBUG_MSG("vips_php_call_array: building ...\n");
1013 	if (vips_cache_operation_buildp(&call->operation)) {
1014 		VIPS_DEBUG_MSG("vips_php_call_array: call failed!\n");
1015 		vips_object_unref_outputs(VIPS_OBJECT(call->operation));
1016 		vips_php_call_free(call);
1017 		return -1;
1018 	}
1019 
1020 	/* Walk args again, getting required output.
1021 	 */
1022 	VIPS_DEBUG_MSG("vips_php_call_array: getting required output ...\n");
1023 	array_init(return_value);
1024 	if (vips_argument_map(VIPS_OBJECT(call->operation),
1025 		vips_php_get_required_output, call, return_value)) {
1026 		vips_object_unref_outputs(VIPS_OBJECT(call->operation));
1027 		vips_php_call_free(call);
1028 		return -1;
1029 	}
1030 
1031 	/* And optional output.
1032 	 */
1033 	VIPS_DEBUG_MSG("vips_php_call_array: getting optional output ...\n");
1034 	if (call->options &&
1035 		vips_php_get_optional_output(call, call->options, return_value)) {
1036 		vips_object_unref_outputs(VIPS_OBJECT(call->operation));
1037 		vips_php_call_free(call);
1038 		return -1;
1039 	}
1040 
1041 	vips_php_call_free(call);
1042 
1043 	VIPS_DEBUG_MSG("vips_php_call_array: success!\n");
1044 
1045 	return 0;
1046 }
1047 
1048 /* }}} */
1049 
1050 /* {{{ proto mixed vips_php_call(string operation_name, resource instance [, more])
1051    Call any vips operation */
1052 
PHP_FUNCTION(vips_call)1053 PHP_FUNCTION(vips_call)
1054 {
1055 	int argc;
1056 	zval *argv;
1057 	char *operation_name;
1058 	size_t operation_name_len;
1059 	zval *instance;
1060 
1061 	VIPS_DEBUG_MSG("vips_call:\n");
1062 
1063 	argc = ZEND_NUM_ARGS();
1064 
1065 	if (argc < 1) {
1066 		WRONG_PARAM_COUNT;
1067 	}
1068 
1069 	argv = (zval *)emalloc(argc * sizeof(zval));
1070 
1071 	if (zend_get_parameters_array_ex(argc, argv) == FAILURE) {
1072 		efree(argv);
1073 		WRONG_PARAM_COUNT;
1074 	}
1075 
1076 	if (zend_parse_parameter(0, 0, &argv[0],
1077 		"s", &operation_name, &operation_name_len) == FAILURE) {
1078 		efree(argv);
1079 		RETURN_LONG(-1);
1080 	}
1081 
1082 	if (zend_parse_parameter(0, 1, &argv[1], "r!", &instance) == FAILURE) {
1083 		efree(argv);
1084 		RETURN_LONG(-1);
1085 	}
1086 
1087 	if (vips_php_call_array(operation_name, instance,
1088 		"", argc - 2, argv + 2, return_value)) {
1089 		efree(argv);
1090 		RETURN_LONG(-1);
1091 	}
1092 
1093 	efree(argv);
1094 }
1095 /* }}} */
1096 
1097 /* {{{ proto resource vips_image_new_from_file(string filename [, array options])
1098    Open an image from a filename */
PHP_FUNCTION(vips_image_new_from_file)1099 PHP_FUNCTION(vips_image_new_from_file)
1100 {
1101 	char *name;
1102 	size_t name_len;
1103 	zval *options;
1104 	char filename[VIPS_PATH_MAX];
1105 	char option_string[VIPS_PATH_MAX];
1106 	const char *operation_name;
1107 	zval argv[2];
1108 	int argc;
1109 
1110 	VIPS_DEBUG_MSG("vips_image_new_from_file:\n");
1111 
1112 	options = NULL;
1113 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|a",
1114 		&name, &name_len, &options) == FAILURE) {
1115 		RETURN_LONG(-1);
1116 	}
1117 	VIPS_DEBUG_MSG("vips_image_new_from_file: name = %s\n", name);
1118 
1119 	vips__filename_split8(name, filename, option_string);
1120 	if (!(operation_name = vips_foreign_find_load(filename))) {
1121 		RETURN_LONG(-1);
1122 	}
1123 
1124 	ZVAL_STRING(&argv[0], filename);
1125 	argc = 1;
1126 	if (options) {
1127 		ZVAL_ARR(&argv[1], Z_ARR_P(options));
1128 		argc += 1;
1129 	}
1130 
1131 	if (vips_php_call_array(operation_name, NULL,
1132 		option_string, argc, argv, return_value)) {
1133 		zval_dtor(&argv[0]);
1134 		RETURN_LONG(-1);
1135 	}
1136 
1137 	zval_dtor(&argv[0]);
1138 }
1139 /* }}} */
1140 
1141 /* {{{ proto resource vips_image_new_from_buffer(string buffer [, string option_string, array options])
1142    Open an image from a string */
PHP_FUNCTION(vips_image_new_from_buffer)1143 PHP_FUNCTION(vips_image_new_from_buffer)
1144 {
1145 	char *buffer;
1146 	size_t buffer_len;
1147 	char *option_string;
1148 	size_t option_string_len;
1149 	zval *options;
1150 	const char *operation_name;
1151 	zval argv[2];
1152 	int argc;
1153 
1154 	VIPS_DEBUG_MSG("vips_image_new_from_buffer:\n");
1155 
1156 	option_string = NULL;
1157 	options = NULL;
1158 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sa",
1159 		&buffer, &buffer_len, &option_string, &option_string_len,
1160 		&options) == FAILURE) {
1161 		RETURN_LONG(-1);
1162 	}
1163 
1164 	if (!(operation_name = vips_foreign_find_load_buffer(buffer, buffer_len))) {
1165 		RETURN_LONG(-1);
1166 	}
1167 
1168 	ZVAL_STRINGL(&argv[0], buffer, buffer_len);
1169 	argc = 1;
1170 	if (options) {
1171 		ZVAL_ARR(&argv[1], Z_ARR_P(options));
1172 		argc += 1;
1173 	}
1174 
1175 	if (vips_php_call_array(operation_name, NULL,
1176 		option_string, argc, argv, return_value)) {
1177 		zval_dtor(&argv[0]);
1178 		RETURN_LONG(-1);
1179 	}
1180 
1181 	zval_dtor(&argv[0]);
1182 }
1183 /* }}} */
1184 
1185 /* {{{ proto resource vips_image_new_from_array(array coefficients [, double scale, double offset])
1186    Open an image from a string */
PHP_FUNCTION(vips_image_new_from_array)1187 PHP_FUNCTION(vips_image_new_from_array)
1188 {
1189 	zval *array;
1190 	double scale;
1191 	double offset;
1192 	int width;
1193 	int height;
1194 	VipsImage *mat;
1195 	int x;
1196 	zval *row;
1197 
1198 	VIPS_DEBUG_MSG("vips_image_new_from_array:\n");
1199 
1200 	scale = 1.0;
1201 	offset = 0.0;
1202 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|dd",
1203 		&array, &scale, &offset) == FAILURE) {
1204 		return;
1205 	}
1206 
1207 	height = zend_hash_num_elements(Z_ARRVAL_P(array));
1208 	if ((row = zend_hash_index_find(Z_ARRVAL_P(array), 0)) == NULL) {
1209 		php_error_docref(NULL, E_WARNING, "no element zero");
1210 		return;
1211 	}
1212 	if (is_2D(array)) {
1213 		mat =  matrix_from_zval(array);
1214 	}
1215 	else {
1216 		/* 1D array.
1217 		 */
1218 		width = height;
1219 		height = 1;
1220 
1221 		mat = vips_image_new_matrix(width, height);
1222 
1223 		for (x = 0; x < width; x++) {
1224 			zval *ele;
1225 
1226 			ele = zend_hash_index_find(Z_ARRVAL_P(array), x);
1227 			if (ele) {
1228 				*VIPS_MATRIX(mat, x, 0) = zval_get_double(ele);
1229 			}
1230 		}
1231 	}
1232 
1233 	vips_image_set_double(mat, "scale", scale);
1234 	vips_image_set_double(mat, "offset", offset);
1235 
1236 	RETURN_RES(zend_register_resource(mat, le_gobject));
1237 }
1238 /* }}} */
1239 
1240 /* {{{ proto resource vips_interpolate_new(string name])
1241    make a new interpolator */
PHP_FUNCTION(vips_interpolate_new)1242 PHP_FUNCTION(vips_interpolate_new)
1243 {
1244 	char *name;
1245 	size_t name_len;
1246 	VipsInterpolate *interp;
1247 
1248 	VIPS_DEBUG_MSG("vips_interpolate_new:\n");
1249 
1250 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p",
1251 		&name, &name_len) == FAILURE) {
1252 		return;
1253 	}
1254 	VIPS_DEBUG_MSG("vips_interpolate_new: name = %s\n", name);
1255 
1256 	if (!(interp = vips_interpolate_new(name)))
1257 		return;
1258 
1259 	RETURN_RES(zend_register_resource(interp, le_gobject));
1260 }
1261 /* }}} */
1262 
1263 /* {{{ proto long vips_image_write_to_file(resource image, string filename [, array options])
1264    Write an image to a filename */
PHP_FUNCTION(vips_image_write_to_file)1265 PHP_FUNCTION(vips_image_write_to_file)
1266 {
1267 	zval *IM;
1268 	char *filename;
1269 	size_t filename_len;
1270 	zval *options = NULL;
1271 	VipsImage *image;
1272 	char path_string[VIPS_PATH_MAX];
1273 	char option_string[VIPS_PATH_MAX];
1274 	const char *operation_name;
1275 	zval argv[2];
1276 	int argc;
1277 
1278 	VIPS_DEBUG_MSG("vips_image_write_to_file:\n");
1279 
1280 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rp|a",
1281 		&IM, &filename, &filename_len, &options) == FAILURE) {
1282 		RETURN_LONG(-1);
1283 	}
1284 
1285 	if ((image = (VipsImage *)zend_fetch_resource(Z_RES_P(IM),
1286 		"GObject", le_gobject)) == NULL) {
1287 		RETURN_LONG(-1);
1288 	}
1289 
1290 	VIPS_DEBUG_MSG("\t%p -> %s\n", image, filename);
1291 
1292 	vips__filename_split8(filename, path_string, option_string);
1293 	if (!(operation_name = vips_foreign_find_save(path_string))) {
1294 		RETURN_LONG(-1);
1295 	}
1296 
1297 	ZVAL_STRINGL(&argv[0], filename, filename_len);
1298 	argc = 1;
1299 	if (options) {
1300 		ZVAL_ARR(&argv[1], Z_ARR_P(options));
1301 		argc += 1;
1302 	}
1303 
1304 	if (vips_php_call_array(operation_name, IM,
1305 		option_string, argc, argv, return_value)) {
1306 		zval_dtor(&argv[0]);
1307 		RETURN_LONG(-1);
1308 	}
1309 
1310 	zval_dtor(&argv[0]);
1311 }
1312 /* }}} */
1313 
1314 /* {{{ proto string|long vips_image_write_to_buffer(resource image, string suffix [, array options])
1315    Write an image to a string */
PHP_FUNCTION(vips_image_write_to_buffer)1316 PHP_FUNCTION(vips_image_write_to_buffer)
1317 {
1318 	zval *IM;
1319 	zval *options = NULL;
1320 	char *suffix;
1321 	size_t suffix_len;
1322 	VipsImage *image;
1323 	char filename[VIPS_PATH_MAX];
1324 	char option_string[VIPS_PATH_MAX];
1325 	const char *operation_name;
1326 	zval argv[1];
1327 	int argc;
1328 
1329 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs|a",
1330 		&IM, &suffix, &suffix_len, &options) == FAILURE) {
1331 		RETURN_LONG(-1);
1332 	}
1333 
1334 	if ((image = (VipsImage *)zend_fetch_resource(Z_RES_P(IM),
1335 		"GObject", le_gobject)) == NULL) {
1336 		RETURN_LONG(-1);
1337 	}
1338 
1339 	vips__filename_split8(suffix, filename, option_string);
1340 	if (!(operation_name = vips_foreign_find_save_buffer(filename))) {
1341 		RETURN_LONG(-1);
1342 	}
1343 
1344 	argc = 0;
1345 	if (options) {
1346 		ZVAL_ARR(&argv[0], Z_ARR_P(options));
1347 		argc += 1;
1348 	}
1349 
1350 	if (vips_php_call_array(operation_name, IM,
1351 		option_string, argc, argv, return_value)) {
1352 		RETURN_LONG(-1);
1353 	}
1354 }
1355 /* }}} */
1356 
1357 /* {{{ proto resource vips_image_copy_memory(resource image)
1358    Copy an image to a memory image */
PHP_FUNCTION(vips_image_copy_memory)1359 PHP_FUNCTION(vips_image_copy_memory)
1360 {
1361 	zval *IM;
1362 	VipsImage *image;
1363 	VipsImage *new_image;
1364 	zend_resource *resource;
1365 	zval zvalue;
1366 
1367 	VIPS_DEBUG_MSG("vips_image_copy_memory:\n");
1368 
1369 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &IM) == FAILURE) {
1370 		RETURN_LONG(-1);
1371 	}
1372 
1373 	if ((image = (VipsImage *)zend_fetch_resource(Z_RES_P(IM),
1374 		"GObject", le_gobject)) == NULL) {
1375 		RETURN_LONG(-1);
1376 	}
1377 
1378 	if (!(new_image = vips_image_copy_memory(image))) {
1379 		RETURN_LONG(-1);
1380 	}
1381 
1382 	/* Return as an array for all OK, -1 for error.
1383 	 */
1384 	array_init(return_value);
1385 	resource = zend_register_resource(new_image, le_gobject);
1386 	ZVAL_RES(&zvalue, resource);
1387 	add_assoc_zval(return_value, "out", &zvalue);
1388 }
1389 /* }}} */
1390 
1391 /* {{{ proto resource vips_image_new_from_memory(string data, integer width, integer height, integer bands, string format)
1392    Wrap an image around a memory array. */
PHP_FUNCTION(vips_image_new_from_memory)1393 PHP_FUNCTION(vips_image_new_from_memory)
1394 {
1395 	char *bstr;
1396 	size_t bstr_len;
1397 	long width;
1398 	long height;
1399 	long bands;
1400 	char *format;
1401 	size_t format_len;
1402 	int format_value;
1403 	VipsBandFormat band_format;
1404 	VipsImage *image;
1405 	zend_resource *resource;
1406 	zval zvalue;
1407 
1408 	VIPS_DEBUG_MSG("vips_image_new_from_memory:\n");
1409 
1410 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "slllp",
1411 		&bstr, &bstr_len, &width, &height, &bands, &format, &format_len) == FAILURE) {
1412 		RETURN_LONG(-1);
1413 	}
1414 
1415 	if ((format_value = vips_enum_from_nick("php-vips", VIPS_TYPE_BAND_FORMAT, format)) < 0) {
1416 		RETURN_LONG(-1);
1417 	}
1418 	band_format = format_value;
1419 
1420 	if (!(image = vips_image_new_from_memory_copy(bstr, bstr_len, width, height, bands,
1421 		band_format))) {
1422 		RETURN_LONG(-1);
1423 	}
1424 
1425 	/* Return as an array for all OK, -1 for error.
1426 	 */
1427 	array_init(return_value);
1428 	resource = zend_register_resource(image, le_gobject);
1429 	ZVAL_RES(&zvalue, resource);
1430 	add_assoc_zval(return_value, "out", &zvalue);
1431 }
1432 /* }}} */
1433 
1434 /* {{{ proto string vips_image_write_to_memory(resource image)
1435    Write an image to a memory array. */
PHP_FUNCTION(vips_image_write_to_memory)1436 PHP_FUNCTION(vips_image_write_to_memory)
1437 {
1438 	zval *IM;
1439 	VipsImage *image;
1440 	size_t arr_len;
1441 	uint8_t *arr;
1442 
1443 	VIPS_DEBUG_MSG("vips_image_write_to_memory:\n");
1444 
1445 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &IM) == FAILURE) {
1446 		RETURN_LONG(-1);
1447 	}
1448 
1449 	if ((image = (VipsImage *)zend_fetch_resource(Z_RES_P(IM),
1450 		"GObject", le_gobject)) == NULL) {
1451 		RETURN_LONG(-1);
1452 	}
1453 
1454 	if (!(arr = vips_image_write_to_memory(image, &arr_len))) {
1455 		RETURN_LONG(-1);
1456 	}
1457 
1458 	RETVAL_STRINGL((char *)arr, arr_len);
1459 
1460 	g_free(arr);
1461 }
1462 /* }}} */
1463 
1464 #define ADD_ELEMENTS(TYPE, APPEND, N) { \
1465 	TYPE *p = (TYPE *) arr; \
1466 	size_t i; \
1467 	\
1468 	for (i = 0; i < (N); i++) \
1469 		APPEND(return_value, p[i]); \
1470 }
1471 
1472 /* {{{ proto array vips_image_write_to_array(resource image)
1473    Write an image to a PHP array. */
PHP_FUNCTION(vips_image_write_to_array)1474 PHP_FUNCTION(vips_image_write_to_array)
1475 {
1476 	zval *IM;
1477 	VipsImage *image;
1478 	size_t arr_len;
1479 	uint8_t *arr;
1480 	size_t n;
1481 
1482 	VIPS_DEBUG_MSG("vips_image_write_to_array:\n");
1483 
1484 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &IM) == FAILURE) {
1485 		RETURN_LONG(-1);
1486 	}
1487 
1488 	if ((image = (VipsImage *)zend_fetch_resource(Z_RES_P(IM),
1489 		"GObject", le_gobject)) == NULL) {
1490 		RETURN_LONG(-1);
1491 	}
1492 
1493 	if (!(arr = vips_image_write_to_memory(image, &arr_len))) {
1494 		RETURN_LONG(-1);
1495 	}
1496 
1497 	array_init(return_value);
1498 	n = arr_len / vips_format_sizeof(image->BandFmt);
1499 	g_assert(arr_len % vips_format_sizeof(image->BandFmt) == 0);
1500 	switch (image->BandFmt) {
1501 	case VIPS_FORMAT_UCHAR:
1502 		ADD_ELEMENTS (unsigned char, add_next_index_long, n);
1503 		break;
1504 
1505 	case VIPS_FORMAT_CHAR:
1506 		ADD_ELEMENTS (signed char, add_next_index_long, n);
1507 		break;
1508 
1509 	case VIPS_FORMAT_USHORT:
1510 		ADD_ELEMENTS (unsigned short, add_next_index_long, n);
1511 		break;
1512 
1513 	case VIPS_FORMAT_SHORT:
1514 		ADD_ELEMENTS (signed short, add_next_index_long, n);
1515 		break;
1516 
1517 	case VIPS_FORMAT_UINT:
1518 		ADD_ELEMENTS (unsigned int, add_next_index_long, n);
1519 		break;
1520 
1521 	case VIPS_FORMAT_INT:
1522 		ADD_ELEMENTS (signed int, add_next_index_long, n);
1523 		break;
1524 
1525 	case VIPS_FORMAT_FLOAT:
1526 		ADD_ELEMENTS (float, add_next_index_double, n);
1527 		break;
1528 
1529 	case VIPS_FORMAT_DOUBLE:
1530 		ADD_ELEMENTS (double, add_next_index_double, n);
1531 		break;
1532 
1533 	case VIPS_FORMAT_COMPLEX:
1534 		ADD_ELEMENTS (float, add_next_index_double, n * 2);
1535 		break;
1536 
1537 	case VIPS_FORMAT_DPCOMPLEX:
1538 		ADD_ELEMENTS (double, add_next_index_double, n * 2);
1539 		break;
1540 
1541 	default:
1542 		break;
1543 	}
1544 
1545 	g_free(arr);
1546 }
1547 /* }}} */
1548 
1549 /* {{{ proto string|long vips_foreign_find_load(string filename)
1550    Find a loader for a file */
PHP_FUNCTION(vips_foreign_find_load)1551 PHP_FUNCTION(vips_foreign_find_load)
1552 {
1553 	char *filename;
1554 	size_t filename_len;
1555 	const char *operation_name;
1556 
1557 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
1558 		&filename, &filename_len) == FAILURE) {
1559 		RETURN_LONG(-1);
1560 	}
1561 
1562 	if (!(operation_name = vips_foreign_find_load(filename))) {
1563 		RETURN_LONG(-1);
1564 	}
1565 
1566 	RETVAL_STRING(strdup(operation_name));
1567 }
1568 /* }}} */
1569 
1570 /* {{{ proto string|long vips_foreign_find_load_buffer(string buffer)
1571    Find a loader for a buffer */
PHP_FUNCTION(vips_foreign_find_load_buffer)1572 PHP_FUNCTION(vips_foreign_find_load_buffer)
1573 {
1574 	char *buffer;
1575 	size_t buffer_len;
1576 	const char *operation_name;
1577 
1578 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
1579 		&buffer, &buffer_len) == FAILURE) {
1580 		RETURN_LONG(-1);
1581 	}
1582 
1583 	if (!(operation_name = vips_foreign_find_load_buffer(buffer, buffer_len))) {
1584 		RETURN_LONG(-1);
1585 	}
1586 
1587 	RETVAL_STRING(strdup(operation_name));
1588 }
1589 /* }}} */
1590 
1591 /* {{{ proto array vips_image_get(resource image, string field)
1592    Fetch field from image */
PHP_FUNCTION(vips_image_get)1593 PHP_FUNCTION(vips_image_get)
1594 {
1595 	zval *im;
1596 	char *field_name;
1597 	size_t field_name_len;
1598 	VipsImage *image;
1599 	GValue gvalue = { 0 };
1600 	zval zvalue;
1601 	GParamSpec *pspec;
1602 
1603 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs",
1604 		&im, &field_name, &field_name_len) == FAILURE) {
1605 		RETURN_LONG(-1);
1606 	}
1607 
1608 	if ((image = (VipsImage *)zend_fetch_resource(Z_RES_P(im),
1609 		"GObject", le_gobject)) == NULL) {
1610 		RETURN_LONG(-1);
1611 	}
1612 
1613 	/* Ugly: older libvipses would return enums from the true header fields
1614 	 * (eg. ->interpretation) as ints, but we want to send a string back
1615 	 * for things like this.
1616 	 *
1617 	 * Test if field_name exists as a regular glib property and if it does, use
1618 	 * g_object_get(). Otherwise use vips_image_get(), since it can read extra
1619 	 * image metadata.
1620 	 */
1621 	if ((pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(image),
1622 		field_name))) {
1623 		g_value_init(&gvalue, G_PARAM_SPEC_VALUE_TYPE(pspec));
1624 		g_object_get_property(G_OBJECT(image), field_name, &gvalue);
1625 	}
1626 	else if (vips_image_get(image, field_name, &gvalue)) {
1627 		RETURN_LONG(-1);
1628 	}
1629 
1630 	if (vips_php_gval_to_zval(&gvalue, &zvalue)) {
1631 		g_value_unset(&gvalue);
1632 		RETURN_LONG(-1);
1633 	}
1634 	g_value_unset(&gvalue);
1635 
1636 	/* Return as an array for all OK, -1 for error.
1637 	 */
1638 	array_init(return_value);
1639 	add_assoc_zval(return_value, "out", &zvalue);
1640 }
1641 /* }}} */
1642 
1643 /* {{{ proto long vips_image_get_typeof(resource image, string field)
1644    Fetch type of field from image */
PHP_FUNCTION(vips_image_get_typeof)1645 PHP_FUNCTION(vips_image_get_typeof)
1646 {
1647 	zval *im;
1648 	char *field_name;
1649 	size_t field_name_len;
1650 	VipsImage *image;
1651 	GType type;
1652 
1653 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs",
1654 		&im, &field_name, &field_name_len) == FAILURE) {
1655 		RETURN_LONG(-1);
1656 	}
1657 
1658 	if ((image = (VipsImage *)zend_fetch_resource(Z_RES_P(im),
1659 		"GObject", le_gobject)) == NULL) {
1660 		RETURN_LONG(-1);
1661 	}
1662 
1663 	type = vips_image_get_typeof(image, field_name);
1664 
1665 	RETURN_LONG(type);
1666 }
1667 /* }}} */
1668 
1669 /* {{{ proto long vips_image_set(resource image, string field, mixed value)
1670    Set field on image */
PHP_FUNCTION(vips_image_set)1671 PHP_FUNCTION(vips_image_set)
1672 {
1673 	zval *im;
1674 	char *field_name;
1675 	size_t field_name_len;
1676 	zval *zvalue;
1677 	VipsImage *image;
1678 	GType type;
1679 	GValue gvalue = { 0 };
1680 
1681 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsz",
1682 		&im, &field_name, &field_name_len, &zvalue) == FAILURE) {
1683 		RETURN_LONG(-1);
1684 	}
1685 
1686 	if ((image = (VipsImage *)zend_fetch_resource(Z_RES_P(im),
1687 		"GObject", le_gobject)) == NULL) {
1688 		RETURN_LONG(-1);
1689 	}
1690 
1691 	type = vips_image_get_typeof(image, field_name);
1692 
1693 	/* If the type is not set, guess a default.
1694 	 */
1695 	if (type == 0) {
1696 		zval *ele;
1697 
1698 		type = 0;
1699 
1700 		zvalue = zval_get_nonref(zvalue);
1701 
1702 		switch (Z_TYPE_P(zvalue)) {
1703 			case IS_ARRAY:
1704 				if ((ele = zend_hash_index_find(Z_ARRVAL_P(zvalue),
1705 					0)) != NULL) {
1706 					ele = zval_get_nonref(ele);
1707 
1708 					switch (Z_TYPE_P(ele)) {
1709 						case IS_RESOURCE:
1710 							type = VIPS_TYPE_ARRAY_IMAGE;
1711 							break;
1712 
1713 						case IS_LONG:
1714 							type = VIPS_TYPE_ARRAY_INT;
1715 							break;
1716 
1717 						case IS_DOUBLE:
1718 							type = VIPS_TYPE_ARRAY_DOUBLE;
1719 							break;
1720 
1721 						default:
1722 							break;
1723 					}
1724 				}
1725 				break;
1726 
1727 			case IS_RESOURCE:
1728 				type = VIPS_TYPE_IMAGE;
1729 				break;
1730 
1731 			case IS_LONG:
1732 				type = G_TYPE_INT;
1733 				break;
1734 
1735 			case IS_DOUBLE:
1736 				type = G_TYPE_DOUBLE;
1737 				break;
1738 
1739 			case IS_STRING:
1740 				type = VIPS_TYPE_REF_STRING;
1741 				break;
1742 
1743 			default:
1744 				break;
1745 		}
1746 	}
1747 
1748 	g_value_init(&gvalue, type);
1749 
1750 	if (vips_php_zval_to_gval(NULL, zvalue, &gvalue)) {
1751 		RETURN_LONG(-1);
1752 	}
1753 
1754 	vips_image_set(image, field_name, &gvalue);
1755 
1756 	g_value_unset(&gvalue);
1757 
1758 	RETURN_LONG(0);
1759 }
1760 /* }}} */
1761 
1762 /* {{{ proto long vips_image_remove(resource image, string field)
1763    Remove field from image */
PHP_FUNCTION(vips_image_remove)1764 PHP_FUNCTION(vips_image_remove)
1765 {
1766 	zval *im;
1767 	char *field_name;
1768 	size_t field_name_len;
1769 	VipsImage *image;
1770 
1771 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs",
1772 		&im, &field_name, &field_name_len) == FAILURE) {
1773 		RETURN_LONG(-1);
1774 	}
1775 
1776 	if ((image = (VipsImage *)zend_fetch_resource(Z_RES_P(im),
1777 		"GObject", le_gobject)) == NULL) {
1778 		RETURN_LONG(-1);
1779 	}
1780 
1781 	if (!vips_image_remove(image, field_name)) {
1782 		RETURN_LONG(-1);
1783 	}
1784 
1785 	RETURN_LONG(0);
1786 }
1787 /* }}} */
1788 
1789 /* {{{ proto string vips_error_buffer()
1790    Fetch and clear the vips error buffer */
PHP_FUNCTION(vips_error_buffer)1791 PHP_FUNCTION(vips_error_buffer)
1792 {
1793 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "") == FAILURE) {
1794 		return;
1795 	}
1796 
1797 	RETVAL_STRING(strdup(vips_error_buffer()));
1798 	vips_error_clear();
1799 }
1800 /* }}} */
1801 
1802 /* {{{ proto void vips_cache_set_max(integer value)
1803    Set max number of operations to cache */
PHP_FUNCTION(vips_cache_set_max)1804 PHP_FUNCTION(vips_cache_set_max)
1805 {
1806 	long value;
1807 
1808 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &value) == FAILURE) {
1809 		return;
1810 	}
1811 
1812 	vips_cache_set_max(value);
1813 }
1814 /* }}} */
1815 
1816 /* {{{ proto void vips_cache_set_max_mem(integer value)
1817    Set max memory to use for operation cache */
PHP_FUNCTION(vips_cache_set_max_mem)1818 PHP_FUNCTION(vips_cache_set_max_mem)
1819 {
1820 	long value;
1821 
1822 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &value) == FAILURE) {
1823 		return;
1824 	}
1825 
1826 	vips_cache_set_max_mem(value);
1827 }
1828 /* }}} */
1829 
1830 /* {{{ proto void vips_cache_set_max_files(integer value)
1831    Set max number of open files for operation cache */
PHP_FUNCTION(vips_cache_set_max_files)1832 PHP_FUNCTION(vips_cache_set_max_files)
1833 {
1834 	long value;
1835 
1836 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &value) == FAILURE) {
1837 		return;
1838 	}
1839 
1840 	vips_cache_set_max_files(value);
1841 }
1842 /* }}} */
1843 
1844 /* {{{ proto void vips_concurrency_set(integer value)
1845    Set number of workers per threadpool */
PHP_FUNCTION(vips_concurrency_set)1846 PHP_FUNCTION(vips_concurrency_set)
1847 {
1848 	long value;
1849 
1850 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &value) == FAILURE) {
1851 		return;
1852 	}
1853 
1854 	vips_concurrency_set(value);
1855 }
1856 /* }}} */
1857 
1858 /* {{{ proto string vips_version()
1859    Returns the version number of the vips library */
PHP_FUNCTION(vips_version)1860 PHP_FUNCTION(vips_version)
1861 {
1862 	char digits[256];
1863 
1864 	vips_snprintf(digits, 256, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
1865 
1866 	RETVAL_STRING(digits);
1867 }
1868 /* }}} */
1869 
1870 /* {{{ proto integer vips_cache_get_max_mem()
1871    Get max memory to use for operation cache */
PHP_FUNCTION(vips_cache_get_max_mem)1872 PHP_FUNCTION(vips_cache_get_max_mem)
1873 {
1874 	RETURN_LONG(vips_cache_get_max_mem());
1875 }
1876 /* }}} */
1877 
1878 /* {{{ proto integer vips_cache_get_max_files()
1879    Get max number of open files for operation cache */
PHP_FUNCTION(vips_cache_get_max_files)1880 PHP_FUNCTION(vips_cache_get_max_files)
1881 {
1882 	RETURN_LONG(vips_cache_get_max_files());
1883 }
1884 /* }}} */
1885 
1886 /* {{{ proto integer vips_cache_get_max()
1887    Get max number of operations to cache */
PHP_FUNCTION(vips_cache_get_max)1888 PHP_FUNCTION(vips_cache_get_max)
1889 {
1890 	RETURN_LONG(vips_cache_get_max());
1891 }
1892 /* }}} */
1893 
1894 /* {{{ proto integer vips_cache_get_size()
1895    Get current cached operations */
PHP_FUNCTION(vips_cache_get_size)1896 PHP_FUNCTION(vips_cache_get_size)
1897 {
1898 	RETURN_LONG(vips_cache_get_size());
1899 }
1900 /* }}} */
1901 
1902 /* {{{ proto integer vips_concurrency_get()
1903    Get number of workers per threadpool */
PHP_FUNCTION(vips_concurrency_get)1904 PHP_FUNCTION(vips_concurrency_get)
1905 {
1906 	RETURN_LONG(vips_concurrency_get());
1907 }
1908 /* }}} */
1909 
1910 /* {{{ php_vips_init_globals
1911  */
1912 /* Uncomment this function if you have INI entries
1913 static void php_vips_init_globals(zend_vips_globals *vips_globals)
1914 {
1915 	vips_globals->global_value = 0;
1916 	vips_globals->global_string = NULL;
1917 }
1918 */
1919 /* }}} */
1920 
1921 /* {{{ php_free_vips_object
1922  *  */
php_free_gobject(zend_resource * rsrc)1923 static void php_free_gobject(zend_resource *rsrc)
1924 {
1925 	VIPS_DEBUG_MSG("php_free_gobject: %p\n", rsrc->ptr);
1926 
1927 	g_object_unref((GObject *) rsrc->ptr);
1928 }
1929 /* }}} */
1930 
1931 /* {{{ PHP_MINIT_FUNCTION
1932  */
PHP_MINIT_FUNCTION(vips)1933 PHP_MINIT_FUNCTION(vips)
1934 {
1935 	if (strcmp(sapi_module.name, "apache2handler") == 0) {
1936 		/* "apachectl graceful" can cause us terrible problems. What happens:
1937 		 *
1938 		 * - the main apache process unloads this extension, vips.so
1939 		 * - in turn, the C runtime will unload libvips.so, the vips library,
1940 		 *   since vips.so is the only thing that references it
1941 		 * - libvips.so in turn uses glib.so, but this is often not unloaded,
1942 		 *   since other parts of apache can be using it (glib could also
1943 		 *   possibly be preventing unload itself, I'm not sure)
1944 		 * - the main apache process then reloads vips.so, which in turn will
1945 		 *   reload libvips.so as it starts up
1946 		 * - vips.so tries to init libvips.so
1947 		 * - libvips.so tries to register its types (such as VipsImage) with
1948 		 *   glib.so, but finds the types from the previous init still there
1949 		 * - everything breaks
1950 		 *
1951 		 * A simple fix that will always work is just to lock libvips in
1952 		 * memory and prevent unload. We intentionally leak refs to the shared
1953 		 * library.
1954 		 *
1955 		 * We include the binary API version number that this extension needs.
1956 		 * We can't just load .so, that's only installed with libvips-dev,
1957 		 * which may not be present at runtime.
1958 		 */
1959 #ifdef VIPS_SONAME
1960 		if (!dlopen(VIPS_SONAME, RTLD_LAZY | RTLD_NODELETE))
1961 #else /*!VIPS_SONAME*/
1962 		if (!dlopen("libvips.so.42", RTLD_LAZY | RTLD_NODELETE))
1963 #endif /*VIPS_SONAME*/
1964 		{
1965 			sapi_module.sapi_error(E_WARNING, "php-vips-ext: unable to lock "
1966 				"libvips -- graceful may be unreliable");
1967 		}
1968 	}
1969 
1970 	/* If you have INI entries, uncomment these lines
1971 	REGISTER_INI_ENTRIES();
1972 	*/
1973 
1974 	/* We're supposed to use the filename of something we think is in
1975 	 * $VIPSHOME/bin, but we don't have that. Use a nonsense name and
1976 	 * vips_init() will fall back to other techniques for finding data
1977 	 * files.
1978 	 */
1979 	if (VIPS_INIT("banana"))
1980 		return FAILURE;
1981 
1982 	le_gobject = zend_register_list_destructors_ex(php_free_gobject,
1983 		NULL, "GObject", module_number);
1984 
1985 #ifdef VIPS_DEBUG
1986 	printf( "php-vips-ext init\n" );
1987 	printf( "enabling vips leak testing ...\n" );
1988 	vips_leak_set( TRUE );
1989 #endif /*VIPS_DEBUG*/
1990 
1991 	return SUCCESS;
1992 }
1993 /* }}} */
1994 
1995 /* {{{ PHP_MSHUTDOWN_FUNCTION
1996  */
PHP_MSHUTDOWN_FUNCTION(vips)1997 PHP_MSHUTDOWN_FUNCTION(vips)
1998 {
1999 	/* uncomment this line if you have INI entries
2000 	UNREGISTER_INI_ENTRIES();
2001 	*/
2002 
2003 #ifdef VIPS_DEBUG
2004 	printf( "php-vips-ext shutdown\n" );
2005 #endif /*VIPS_DEBUG*/
2006 
2007 	/* We must not call vips_shutdown() since we've locked libvips in memory
2008 	 * and will need to reuse it if we restart via graceful.
2009 	 */
2010 
2011 	return SUCCESS;
2012 }
2013 /* }}} */
2014 
2015 /* Remove if there's nothing to do at request start */
2016 /* {{{ PHP_RINIT_FUNCTION
2017  */
PHP_RINIT_FUNCTION(vips)2018 PHP_RINIT_FUNCTION(vips)
2019 {
2020 #if defined(COMPILE_DL_VIPS) && defined(ZTS)
2021 	ZEND_TSRMLS_CACHE_UPDATE();
2022 #endif
2023 	return SUCCESS;
2024 }
2025 /* }}} */
2026 
2027 /* Remove if there's nothing to do at request end */
2028 /* {{{ PHP_RSHUTDOWN_FUNCTION
2029  */
PHP_RSHUTDOWN_FUNCTION(vips)2030 PHP_RSHUTDOWN_FUNCTION(vips)
2031 {
2032 	return SUCCESS;
2033 }
2034 /* }}} */
2035 
2036 /* {{{ PHP_MINFO_FUNCTION
2037  */
PHP_MINFO_FUNCTION(vips)2038 PHP_MINFO_FUNCTION(vips)
2039 {
2040 	char digits[256];
2041 
2042 	php_info_print_table_start();
2043 	php_info_print_table_header(2, "vips property", "value");
2044 
2045 	vips_snprintf(digits, 256, "%d.%d.%d", VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION, VIPS_MICRO_VERSION);
2046 	php_info_print_table_row(2, "Vips headers version", digits);
2047 	vips_snprintf(digits, 256, "%d.%d.%d", vips_version(0), vips_version(1), vips_version(2));
2048 	php_info_print_table_row(2, "Vips library version", digits);
2049 	vips_snprintf(digits, 256, "%d.%d.%d", vips_version(3), vips_version(4), vips_version(5));
2050 	php_info_print_table_row(2, "Vips ABI version", digits);
2051 
2052 	vips_snprintf(digits, 256, "%d", vips_version(0));
2053 	php_info_print_table_row(2, "Major version", digits);
2054 	vips_snprintf(digits, 256, "%d", vips_version(1));
2055 	php_info_print_table_row(2, "Minor version", digits);
2056 	vips_snprintf(digits, 256, "%d", vips_version(2));
2057 	php_info_print_table_row(2, "Micro version", digits);
2058 
2059 	vips_snprintf(digits, 256, "%zd", vips_cache_get_max_mem());
2060 	php_info_print_table_row(2, "Cache max mem", digits);
2061 
2062 	vips_snprintf(digits, 256, "%d", vips_cache_get_max());
2063 	php_info_print_table_row(2, "Cache max operations", digits);
2064 
2065 	vips_snprintf(digits, 256, "%d", vips_cache_get_size());
2066 	php_info_print_table_row(2, "Cache current operations", digits);
2067 
2068 	vips_snprintf(digits, 256, "%d", vips_cache_get_max_files());
2069 	php_info_print_table_row(2, "Cache max open files", digits);
2070 
2071 	vips_snprintf(digits, 256, "%d", vips_concurrency_get());
2072 	php_info_print_table_row(2, "Concurrency", digits);
2073 
2074 	php_info_print_table_row(2, "SIMD support with liborc",
2075 		vips_vector_isenabled() ? "yes" : "no" );
2076 
2077 	php_info_print_table_row(2, "JPEG support",
2078 		vips_type_find("VipsOperation", "jpegload") ? "yes" : "no" );
2079 	php_info_print_table_row(2, "PNG support",
2080 		vips_type_find("VipsOperation", "pngload") ? "yes" : "no" );
2081 	php_info_print_table_row(2, "TIFF support",
2082 		vips_type_find("VipsOperation", "tiffload") ? "yes" : "no" );
2083 	php_info_print_table_row(2, "GIF support",
2084 		vips_type_find("VipsOperation", "gifload") ? "yes" : "no" );
2085 	php_info_print_table_row(2, "OpenEXR support",
2086 		vips_type_find("VipsOperation", "openexrload") ? "yes" : "no" );
2087 	php_info_print_table_row(2, "load OpenSlide",
2088 		vips_type_find("VipsOperation", "openslideload") ? "yes" : "no" );
2089 	php_info_print_table_row(2, "load Matlab",
2090 		vips_type_find("VipsOperation", "matload") ? "yes" : "no" );
2091 	php_info_print_table_row(2, "load PDF",
2092 		vips_type_find("VipsOperation", "pdfload") ? "yes" : "no" );
2093 	php_info_print_table_row(2, "load SVG",
2094 		vips_type_find("VipsOperation", "svgload") ? "yes" : "no" );
2095 	php_info_print_table_row(2, "FITS support",
2096 		vips_type_find("VipsOperation", "fitsload") ? "yes" : "no" );
2097 	php_info_print_table_row(2, "WebP support",
2098 		vips_type_find("VipsOperation", "webpload") ? "yes" : "no" );
2099 	php_info_print_table_row(2, "HEIF support",
2100 		vips_type_find("VipsOperation", "heifload") ? "yes" : "no" );
2101 
2102 	php_info_print_table_row(2, "load with libMagick",
2103 		vips_type_find("VipsOperation", "magickload") ? "yes" : "no" );
2104 
2105 	php_info_print_table_row(2, "Text rendering support",
2106 		vips_type_find("VipsOperation", "text") ? "yes" : "no" );
2107 
2108 	php_info_print_table_row(2, "ICC profile support with lcms",
2109 		vips_icc_present() ? "yes" : "no" );
2110 
2111 	php_info_print_table_end();
2112 
2113 	/* Remove comments if you have entries in php.ini
2114 	DISPLAY_INI_ENTRIES();
2115 	*/
2116 }
2117 /* }}} */
2118 
2119 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_new_from_file, 0)
2120 	ZEND_ARG_INFO(0, filename)
2121 	ZEND_ARG_INFO(0, options)
2122 ZEND_END_ARG_INFO()
2123 
2124 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_new_from_buffer, 0)
2125 	ZEND_ARG_INFO(0, buffer)
2126 	ZEND_ARG_INFO(0, option_string)
2127 	ZEND_ARG_INFO(0, options)
2128 ZEND_END_ARG_INFO()
2129 
2130 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_new_from_array, 0)
2131 	ZEND_ARG_INFO(0, array)
2132 	ZEND_ARG_INFO(0, scale)
2133 	ZEND_ARG_INFO(0, offset)
2134 ZEND_END_ARG_INFO()
2135 
2136 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_write_to_file, 0)
2137 	ZEND_ARG_INFO(0, image)
2138 	ZEND_ARG_INFO(0, filename)
2139 	ZEND_ARG_INFO(0, options)
2140 ZEND_END_ARG_INFO()
2141 
2142 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_write_to_buffer, 0)
2143 	ZEND_ARG_INFO(0, image)
2144 	ZEND_ARG_INFO(0, options)
2145 ZEND_END_ARG_INFO()
2146 
2147 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_copy_memory, 0)
2148 	ZEND_ARG_INFO(0, image)
2149 ZEND_END_ARG_INFO()
2150 
2151 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_new_from_memory, 0)
2152 	ZEND_ARG_INFO(0, array)
2153 	ZEND_ARG_INFO(0, width)
2154 	ZEND_ARG_INFO(0, height)
2155 	ZEND_ARG_INFO(0, bands)
2156 	ZEND_ARG_INFO(0, format)
2157 ZEND_END_ARG_INFO()
2158 
2159 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_write_to_memory, 0)
2160 	ZEND_ARG_INFO(0, image)
2161 ZEND_END_ARG_INFO()
2162 
2163 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_write_to_array, 0)
2164 	ZEND_ARG_INFO(0, image)
2165 ZEND_END_ARG_INFO()
2166 
2167 ZEND_BEGIN_ARG_INFO(arginfo_vips_foreign_find_load, 0)
2168 	ZEND_ARG_INFO(0, filename)
2169 ZEND_END_ARG_INFO()
2170 
2171 ZEND_BEGIN_ARG_INFO(arginfo_vips_interpolate_new, 0)
2172 	ZEND_ARG_INFO(0, name)
2173 ZEND_END_ARG_INFO()
2174 
2175 ZEND_BEGIN_ARG_INFO(arginfo_vips_foreign_find_load_buffer, 0)
2176 	ZEND_ARG_INFO(0, buffer)
2177 ZEND_END_ARG_INFO()
2178 
2179 ZEND_BEGIN_ARG_INFO(arginfo_vips_call, 0)
2180 	ZEND_ARG_INFO(0, operation_name)
2181 	ZEND_ARG_INFO(0, instance)
2182 ZEND_END_ARG_INFO()
2183 
2184 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_get, 0)
2185 	ZEND_ARG_INFO(0, image)
2186 	ZEND_ARG_INFO(0, field)
2187 ZEND_END_ARG_INFO()
2188 
2189 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_get_typeof, 0)
2190 	ZEND_ARG_INFO(0, image)
2191 	ZEND_ARG_INFO(0, field)
2192 ZEND_END_ARG_INFO()
2193 
2194 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_set, 0)
2195 	ZEND_ARG_INFO(0, image)
2196 	ZEND_ARG_INFO(0, field)
2197 	ZEND_ARG_INFO(0, value)
2198 ZEND_END_ARG_INFO()
2199 
2200 ZEND_BEGIN_ARG_INFO(arginfo_vips_image_remove, 0)
2201 	ZEND_ARG_INFO(0, image)
2202 	ZEND_ARG_INFO(0, field)
2203 ZEND_END_ARG_INFO()
2204 
2205 ZEND_BEGIN_ARG_INFO(arginfo_vips_error_buffer, 0)
2206 ZEND_END_ARG_INFO()
2207 
2208 ZEND_BEGIN_ARG_INFO(arginfo_vips_cache_set_max, 0)
2209 	ZEND_ARG_INFO(0, value)
2210 ZEND_END_ARG_INFO()
2211 
2212 ZEND_BEGIN_ARG_INFO(arginfo_vips_cache_set_max_mem, 0)
2213 	ZEND_ARG_INFO(0, value)
2214 ZEND_END_ARG_INFO()
2215 
2216 ZEND_BEGIN_ARG_INFO(arginfo_vips_cache_set_max_files, 0)
2217 	ZEND_ARG_INFO(0, value)
2218 ZEND_END_ARG_INFO()
2219 
2220 ZEND_BEGIN_ARG_INFO(arginfo_vips_concurrency_set, 0)
2221 	ZEND_ARG_INFO(0, value)
2222 ZEND_END_ARG_INFO()
2223 
2224 ZEND_BEGIN_ARG_INFO(arginfo_vips_cache_get_max, 0)
2225 ZEND_END_ARG_INFO()
2226 
2227 ZEND_BEGIN_ARG_INFO(arginfo_vips_cache_get_max_mem, 0)
2228 ZEND_END_ARG_INFO()
2229 
2230 ZEND_BEGIN_ARG_INFO(arginfo_vips_cache_get_max_files, 0)
2231 ZEND_END_ARG_INFO()
2232 
2233 ZEND_BEGIN_ARG_INFO(arginfo_vips_cache_get_size, 0)
2234 ZEND_END_ARG_INFO()
2235 
2236 ZEND_BEGIN_ARG_INFO(arginfo_vips_concurrency_get, 0)
2237 ZEND_END_ARG_INFO()
2238 
2239 ZEND_BEGIN_ARG_INFO(arginfo_vips_version, 0)
2240 ZEND_END_ARG_INFO()
2241 /* {{{ vips_functions[]
2242  *
2243  * Every user visible function must have an entry in vips_functions[].
2244  */
2245 const zend_function_entry vips_functions[] = {
2246 	PHP_FE(vips_image_new_from_file, arginfo_vips_image_new_from_file)
2247 	PHP_FE(vips_image_new_from_buffer, arginfo_vips_image_new_from_buffer)
2248 	PHP_FE(vips_image_new_from_array, arginfo_vips_image_new_from_array)
2249 	PHP_FE(vips_image_write_to_file, arginfo_vips_image_write_to_file)
2250 	PHP_FE(vips_image_write_to_buffer, arginfo_vips_image_write_to_buffer)
2251 	PHP_FE(vips_image_copy_memory, arginfo_vips_image_copy_memory)
2252 	PHP_FE(vips_image_new_from_memory, arginfo_vips_image_new_from_memory)
2253 	PHP_FE(vips_image_write_to_memory, arginfo_vips_image_write_to_memory)
2254 	PHP_FE(vips_image_write_to_array, arginfo_vips_image_write_to_array)
2255 	PHP_FE(vips_foreign_find_load, arginfo_vips_foreign_find_load)
2256 	PHP_FE(vips_foreign_find_load_buffer, arginfo_vips_foreign_find_load_buffer)
2257 	PHP_FE(vips_interpolate_new, arginfo_vips_interpolate_new)
2258 
2259 	PHP_FE(vips_call, arginfo_vips_call)
2260 	PHP_FE(vips_image_get, arginfo_vips_image_get)
2261 	PHP_FE(vips_image_get_typeof, arginfo_vips_image_get_typeof)
2262 	PHP_FE(vips_image_set, arginfo_vips_image_set)
2263 	PHP_FE(vips_image_remove, arginfo_vips_image_remove)
2264 	PHP_FE(vips_error_buffer, arginfo_vips_error_buffer)
2265 	PHP_FE(vips_cache_set_max, arginfo_vips_cache_set_max)
2266 	PHP_FE(vips_cache_set_max_mem, arginfo_vips_cache_set_max_mem)
2267 	PHP_FE(vips_cache_set_max_files, arginfo_vips_cache_set_max_files)
2268 	PHP_FE(vips_concurrency_set, arginfo_vips_concurrency_set)
2269     PHP_FE(vips_cache_get_max, arginfo_vips_cache_get_max)
2270 	PHP_FE(vips_cache_get_max_mem, arginfo_vips_cache_get_max_mem)
2271 	PHP_FE(vips_cache_get_max_files, arginfo_vips_cache_get_max_files)
2272     PHP_FE(vips_cache_get_size, arginfo_vips_cache_get_size)
2273     PHP_FE(vips_concurrency_get, arginfo_vips_concurrency_get)
2274 	PHP_FE(vips_version, arginfo_vips_version)
2275 
2276 	PHP_FE_END	/* Must be the last line in vips_functions[] */
2277 };
2278 /* }}} */
2279 
2280 /* {{{ vips_module_entry
2281  */
2282 zend_module_entry vips_module_entry = {
2283 	STANDARD_MODULE_HEADER,
2284 	"vips",
2285 	vips_functions,
2286 	PHP_MINIT(vips),
2287 	PHP_MSHUTDOWN(vips),
2288 	PHP_RINIT(vips),		/* Replace with NULL if there's nothing to do at request start */
2289 	PHP_RSHUTDOWN(vips),	/* Replace with NULL if there's nothing to do at request end */
2290 	PHP_MINFO(vips),
2291 	PHP_VIPS_VERSION,
2292 	STANDARD_MODULE_PROPERTIES
2293 };
2294 /* }}} */
2295 
2296 #ifdef COMPILE_DL_VIPS
2297 #ifdef ZTS
2298 ZEND_TSRMLS_CACHE_DEFINE()
2299 #endif
2300 ZEND_GET_MODULE(vips)
2301 #endif
2302 
2303 /*
2304  * Local variables:
2305  * tab-width: 4
2306  * c-basic-offset: 4
2307  * End:
2308  * vim600: noet sw=4 ts=4 fdm=marker
2309  * vim<600: noet sw=4 ts=4
2310  */
2311