Skip to content

Commit 08b41c9

Browse files
committed
Add API to register custom image handlers
This is modelled similarly to the password registry API. We have an array to which new handlers can be added, and when a built-in handler cannot handle the image, we try the handlers in the array. The standard module is in control of registering a new constant for the image file type so that no clashes can occur. It also updates the image file type count constant. As such, the registration may only happen during module startup.
1 parent 7a50ec4 commit 08b41c9

File tree

6 files changed

+147
-44
lines changed

6 files changed

+147
-44
lines changed

ext/standard/basic_functions.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */
303303
BASIC_MINIT_SUBMODULE(standard_filters)
304304
BASIC_MINIT_SUBMODULE(user_filters)
305305
BASIC_MINIT_SUBMODULE(password)
306+
BASIC_MINIT_SUBMODULE(image)
306307

307308
#ifdef ZTS
308309
BASIC_MINIT_SUBMODULE(localeconv)
@@ -376,6 +377,7 @@ PHP_MSHUTDOWN_FUNCTION(basic) /* {{{ */
376377
#endif
377378
BASIC_MSHUTDOWN_SUBMODULE(crypt)
378379
BASIC_MSHUTDOWN_SUBMODULE(password)
380+
BASIC_MSHUTDOWN_SUBMODULE(image)
379381

380382
return SUCCESS;
381383
}

ext/standard/basic_functions.stub.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@
658658
const IMAGETYPE_UNKNOWN = UNKNOWN;
659659
/**
660660
* @var int
661-
* @cvalue IMAGE_FILETYPE_COUNT
661+
* @cvalue IMAGE_FILETYPE_FIXED_COUNT
662662
*/
663663
const IMAGETYPE_COUNT = UNKNOWN;
664664

@@ -3032,13 +3032,11 @@ function request_parse_body(?array $options = null): array {}
30323032
/* image.c */
30333033

30343034
/**
3035-
* @compile-time-eval
30363035
* @refcount 1
30373036
*/
30383037
function image_type_to_mime_type(int $image_type): string {}
30393038

30403039
/**
3041-
* @compile-time-eval
30423040
* @refcount 1
30433041
*/
30443042
function image_type_to_extension(int $image_type, bool $include_dot = true): string|false {}

ext/standard/basic_functions_arginfo.h

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/standard/image.c

Lines changed: 87 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ PHPAPI const char php_sig_mif1[4] = {'m', 'i', 'f', '1'};
5656
PHPAPI const char php_sig_heic[4] = {'h', 'e', 'i', 'c'};
5757
PHPAPI const char php_sig_heix[4] = {'h', 'e', 'i', 'x'};
5858

59+
static zend_array php_image_handlers;
60+
static int php_image_handler_next_id = IMAGE_FILETYPE_FIXED_COUNT;
61+
5962
/* REMEMBER TO ADD MIME-TYPE TO FUNCTION php_image_type_to_mime_type */
6063
/* PCX must check first 64bytes and byte 0=0x0a and byte2 < 0x06 */
6164

@@ -1214,7 +1217,7 @@ bool php_is_image_avif(php_stream* stream) {
12141217

12151218
/* {{{ php_image_type_to_mime_type
12161219
* Convert internal image_type to mime type */
1217-
PHPAPI char * php_image_type_to_mime_type(int image_type)
1220+
PHPAPI const char * php_image_type_to_mime_type(int image_type)
12181221
{
12191222
switch( image_type) {
12201223
case IMAGE_FILETYPE_GIF:
@@ -1251,7 +1254,13 @@ PHPAPI char * php_image_type_to_mime_type(int image_type)
12511254
return "image/avif";
12521255
case IMAGE_FILETYPE_HEIF:
12531256
return "image/heif";
1254-
default:
1257+
default: {
1258+
const struct php_image_handler *handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) image_type);
1259+
if (handler) {
1260+
return handler->mime_type;
1261+
}
1262+
ZEND_FALLTHROUGH;
1263+
}
12551264
case IMAGE_FILETYPE_UNKNOWN:
12561265
return "application/octet-stream"; /* suppose binary format */
12571266
}
@@ -1267,7 +1276,7 @@ PHP_FUNCTION(image_type_to_mime_type)
12671276
Z_PARAM_LONG(p_image_type)
12681277
ZEND_PARSE_PARAMETERS_END();
12691278

1270-
ZVAL_STRING(return_value, (char*)php_image_type_to_mime_type(p_image_type));
1279+
ZVAL_STRING(return_value, php_image_type_to_mime_type(p_image_type));
12711280
}
12721281
/* }}} */
12731282

@@ -1339,7 +1348,13 @@ PHP_FUNCTION(image_type_to_extension)
13391348
case IMAGE_FILETYPE_HEIF:
13401349
imgext = ".heif";
13411350
break;
1342-
break;
1351+
default: {
1352+
const struct php_image_handler *handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) image_type);
1353+
if (handler) {
1354+
imgext = handler->extension;
1355+
}
1356+
break;
1357+
}
13431358
}
13441359

13451360
if (imgext) {
@@ -1447,6 +1462,15 @@ PHPAPI int php_getimagetype(php_stream *stream, const char *input, char *filetyp
14471462
return IMAGE_FILETYPE_XBM;
14481463
}
14491464

1465+
zend_ulong h;
1466+
zval *zv;
1467+
ZEND_HASH_FOREACH_NUM_KEY_VAL(&php_image_handlers, h, zv) {
1468+
const struct php_image_handler *handler = Z_PTR_P(zv);
1469+
if (handler->identify(stream) == SUCCESS) {
1470+
return (int) h;
1471+
}
1472+
} ZEND_HASH_FOREACH_END();
1473+
14501474
return IMAGE_FILETYPE_UNKNOWN;
14511475
}
14521476
/* }}} */
@@ -1455,6 +1479,7 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
14551479
{
14561480
int itype = 0;
14571481
struct php_gfxinfo *result = NULL;
1482+
const char *mime_type = NULL;
14581483

14591484
if (!stream) {
14601485
RETURN_FALSE;
@@ -1479,6 +1504,7 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
14791504
result = php_handle_swf(stream);
14801505
break;
14811506
case IMAGE_FILETYPE_SWC:
1507+
/* TODO: with the new php_image_register_handler() APIs, this restriction could be solved */
14821508
#if defined(HAVE_ZLIB) && !defined(COMPILE_DL_ZLIB)
14831509
result = php_handle_swc(stream);
14841510
#else
@@ -1526,27 +1552,44 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
15261552
result = php_handle_avif(stream);
15271553
}
15281554
break;
1529-
default:
1555+
default: {
1556+
struct php_image_handler* handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) itype);
1557+
if (handler) {
1558+
result = handler->get_info(stream);
1559+
mime_type = handler->mime_type;
1560+
break;
1561+
}
1562+
ZEND_FALLTHROUGH;
1563+
}
15301564
case IMAGE_FILETYPE_UNKNOWN:
15311565
break;
15321566
}
15331567

15341568
if (result) {
1535-
char temp[MAX_LENGTH_OF_LONG * 2 + sizeof("width=\"\" height=\"\"")];
15361569
array_init(return_value);
1537-
add_index_long(return_value, 0, result->width);
1538-
add_index_long(return_value, 1, result->height);
1570+
if (result->width_str) {
1571+
add_index_str(return_value, 0, result->width_str);
1572+
add_index_str(return_value, 1, result->height_str);
1573+
} else {
1574+
add_index_long(return_value, 0, result->width);
1575+
add_index_long(return_value, 1, result->height);
1576+
}
15391577
add_index_long(return_value, 2, itype);
1540-
snprintf(temp, sizeof(temp), "width=\"%d\" height=\"%d\"", result->width, result->height);
1541-
add_index_string(return_value, 3, temp);
1578+
if (result->width_str) {
1579+
add_index_str(return_value, 3, zend_strpprintf_unchecked(0, "width=\"%S\" height=\"%S\"", result->width_str, result->height_str));
1580+
} else {
1581+
char temp[MAX_LENGTH_OF_LONG * 2 + sizeof("width=\"\" height=\"\"")];
1582+
snprintf(temp, sizeof(temp), "width=\"%d\" height=\"%d\"", result->width, result->height);
1583+
add_index_string(return_value, 3, temp);
1584+
}
15421585

15431586
if (result->bits != 0) {
15441587
add_assoc_long(return_value, "bits", result->bits);
15451588
}
15461589
if (result->channels != 0) {
15471590
add_assoc_long(return_value, "channels", result->channels);
15481591
}
1549-
add_assoc_string(return_value, "mime", (char*)php_image_type_to_mime_type(itype));
1592+
add_assoc_string(return_value, "mime", mime_type ? mime_type : php_image_type_to_mime_type(itype));
15501593
efree(result);
15511594
} else {
15521595
RETURN_FALSE;
@@ -1609,3 +1652,36 @@ PHP_FUNCTION(getimagesizefromstring)
16091652
php_getimagesize_from_any(INTERNAL_FUNCTION_PARAM_PASSTHRU, FROM_DATA);
16101653
}
16111654
/* }}} */
1655+
1656+
PHP_MINIT_FUNCTION(image)
1657+
{
1658+
zend_hash_init(&php_image_handlers, 4, NULL, NULL, true);
1659+
return SUCCESS;
1660+
}
1661+
1662+
PHP_MSHUTDOWN_FUNCTION(image)
1663+
{
1664+
#ifdef ZTS
1665+
if (!tsrm_is_main_thread()) {
1666+
return SUCCESS;
1667+
}
1668+
#endif
1669+
zend_hash_destroy(&php_image_handlers);
1670+
return SUCCESS;
1671+
}
1672+
1673+
extern zend_module_entry basic_functions_module;
1674+
1675+
int php_image_register_handler(const struct php_image_handler *handler)
1676+
{
1677+
zend_hash_index_add_ptr(&php_image_handlers, (zend_ulong) php_image_handler_next_id, (void *) handler);
1678+
zend_register_long_constant(handler->const_name, strlen(handler->const_name), php_image_handler_next_id, CONST_PERSISTENT, basic_functions_module.module_number);
1679+
Z_LVAL_P(zend_get_constant_str(ZEND_STRL("IMAGETYPE_COUNT")))++;
1680+
return php_image_handler_next_id++;
1681+
}
1682+
1683+
zend_result php_image_unregister_handler(int image_type)
1684+
{
1685+
ZEND_ASSERT(image_type >= IMAGE_FILETYPE_FIXED_COUNT);
1686+
return zend_hash_index_del(&php_image_handlers, (zend_ulong) image_type);
1687+
}

ext/standard/php_image.h

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
#ifndef PHP_IMAGE_H
1919
#define PHP_IMAGE_H
2020

21+
PHP_MINIT_FUNCTION(image);
22+
PHP_MSHUTDOWN_FUNCTION(image);
23+
2124
/* {{{ enum image_filetype
2225
This enum is used to have ext/standard/image.c and ext/exif/exif.c use
2326
the same constants for file types.
@@ -46,13 +49,13 @@ typedef enum
4649
IMAGE_FILETYPE_AVIF,
4750
IMAGE_FILETYPE_HEIF,
4851
/* WHEN EXTENDING: PLEASE ALSO REGISTER IN basic_function.stub.php */
49-
IMAGE_FILETYPE_COUNT
52+
IMAGE_FILETYPE_FIXED_COUNT
5053
} image_filetype;
5154
/* }}} */
5255

5356
PHPAPI int php_getimagetype(php_stream *stream, const char *input, char *filetype);
5457

55-
PHPAPI char * php_image_type_to_mime_type(int image_type);
58+
PHPAPI const char * php_image_type_to_mime_type(int image_type);
5659

5760
PHPAPI bool php_is_image_avif(php_stream *stream);
5861

@@ -66,4 +69,22 @@ struct php_gfxinfo {
6669
unsigned int channels;
6770
};
6871

72+
typedef zend_result (*php_image_identify)(php_stream *stream);
73+
typedef struct php_gfxinfo *(*php_image_get_info)(php_stream *stream);
74+
75+
struct php_image_handler {
76+
const char *mime_type;
77+
const char *extension;
78+
const char *const_name;
79+
php_image_identify identify;
80+
php_image_get_info get_info;
81+
};
82+
83+
#define PHP_IMAGE_CONST_NAME(suffix) ("IMAGETYPE_" suffix)
84+
85+
/* This should only be called on module init */
86+
PHPAPI int php_image_register_handler(const struct php_image_handler *handler);
87+
/* This should only be called on module shutdown */
88+
PHPAPI zend_result php_image_unregister_handler(int image_type);
89+
6990
#endif /* PHP_IMAGE_H */

0 commit comments

Comments
 (0)