1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

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.
This commit is contained in:
Niels Dossche
2024-11-01 14:59:43 +01:00
parent 05bb051243
commit 8db883c540
6 changed files with 145 additions and 40 deletions

View File

@@ -303,6 +303,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */
BASIC_MINIT_SUBMODULE(standard_filters)
BASIC_MINIT_SUBMODULE(user_filters)
BASIC_MINIT_SUBMODULE(password)
BASIC_MINIT_SUBMODULE(image)
#ifdef ZTS
BASIC_MINIT_SUBMODULE(localeconv)
@@ -376,6 +377,7 @@ PHP_MSHUTDOWN_FUNCTION(basic) /* {{{ */
#endif
BASIC_MSHUTDOWN_SUBMODULE(crypt)
BASIC_MSHUTDOWN_SUBMODULE(password)
BASIC_MSHUTDOWN_SUBMODULE(image)
return SUCCESS;
}

View File

@@ -658,7 +658,7 @@ const IMAGETYPE_HEIF = UNKNOWN;
const IMAGETYPE_UNKNOWN = UNKNOWN;
/**
* @var int
* @cvalue IMAGE_FILETYPE_COUNT
* @cvalue IMAGE_FILETYPE_FIXED_COUNT
*/
const IMAGETYPE_COUNT = UNKNOWN;

View File

@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 33629477a13fc7b675fe47eeaaac0d9e15dd2e17 */
* Stub hash: dcc4e6865dac52b23534b6ef61c0d6be766af6b9 */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0)
ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0)
@@ -3647,7 +3647,7 @@ static void register_basic_functions_symbols(int module_number)
REGISTER_LONG_CONSTANT("IMAGETYPE_AVIF", IMAGE_FILETYPE_AVIF, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMAGETYPE_HEIF", IMAGE_FILETYPE_HEIF, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMAGETYPE_UNKNOWN", IMAGE_FILETYPE_UNKNOWN, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMAGETYPE_COUNT", IMAGE_FILETYPE_COUNT, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("IMAGETYPE_COUNT", IMAGE_FILETYPE_FIXED_COUNT, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("INFO_GENERAL", PHP_INFO_GENERAL, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("INFO_CREDITS", PHP_INFO_CREDITS, CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("INFO_CONFIGURATION", PHP_INFO_CONFIGURATION, CONST_PERSISTENT);

View File

@@ -56,6 +56,9 @@ PHPAPI const char php_sig_mif1[4] = {'m', 'i', 'f', '1'};
PHPAPI const char php_sig_heic[4] = {'h', 'e', 'i', 'c'};
PHPAPI const char php_sig_heix[4] = {'h', 'e', 'i', 'x'};
static zend_array php_image_handlers;
static int php_image_handler_next_id = IMAGE_FILETYPE_FIXED_COUNT;
/* REMEMBER TO ADD MIME-TYPE TO FUNCTION php_image_type_to_mime_type */
/* PCX must check first 64bytes and byte 0=0x0a and byte2 < 0x06 */
@@ -1214,7 +1217,7 @@ bool php_is_image_avif(php_stream* stream) {
/* {{{ php_image_type_to_mime_type
* Convert internal image_type to mime type */
PHPAPI char * php_image_type_to_mime_type(int image_type)
PHPAPI const char * php_image_type_to_mime_type(int image_type)
{
switch( image_type) {
case IMAGE_FILETYPE_GIF:
@@ -1251,7 +1254,13 @@ PHPAPI char * php_image_type_to_mime_type(int image_type)
return "image/avif";
case IMAGE_FILETYPE_HEIF:
return "image/heif";
default:
default: {
const struct php_image_handler *handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) image_type);
if (handler) {
return handler->mime_type;
}
ZEND_FALLTHROUGH;
}
case IMAGE_FILETYPE_UNKNOWN:
return "application/octet-stream"; /* suppose binary format */
}
@@ -1267,7 +1276,7 @@ PHP_FUNCTION(image_type_to_mime_type)
Z_PARAM_LONG(p_image_type)
ZEND_PARSE_PARAMETERS_END();
ZVAL_STRING(return_value, (char*)php_image_type_to_mime_type(p_image_type));
ZVAL_STRING(return_value, php_image_type_to_mime_type(p_image_type));
}
/* }}} */
@@ -1339,7 +1348,13 @@ PHP_FUNCTION(image_type_to_extension)
case IMAGE_FILETYPE_HEIF:
imgext = ".heif";
break;
break;
default: {
const struct php_image_handler *handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) image_type);
if (handler) {
imgext = handler->extension;
}
break;
}
}
if (imgext) {
@@ -1447,6 +1462,15 @@ PHPAPI int php_getimagetype(php_stream *stream, const char *input, char *filetyp
return IMAGE_FILETYPE_XBM;
}
zend_ulong h;
zval *zv;
ZEND_HASH_FOREACH_NUM_KEY_VAL(&php_image_handlers, h, zv) {
const struct php_image_handler *handler = Z_PTR_P(zv);
if (handler->identify(stream) == SUCCESS) {
return (int) h;
}
} ZEND_HASH_FOREACH_END();
return IMAGE_FILETYPE_UNKNOWN;
}
/* }}} */
@@ -1455,6 +1479,7 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
{
int itype = 0;
struct php_gfxinfo *result = NULL;
const char *mime_type = NULL;
if (!stream) {
RETURN_FALSE;
@@ -1479,6 +1504,7 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
result = php_handle_swf(stream);
break;
case IMAGE_FILETYPE_SWC:
/* TODO: with the new php_image_register_handler() APIs, this restriction could be solved */
#if defined(HAVE_ZLIB) && !defined(COMPILE_DL_ZLIB)
result = php_handle_swc(stream);
#else
@@ -1526,19 +1552,36 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
result = php_handle_avif(stream);
}
break;
default:
default: {
struct php_image_handler* handler = zend_hash_index_find_ptr(&php_image_handlers, (zend_ulong) itype);
if (handler) {
result = handler->get_info(stream);
mime_type = handler->mime_type;
break;
}
ZEND_FALLTHROUGH;
}
case IMAGE_FILETYPE_UNKNOWN:
break;
}
if (result) {
char temp[MAX_LENGTH_OF_LONG * 2 + sizeof("width=\"\" height=\"\"")];
array_init(return_value);
add_index_long(return_value, 0, result->width);
add_index_long(return_value, 1, result->height);
if (result->width_str) {
add_index_str(return_value, 0, result->width_str);
add_index_str(return_value, 1, result->height_str);
} else {
add_index_long(return_value, 0, result->width);
add_index_long(return_value, 1, result->height);
}
add_index_long(return_value, 2, itype);
snprintf(temp, sizeof(temp), "width=\"%d\" height=\"%d\"", result->width, result->height);
add_index_string(return_value, 3, temp);
if (result->width_str) {
add_index_str(return_value, 3, zend_strpprintf_unchecked(0, "width=\"%S\" height=\"%S\"", result->width_str, result->height_str));
} else {
char temp[MAX_LENGTH_OF_LONG * 2 + sizeof("width=\"\" height=\"\"")];
snprintf(temp, sizeof(temp), "width=\"%d\" height=\"%d\"", result->width, result->height);
add_index_string(return_value, 3, temp);
}
if (result->bits != 0) {
add_assoc_long(return_value, "bits", result->bits);
@@ -1546,7 +1589,7 @@ static void php_getimagesize_from_stream(php_stream *stream, char *input, zval *
if (result->channels != 0) {
add_assoc_long(return_value, "channels", result->channels);
}
add_assoc_string(return_value, "mime", (char*)php_image_type_to_mime_type(itype));
add_assoc_string(return_value, "mime", mime_type ? mime_type : php_image_type_to_mime_type(itype));
efree(result);
} else {
RETURN_FALSE;
@@ -1609,3 +1652,36 @@ PHP_FUNCTION(getimagesizefromstring)
php_getimagesize_from_any(INTERNAL_FUNCTION_PARAM_PASSTHRU, FROM_DATA);
}
/* }}} */
PHP_MINIT_FUNCTION(image)
{
zend_hash_init(&php_image_handlers, 4, NULL, NULL, true);
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(image)
{
#ifdef ZTS
if (!tsrm_is_main_thread()) {
return SUCCESS;
}
#endif
zend_hash_destroy(&php_image_handlers);
return SUCCESS;
}
extern zend_module_entry basic_functions_module;
int php_image_register_handler(const struct php_image_handler *handler)
{
zend_hash_index_add_ptr(&php_image_handlers, (zend_ulong) php_image_handler_next_id, (void *) handler);
zend_register_long_constant(handler->const_name, strlen(handler->const_name), php_image_handler_next_id, CONST_PERSISTENT, basic_functions_module.module_number);
Z_LVAL_P(zend_get_constant_str(ZEND_STRL("IMAGETYPE_COUNT")))++;
return php_image_handler_next_id++;
}
zend_result php_image_unregister_handler(int image_type)
{
ZEND_ASSERT(image_type >= IMAGE_FILETYPE_FIXED_COUNT);
return zend_hash_index_del(&php_image_handlers, (zend_ulong) image_type);
}

View File

@@ -18,6 +18,9 @@
#ifndef PHP_IMAGE_H
#define PHP_IMAGE_H
PHP_MINIT_FUNCTION(image);
PHP_MSHUTDOWN_FUNCTION(image);
/* {{{ enum image_filetype
This enum is used to have ext/standard/image.c and ext/exif/exif.c use
the same constants for file types.
@@ -46,13 +49,13 @@ typedef enum
IMAGE_FILETYPE_AVIF,
IMAGE_FILETYPE_HEIF,
/* WHEN EXTENDING: PLEASE ALSO REGISTER IN basic_function.stub.php */
IMAGE_FILETYPE_COUNT
IMAGE_FILETYPE_FIXED_COUNT
} image_filetype;
/* }}} */
PHPAPI int php_getimagetype(php_stream *stream, const char *input, char *filetype);
PHPAPI char * php_image_type_to_mime_type(int image_type);
PHPAPI const char * php_image_type_to_mime_type(int image_type);
PHPAPI bool php_is_image_avif(php_stream *stream);
@@ -66,4 +69,22 @@ struct php_gfxinfo {
unsigned int channels;
};
typedef zend_result (*php_image_identify)(php_stream *stream);
typedef struct php_gfxinfo *(*php_image_get_info)(php_stream *stream);
struct php_image_handler {
const char *mime_type;
const char *extension;
const char *const_name;
php_image_identify identify;
php_image_get_info get_info;
};
#define PHP_IMAGE_CONST_NAME(suffix) ("IMAGETYPE_" suffix)
/* This should only be called on module init */
PHPAPI int php_image_register_handler(const struct php_image_handler *handler);
/* This should only be called on module shutdown */
PHPAPI zend_result php_image_unregister_handler(int image_type);
#endif /* PHP_IMAGE_H */

View File

@@ -1,81 +1,87 @@
--TEST--
image_type_to_mime_type() (passinf equivalent integer values)
image_type_to_mime_type() (passing equivalent integer values)
--CREDITS--
Sanjay Mantoor <sanjay.mantoor@gmail.com>
--FILE--
<?php
echo "*** Testing image_type_to_mime_type() : usage variations ***\n";
for($imagetype = 0; $imagetype <= IMAGETYPE_COUNT; ++$imagetype) {
for($imagetype = 0; $imagetype <= 19; ++$imagetype) {
echo "\n-- Iteration $imagetype --\n";
var_dump(image_type_to_mime_type($imagetype));
}
echo "\n-- Iteration 999 --\n";
var_dump(image_type_to_mime_type(999));
?>
--EXPECTREGEX--
\*\*\* Testing image_type_to_mime_type\(\) : usage variations \*\*\*
--EXPECT--
*** Testing image_type_to_mime_type() : usage variations ***
-- Iteration 0 --
string\(24\) "application\/octet-stream"
string(24) "application/octet-stream"
-- Iteration 1 --
string\(9\) "image\/gif"
string(9) "image/gif"
-- Iteration 2 --
string\(10\) "image\/jpeg"
string(10) "image/jpeg"
-- Iteration 3 --
string\(9\) "image\/png"
string(9) "image/png"
-- Iteration 4 --
string\(29\) "application\/x-shockwave-flash"
string(29) "application/x-shockwave-flash"
-- Iteration 5 --
string\(9\) "image\/psd"
string(9) "image/psd"
-- Iteration 6 --
string\(9\) "image\/bmp"
string(9) "image/bmp"
-- Iteration 7 --
string\(10\) "image\/tiff"
string(10) "image/tiff"
-- Iteration 8 --
string\(10\) "image\/tiff"
string(10) "image/tiff"
-- Iteration 9 --
string\(24\) "application\/octet-stream"
string(24) "application/octet-stream"
-- Iteration 10 --
string\(9\) "image\/jp2"
string(9) "image/jp2"
-- Iteration 11 --
string\(24\) "application\/octet-stream"
string(24) "application/octet-stream"
-- Iteration 12 --
string\(24\) "application\/octet-stream"
string(24) "application/octet-stream"
-- Iteration 13 --
string\(2[49]\) "application\/(x-shockwave-flash|octet-stream)"
string(29) "application/x-shockwave-flash"
-- Iteration 14 --
string\(9\) "image\/iff"
string(9) "image/iff"
-- Iteration 15 --
string\(18\) "image\/vnd.wap.wbmp"
string(18) "image/vnd.wap.wbmp"
-- Iteration 16 --
string\(9\) "image\/xbm"
string(9) "image/xbm"
-- Iteration 17 --
string\(24\) "image\/vnd.microsoft.icon"
string(24) "image/vnd.microsoft.icon"
-- Iteration 18 --
string\(10\) "image\/webp"
string(10) "image/webp"
-- Iteration 19 --
string\(10\) "image\/avif"
string(10) "image/avif"
-- Iteration 20 --
string\(10\) "image\/heif"
-- Iteration 21 --
string\(24\) "application\/octet-stream"
-- Iteration 999 --
string(24) "application/octet-stream"