Skip to content

Commit 8db883c

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 05bb051 commit 8db883c

File tree

6 files changed

+145
-40
lines changed

6 files changed

+145
-40
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 & 1 deletion
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

ext/standard/basic_functions_arginfo.h

Lines changed: 2 additions & 2 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 */
Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,87 @@
11
--TEST--
2-
image_type_to_mime_type() (passinf equivalent integer values)
2+
image_type_to_mime_type() (passing equivalent integer values)
33
--CREDITS--
44
Sanjay Mantoor <[email protected]>
55
--FILE--
66
<?php
77
echo "*** Testing image_type_to_mime_type() : usage variations ***\n";
88

9-
for($imagetype = 0; $imagetype <= IMAGETYPE_COUNT; ++$imagetype) {
9+
for($imagetype = 0; $imagetype <= 19; ++$imagetype) {
1010
echo "\n-- Iteration $imagetype --\n";
1111
var_dump(image_type_to_mime_type($imagetype));
1212
}
13+
14+
echo "\n-- Iteration 999 --\n";
15+
var_dump(image_type_to_mime_type(999));
1316
?>
14-
--EXPECTREGEX--
15-
\*\*\* Testing image_type_to_mime_type\(\) : usage variations \*\*\*
17+
--EXPECT--
18+
*** Testing image_type_to_mime_type() : usage variations ***
1619

1720
-- Iteration 0 --
18-
string\(24\) "application\/octet-stream"
21+
string(24) "application/octet-stream"
1922

2023
-- Iteration 1 --
21-
string\(9\) "image\/gif"
24+
string(9) "image/gif"
2225

2326
-- Iteration 2 --
24-
string\(10\) "image\/jpeg"
27+
string(10) "image/jpeg"
2528

2629
-- Iteration 3 --
27-
string\(9\) "image\/png"
30+
string(9) "image/png"
2831

2932
-- Iteration 4 --
30-
string\(29\) "application\/x-shockwave-flash"
33+
string(29) "application/x-shockwave-flash"
3134

3235
-- Iteration 5 --
33-
string\(9\) "image\/psd"
36+
string(9) "image/psd"
3437

3538
-- Iteration 6 --
36-
string\(9\) "image\/bmp"
39+
string(9) "image/bmp"
3740

3841
-- Iteration 7 --
39-
string\(10\) "image\/tiff"
42+
string(10) "image/tiff"
4043

4144
-- Iteration 8 --
42-
string\(10\) "image\/tiff"
45+
string(10) "image/tiff"
4346

4447
-- Iteration 9 --
45-
string\(24\) "application\/octet-stream"
48+
string(24) "application/octet-stream"
4649

4750
-- Iteration 10 --
48-
string\(9\) "image\/jp2"
51+
string(9) "image/jp2"
4952

5053
-- Iteration 11 --
51-
string\(24\) "application\/octet-stream"
54+
string(24) "application/octet-stream"
5255

5356
-- Iteration 12 --
54-
string\(24\) "application\/octet-stream"
57+
string(24) "application/octet-stream"
5558

5659
-- Iteration 13 --
57-
string\(2[49]\) "application\/(x-shockwave-flash|octet-stream)"
60+
string(29) "application/x-shockwave-flash"
5861

5962
-- Iteration 14 --
60-
string\(9\) "image\/iff"
63+
string(9) "image/iff"
6164

6265
-- Iteration 15 --
63-
string\(18\) "image\/vnd.wap.wbmp"
66+
string(18) "image/vnd.wap.wbmp"
6467

6568
-- Iteration 16 --
66-
string\(9\) "image\/xbm"
69+
string(9) "image/xbm"
6770

6871
-- Iteration 17 --
69-
string\(24\) "image\/vnd.microsoft.icon"
72+
string(24) "image/vnd.microsoft.icon"
7073

7174
-- Iteration 18 --
72-
string\(10\) "image\/webp"
75+
string(10) "image/webp"
7376

7477
-- Iteration 19 --
75-
string\(10\) "image\/avif"
78+
string(10) "image/avif"
7679

7780
-- Iteration 20 --
7881
string\(10\) "image\/heif"
7982

8083
-- Iteration 21 --
8184
string\(24\) "application\/octet-stream"
85+
86+
-- Iteration 999 --
87+
string(24) "application/octet-stream"

0 commit comments

Comments
 (0)