Skip to content

Commit 39a8071

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 49efc93 commit 39a8071

File tree

6 files changed

+146
-45
lines changed

6 files changed

+146
-45
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
@@ -653,7 +653,7 @@
653653
const IMAGETYPE_UNKNOWN = UNKNOWN;
654654
/**
655655
* @var int
656-
* @cvalue IMAGE_FILETYPE_COUNT
656+
* @cvalue IMAGE_FILETYPE_FIXED_COUNT
657657
*/
658658
const IMAGETYPE_COUNT = UNKNOWN;
659659

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

30293029
/**
3030-
* @compile-time-eval
30313030
* @refcount 1
30323031
*/
30333032
function image_type_to_mime_type(int $image_type): string {}
30343033

30353034
/**
3036-
* @compile-time-eval
30373035
* @refcount 1
30383036
*/
30393037
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 & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ PHPAPI const char php_sig_ico[4] = {(char)0x00, (char)0x00, (char)0x01, (char)0x
5252
PHPAPI const char php_sig_riff[4] = {'R', 'I', 'F', 'F'};
5353
PHPAPI const char php_sig_webp[4] = {'W', 'E', 'B', 'P'};
5454

55+
static zend_array php_image_handlers;
56+
static int php_image_handler_next_id = IMAGE_FILETYPE_FIXED_COUNT;
57+
5558
/* REMEMBER TO ADD MIME-TYPE TO FUNCTION php_image_type_to_mime_type */
5659
/* PCX must check first 64bytes and byte 0=0x0a and byte2 < 0x06 */
5760

@@ -1210,7 +1213,7 @@ bool php_is_image_avif(php_stream* stream) {
12101213

12111214
/* {{{ php_image_type_to_mime_type
12121215
* Convert internal image_type to mime type */
1213-
PHPAPI char * php_image_type_to_mime_type(int image_type)
1216+
PHPAPI const char * php_image_type_to_mime_type(int image_type)
12141217
{
12151218
switch( image_type) {
12161219
case IMAGE_FILETYPE_GIF:
@@ -1245,7 +1248,13 @@ PHPAPI char * php_image_type_to_mime_type(int image_type)
12451248
return "image/webp";
12461249
case IMAGE_FILETYPE_AVIF:
12471250
return "image/avif";
1248-
default:
1251+
default: {
1252+
const struct php_image_handler *handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) image_type);
1253+
if (handler) {
1254+
return handler->mime_type;
1255+
}
1256+
ZEND_FALLTHROUGH;
1257+
}
12491258
case IMAGE_FILETYPE_UNKNOWN:
12501259
return "application/octet-stream"; /* suppose binary format */
12511260
}
@@ -1261,7 +1270,7 @@ PHP_FUNCTION(image_type_to_mime_type)
12611270
Z_PARAM_LONG(p_image_type)
12621271
ZEND_PARSE_PARAMETERS_END();
12631272

1264-
ZVAL_STRING(return_value, (char*)php_image_type_to_mime_type(p_image_type));
1273+
ZVAL_STRING(return_value, php_image_type_to_mime_type(p_image_type));
12651274
}
12661275
/* }}} */
12671276

@@ -1330,6 +1339,13 @@ PHP_FUNCTION(image_type_to_extension)
13301339
case IMAGE_FILETYPE_AVIF:
13311340
imgext = ".avif";
13321341
break;
1342+
default: {
1343+
const struct php_image_handler *handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) image_type);
1344+
if (handler) {
1345+
imgext = handler->extension;
1346+
}
1347+
break;
1348+
}
13331349
}
13341350

13351351
if (imgext) {
@@ -1432,6 +1448,15 @@ PHPAPI int php_getimagetype(php_stream *stream, const char *input, char *filetyp
14321448
return IMAGE_FILETYPE_XBM;
14331449
}
14341450

1451+
zend_ulong h;
1452+
zval *zv;
1453+
ZEND_HASH_FOREACH_NUM_KEY_VAL(&php_image_handlers, h, zv) {
1454+
const struct php_image_handler *handler = Z_PTR_P(zv);
1455+
if (handler->identify(stream) == SUCCESS) {
1456+
return (int) h;
1457+
}
1458+
} ZEND_HASH_FOREACH_END();
1459+
14351460
return IMAGE_FILETYPE_UNKNOWN;
14361461
}
14371462
/* }}} */
@@ -1440,6 +1465,7 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
14401465
{
14411466
int itype = 0;
14421467
struct php_gfxinfo *result = NULL;
1468+
const char *mime_type = NULL;
14431469

14441470
if (!stream) {
14451471
RETURN_FALSE;
@@ -1464,6 +1490,7 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
14641490
result = php_handle_swf(stream);
14651491
break;
14661492
case IMAGE_FILETYPE_SWC:
1493+
/* TODO: with the new php_image_register_handler() APIs, this restriction could be solved */
14671494
#if defined(HAVE_ZLIB) && !defined(COMPILE_DL_ZLIB)
14681495
result = php_handle_swc(stream);
14691496
#else
@@ -1506,27 +1533,44 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
15061533
case IMAGE_FILETYPE_AVIF:
15071534
result = php_handle_avif(stream);
15081535
break;
1509-
default:
1536+
default: {
1537+
struct php_image_handler* handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) itype);
1538+
if (handler) {
1539+
result = handler->get_info(stream);
1540+
mime_type = handler->mime_type;
1541+
break;
1542+
}
1543+
ZEND_FALLTHROUGH;
1544+
}
15101545
case IMAGE_FILETYPE_UNKNOWN:
15111546
break;
15121547
}
15131548

15141549
if (result) {
1515-
char temp[MAX_LENGTH_OF_LONG * 2 + sizeof("width=\"\" height=\"\"")];
15161550
array_init(return_value);
1517-
add_index_long(return_value, 0, result->width);
1518-
add_index_long(return_value, 1, result->height);
1551+
if (result->width_str) {
1552+
add_index_str(return_value, 0, result->width_str);
1553+
add_index_str(return_value, 1, result->height_str);
1554+
} else {
1555+
add_index_long(return_value, 0, result->width);
1556+
add_index_long(return_value, 1, result->height);
1557+
}
15191558
add_index_long(return_value, 2, itype);
1520-
snprintf(temp, sizeof(temp), "width=\"%d\" height=\"%d\"", result->width, result->height);
1521-
add_index_string(return_value, 3, temp);
1559+
if (result->width_str) {
1560+
add_index_str(return_value, 3, zend_strpprintf_unchecked(0, "width=\"%S\" height=\"%S\"", result->width_str, result->height_str));
1561+
} else {
1562+
char temp[MAX_LENGTH_OF_LONG * 2 + sizeof("width=\"\" height=\"\"")];
1563+
snprintf(temp, sizeof(temp), "width=\"%d\" height=\"%d\"", result->width, result->height);
1564+
add_index_string(return_value, 3, temp);
1565+
}
15221566

15231567
if (result->bits != 0) {
15241568
add_assoc_long(return_value, "bits", result->bits);
15251569
}
15261570
if (result->channels != 0) {
15271571
add_assoc_long(return_value, "channels", result->channels);
15281572
}
1529-
add_assoc_string(return_value, "mime", (char*)php_image_type_to_mime_type(itype));
1573+
add_assoc_string(return_value, "mime", mime_type ? mime_type : php_image_type_to_mime_type(itype));
15301574
efree(result);
15311575
} else {
15321576
RETURN_FALSE;
@@ -1589,3 +1633,36 @@ PHP_FUNCTION(getimagesizefromstring)
15891633
php_getimagesize_from_any(INTERNAL_FUNCTION_PARAM_PASSTHRU, FROM_DATA);
15901634
}
15911635
/* }}} */
1636+
1637+
PHP_MINIT_FUNCTION(image)
1638+
{
1639+
zend_hash_init(&php_image_handlers, 4, NULL, NULL, true);
1640+
return SUCCESS;
1641+
}
1642+
1643+
PHP_MSHUTDOWN_FUNCTION(image)
1644+
{
1645+
#ifdef ZTS
1646+
if (!tsrm_is_main_thread()) {
1647+
return SUCCESS;
1648+
}
1649+
#endif
1650+
zend_hash_destroy(&php_image_handlers);
1651+
return SUCCESS;
1652+
}
1653+
1654+
extern zend_module_entry basic_functions_module;
1655+
1656+
int php_image_register_handler(const struct php_image_handler *handler)
1657+
{
1658+
zend_hash_index_add_ptr(&php_image_handlers, (zend_ulong) php_image_handler_next_id, (void *) handler);
1659+
zend_register_long_constant(handler->const_name, strlen(handler->const_name), php_image_handler_next_id, CONST_PERSISTENT, basic_functions_module.module_number);
1660+
Z_LVAL_P(zend_get_constant_str(ZEND_STRL("IMAGETYPE_COUNT")))++;
1661+
return php_image_handler_next_id++;
1662+
}
1663+
1664+
zend_result php_image_unregister_handler(int image_type)
1665+
{
1666+
ZEND_ASSERT(image_type >= IMAGE_FILETYPE_FIXED_COUNT);
1667+
return zend_hash_index_del(&php_image_handlers, (zend_ulong) image_type);
1668+
}

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.
@@ -45,13 +48,13 @@ typedef enum
4548
IMAGE_FILETYPE_WEBP,
4649
IMAGE_FILETYPE_AVIF,
4750
/* WHEN EXTENDING: PLEASE ALSO REGISTER IN basic_function.stub.php */
48-
IMAGE_FILETYPE_COUNT
51+
IMAGE_FILETYPE_FIXED_COUNT
4952
} image_filetype;
5053
/* }}} */
5154

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

54-
PHPAPI char * php_image_type_to_mime_type(int image_type);
57+
PHPAPI const char * php_image_type_to_mime_type(int image_type);
5558

5659
PHPAPI bool php_is_image_avif(php_stream *stream);
5760

@@ -65,4 +68,22 @@ struct php_gfxinfo {
6568
unsigned int channels;
6669
};
6770

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

0 commit comments

Comments
 (0)