Skip to content

Commit 3e0d3b3

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 3706484 commit 3e0d3b3

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
@@ -318,6 +318,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */
318318
BASIC_MINIT_SUBMODULE(standard_filters)
319319
BASIC_MINIT_SUBMODULE(user_filters)
320320
BASIC_MINIT_SUBMODULE(password)
321+
BASIC_MINIT_SUBMODULE(image)
321322

322323
#ifdef ZTS
323324
BASIC_MINIT_SUBMODULE(localeconv)
@@ -393,6 +394,7 @@ PHP_MSHUTDOWN_FUNCTION(basic) /* {{{ */
393394
#endif
394395
BASIC_MSHUTDOWN_SUBMODULE(crypt)
395396
BASIC_MSHUTDOWN_SUBMODULE(password)
397+
BASIC_MSHUTDOWN_SUBMODULE(image)
396398

397399
return SUCCESS;
398400
}

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

@@ -1205,7 +1208,7 @@ bool php_is_image_avif(php_stream* stream) {
12051208

12061209
/* {{{ php_image_type_to_mime_type
12071210
* Convert internal image_type to mime type */
1208-
PHPAPI char * php_image_type_to_mime_type(int image_type)
1211+
PHPAPI const char * php_image_type_to_mime_type(int image_type)
12091212
{
12101213
switch( image_type) {
12111214
case IMAGE_FILETYPE_GIF:
@@ -1240,7 +1243,13 @@ PHPAPI char * php_image_type_to_mime_type(int image_type)
12401243
return "image/webp";
12411244
case IMAGE_FILETYPE_AVIF:
12421245
return "image/avif";
1243-
default:
1246+
default: {
1247+
const struct php_image_handler *handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) image_type);
1248+
if (handler) {
1249+
return handler->mime_type;
1250+
}
1251+
ZEND_FALLTHROUGH;
1252+
}
12441253
case IMAGE_FILETYPE_UNKNOWN:
12451254
return "application/octet-stream"; /* suppose binary format */
12461255
}
@@ -1256,7 +1265,7 @@ PHP_FUNCTION(image_type_to_mime_type)
12561265
Z_PARAM_LONG(p_image_type)
12571266
ZEND_PARSE_PARAMETERS_END();
12581267

1259-
ZVAL_STRING(return_value, (char*)php_image_type_to_mime_type(p_image_type));
1268+
ZVAL_STRING(return_value, php_image_type_to_mime_type(p_image_type));
12601269
}
12611270
/* }}} */
12621271

@@ -1325,6 +1334,13 @@ PHP_FUNCTION(image_type_to_extension)
13251334
case IMAGE_FILETYPE_AVIF:
13261335
imgext = ".avif";
13271336
break;
1337+
default: {
1338+
const struct php_image_handler *handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) image_type);
1339+
if (handler) {
1340+
imgext = handler->extension;
1341+
}
1342+
break;
1343+
}
13281344
}
13291345

13301346
if (imgext) {
@@ -1427,6 +1443,15 @@ PHPAPI int php_getimagetype(php_stream *stream, const char *input, char *filetyp
14271443
return IMAGE_FILETYPE_XBM;
14281444
}
14291445

1446+
zend_ulong h;
1447+
zval *zv;
1448+
ZEND_HASH_FOREACH_NUM_KEY_VAL(&php_image_handlers, h, zv) {
1449+
const struct php_image_handler *handler = Z_PTR_P(zv);
1450+
if (handler->identify(stream) == SUCCESS) {
1451+
return (int) h;
1452+
}
1453+
} ZEND_HASH_FOREACH_END();
1454+
14301455
return IMAGE_FILETYPE_UNKNOWN;
14311456
}
14321457
/* }}} */
@@ -1435,6 +1460,7 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
14351460
{
14361461
int itype = 0;
14371462
struct php_gfxinfo *result = NULL;
1463+
const char *mime_type = NULL;
14381464

14391465
if (!stream) {
14401466
RETURN_FALSE;
@@ -1459,6 +1485,7 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
14591485
result = php_handle_swf(stream);
14601486
break;
14611487
case IMAGE_FILETYPE_SWC:
1488+
/* TODO: with the new php_image_register_handler() APIs, this restriction could be solved */
14621489
#if defined(HAVE_ZLIB) && !defined(COMPILE_DL_ZLIB)
14631490
result = php_handle_swc(stream);
14641491
#else
@@ -1501,27 +1528,44 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
15011528
case IMAGE_FILETYPE_AVIF:
15021529
result = php_handle_avif(stream);
15031530
break;
1504-
default:
1531+
default: {
1532+
struct php_image_handler* handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) itype);
1533+
if (handler) {
1534+
result = handler->get_info(stream);
1535+
mime_type = handler->mime_type;
1536+
break;
1537+
}
1538+
ZEND_FALLTHROUGH;
1539+
}
15051540
case IMAGE_FILETYPE_UNKNOWN:
15061541
break;
15071542
}
15081543

15091544
if (result) {
1510-
char temp[MAX_LENGTH_OF_LONG * 2 + sizeof("width=\"\" height=\"\"")];
15111545
array_init(return_value);
1512-
add_index_long(return_value, 0, result->width);
1513-
add_index_long(return_value, 1, result->height);
1546+
if (result->width_str) {
1547+
add_index_str(return_value, 0, result->width_str);
1548+
add_index_str(return_value, 1, result->height_str);
1549+
} else {
1550+
add_index_long(return_value, 0, result->width);
1551+
add_index_long(return_value, 1, result->height);
1552+
}
15141553
add_index_long(return_value, 2, itype);
1515-
snprintf(temp, sizeof(temp), "width=\"%d\" height=\"%d\"", result->width, result->height);
1516-
add_index_string(return_value, 3, temp);
1554+
if (result->width_str) {
1555+
add_index_str(return_value, 3, zend_strpprintf_unchecked(0, "width=\"%S\" height=\"%S\"", result->width_str, result->height_str));
1556+
} else {
1557+
char temp[MAX_LENGTH_OF_LONG * 2 + sizeof("width=\"\" height=\"\"")];
1558+
snprintf(temp, sizeof(temp), "width=\"%d\" height=\"%d\"", result->width, result->height);
1559+
add_index_string(return_value, 3, temp);
1560+
}
15171561

15181562
if (result->bits != 0) {
15191563
add_assoc_long(return_value, "bits", result->bits);
15201564
}
15211565
if (result->channels != 0) {
15221566
add_assoc_long(return_value, "channels", result->channels);
15231567
}
1524-
add_assoc_string(return_value, "mime", (char*)php_image_type_to_mime_type(itype));
1568+
add_assoc_string(return_value, "mime", mime_type ? mime_type : php_image_type_to_mime_type(itype));
15251569
efree(result);
15261570
} else {
15271571
RETURN_FALSE;
@@ -1584,3 +1628,36 @@ PHP_FUNCTION(getimagesizefromstring)
15841628
php_getimagesize_from_any(INTERNAL_FUNCTION_PARAM_PASSTHRU, FROM_DATA);
15851629
}
15861630
/* }}} */
1631+
1632+
PHP_MINIT_FUNCTION(image)
1633+
{
1634+
zend_hash_init(&php_image_handlers, 4, NULL, NULL, true);
1635+
return SUCCESS;
1636+
}
1637+
1638+
PHP_MSHUTDOWN_FUNCTION(image)
1639+
{
1640+
#ifdef ZTS
1641+
if (!tsrm_is_main_thread()) {
1642+
return SUCCESS;
1643+
}
1644+
#endif
1645+
zend_hash_destroy(&php_image_handlers);
1646+
return SUCCESS;
1647+
}
1648+
1649+
extern zend_module_entry basic_functions_module;
1650+
1651+
int php_image_register_handler(const struct php_image_handler *handler)
1652+
{
1653+
zend_hash_index_add_ptr(&php_image_handlers, (zend_ulong) php_image_handler_next_id, (void *) handler);
1654+
zend_register_long_constant(handler->const_name, strlen(handler->const_name), php_image_handler_next_id, CONST_PERSISTENT, basic_functions_module.module_number);
1655+
Z_LVAL_P(zend_get_constant_str(ZEND_STRL("IMAGETYPE_COUNT")))++;
1656+
return php_image_handler_next_id++;
1657+
}
1658+
1659+
zend_result php_image_unregister_handler(int image_type)
1660+
{
1661+
ZEND_ASSERT(image_type >= IMAGE_FILETYPE_FIXED_COUNT);
1662+
return zend_hash_index_del(&php_image_handlers, (zend_ulong) image_type);
1663+
}

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)