Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ PHP NEWS

- Standard:
. Add HEIF/HEIC support to getimagesize. (Benstone Zhang)
. Implement #71517 (Implement SVG support for getimagesize() and friends).
(nielsdos)

- URI:
. Empty host handling is fixed. (Máté Kocsis)
Expand Down
12 changes: 12 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,15 @@ PHP 8.5 UPGRADE NOTES
ignored. This change affects only the sendmail transport.
. getimagesize() now supports HEIF/HEIC images.

- Standard:
. getimagesize() now supports SVG images when ext-libxml is also loaded.
Similarly, image_type_to_extension() and image_type_to_extension()
now also handle IMAGETYPE_SVG.
. The array returned by getimagesize() now has two additional entries:
"width_unit" and "height_unit" to indicate in which units the dimensions
are expressed. These units are px by default. They are not necessarily
the same (just to give one example: one may be cm and the other may be px).

- XSL:
. The $namespace argument of XSLTProcessor::getParameter(),
XSLTProcessor::setParameter() and XSLTProcessor::removeParameter()
Expand Down Expand Up @@ -566,6 +575,9 @@ PHP 8.5 UPGRADE NOTES
. T_VOID_CAST.
. T_PIPE.

- Standard:
. IMAGETYPE_SVG when libxml is loaded.

========================================
11. Changes to INI File Handling
========================================
Expand Down
4 changes: 4 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ PHP 8.5 INTERNALS UPGRADE NOTES
. The php_std_date() function has been removed. Use php_format_date() with
the "D, d M Y H:i:s \\G\\M\\T" format instead.
. Added php_url_encode_to_smart_str() to encode a URL to a smart_str buffer.
. The functionality of getimagesize(), image_type_to_mime_type(),
and image_type_to_extension() is now extensible using the internal APIs
php_image_register_handler() and php_image_unregister_handler() in
php_image.h.

========================
4. OpCode changes
Expand Down
45 changes: 22 additions & 23 deletions ext/intl/calendar/gregoriancalendar_methods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ static void _php_intlgregcal_constructor_body(

// instantion of ICU object
Calendar_object *co = Z_INTL_CALENDAR_P(return_value);
GregorianCalendar *gcal = NULL;
std::unique_ptr<GregorianCalendar> gcal;

if (co->ucal) {
zend_throw_error(NULL, "IntlGregorianCalendar object is already constructed");
Expand All @@ -159,15 +159,12 @@ static void _php_intlgregcal_constructor_body(
locale = const_cast<char*>(intl_locale_get_default());
}

gcal = new GregorianCalendar(tz, Locale::createFromName(locale),
status);
gcal = std::unique_ptr<GregorianCalendar>(new GregorianCalendar(tz, Locale::createFromName(locale),
status));
// Should this throw?
if (U_FAILURE(status)) {
intl_error_set(NULL, status, "intlgregcal_create_instance: error "
"creating ICU GregorianCalendar from time zone and locale", 0);
if (gcal) {
delete gcal;
}
delete tz;
if (!is_constructor) {
zval_ptr_dtor(return_value);
Expand All @@ -177,26 +174,28 @@ static void _php_intlgregcal_constructor_body(
}
} else {
// From date/time (3, 5 or 6 arguments)
GregorianCalendar *tmp;
for (int i = 0; i < variant; i++) {
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(largs[i], hasThis() ? (i-1) : i);
}

if (variant == 3) {
gcal = new GregorianCalendar((int32_t)largs[0], (int32_t)largs[1],
tmp = new GregorianCalendar((int32_t)largs[0], (int32_t)largs[1],
(int32_t)largs[2], status);
} else if (variant == 5) {
gcal = new GregorianCalendar((int32_t)largs[0], (int32_t)largs[1],
tmp = new GregorianCalendar((int32_t)largs[0], (int32_t)largs[1],
(int32_t)largs[2], (int32_t)largs[3], (int32_t)largs[4], status);
} else if (variant == 6) {
gcal = new GregorianCalendar((int32_t)largs[0], (int32_t)largs[1],
tmp = new GregorianCalendar((int32_t)largs[0], (int32_t)largs[1],
(int32_t)largs[2], (int32_t)largs[3], (int32_t)largs[4], (int32_t)largs[5],
status);
} else {
ZEND_UNREACHABLE();
}

if (!set_gregorian_calendar_time_zone(gcal, status)) {
delete gcal;
gcal = std::unique_ptr<GregorianCalendar>(tmp);

if (!set_gregorian_calendar_time_zone(gcal.get(), status)) {
if (!is_constructor) {
zval_ptr_dtor(return_value);
RETVAL_NULL();
Expand All @@ -205,7 +204,7 @@ static void _php_intlgregcal_constructor_body(
}
}

co->ucal = gcal;
co->ucal = gcal.release();
}

U_CFUNC PHP_FUNCTION(intlgregcal_create_instance)
Expand Down Expand Up @@ -234,7 +233,7 @@ U_CFUNC PHP_METHOD(IntlGregorianCalendar, createFromDate)
UErrorCode status = U_ZERO_ERROR;
zend_error_handling error_handling;
Calendar_object *co;
GregorianCalendar *gcal;
std::unique_ptr<GregorianCalendar> gcal;

intl_error_reset(NULL);

Expand All @@ -250,15 +249,14 @@ U_CFUNC PHP_METHOD(IntlGregorianCalendar, createFromDate)

zend_replace_error_handling(EH_THROW, IntlException_ce_ptr, &error_handling);

gcal = new GregorianCalendar((int32_t) year, (int32_t) month, (int32_t) day, status);
if (!set_gregorian_calendar_time_zone(gcal, status)) {
delete gcal;
gcal = std::unique_ptr<GregorianCalendar>(new GregorianCalendar((int32_t) year, (int32_t) month, (int32_t) day, status));
if (!set_gregorian_calendar_time_zone(gcal.get(), status)) {
goto cleanup;
}

object_init_ex(return_value, GregorianCalendar_ce_ptr);
co = Z_INTL_CALENDAR_P(return_value);
co->ucal = gcal;
co->ucal = gcal.release();

cleanup:
zend_restore_error_handling(&error_handling);
Expand All @@ -271,7 +269,7 @@ U_CFUNC PHP_METHOD(IntlGregorianCalendar, createFromDateTime)
UErrorCode status = U_ZERO_ERROR;
zend_error_handling error_handling;
Calendar_object *co;
GregorianCalendar *gcal;
GregorianCalendar *tmp;

intl_error_reset(NULL);

Expand All @@ -294,19 +292,20 @@ U_CFUNC PHP_METHOD(IntlGregorianCalendar, createFromDateTime)
zend_replace_error_handling(EH_THROW, IntlException_ce_ptr, &error_handling);

if (second_is_null) {
gcal = new GregorianCalendar((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute, status);
tmp = new GregorianCalendar((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute, status);
} else {
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(second, 6);
gcal = new GregorianCalendar((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute, (int32_t) second, status);
tmp = new GregorianCalendar((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute, (int32_t) second, status);
}
if (!set_gregorian_calendar_time_zone(gcal, status)) {
delete gcal;
auto gcal = std::unique_ptr<GregorianCalendar>(tmp);
if (!set_gregorian_calendar_time_zone(gcal.get(), status)) {
goto cleanup;
}

object_init_ex(return_value, GregorianCalendar_ce_ptr);
co = Z_INTL_CALENDAR_P(return_value);
co->ucal = gcal;
// TODO: trying to get passed the ownership change step
co->ucal = gcal.release();

cleanup:
zend_restore_error_handling(&error_handling);
Expand Down
2 changes: 1 addition & 1 deletion ext/libxml/config.w32
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ if (PHP_LIBXML == "yes") {
if (GREP_HEADER("libxml/xmlversion.h", "#define\\s+LIBXML_VERSION\\s+(\\d+)", PHP_PHP_BUILD + "\\include\\libxml2") &&
+RegExp.$1 >= 20904) {

EXTENSION("libxml", "libxml.c mime_sniff.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
EXTENSION("libxml", "libxml.c mime_sniff.c image_svg.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
AC_DEFINE("HAVE_LIBXML", 1, "Define to 1 if the PHP extension 'libxml' is available.");
ADD_FLAG("CFLAGS_LIBXML", "/D LIBXML_STATIC /D LIBXML_STATIC_FOR_DLL /D HAVE_WIN32_THREADS ");
if (!PHP_LIBXML_SHARED) {
Expand Down
2 changes: 1 addition & 1 deletion ext/libxml/config0.m4
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ if test "$PHP_LIBXML" != "no"; then
AC_DEFINE([HAVE_LIBXML], [1],
[Define to 1 if the PHP extension 'libxml' is available.])
PHP_NEW_EXTENSION([libxml],
[libxml.c mime_sniff.c],
[libxml.c mime_sniff.c image_svg.c],
[$ext_shared],,
[-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1])
PHP_INSTALL_HEADERS([ext/libxml], [php_libxml.h])
Expand Down
178 changes: 178 additions & 0 deletions ext/libxml/image_svg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Niels Dossche <nielsdos@php.net> |
+----------------------------------------------------------------------+
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "image_svg.h"
#include "php_libxml.h"

#include "ext/standard/php_image.h"

#include <libxml/xmlreader.h>

#ifdef HAVE_LIBXML

static int svg_image_type_id;

static int php_libxml_svg_stream_read(void *context, char *buffer, int len)
{
return php_stream_read(context, buffer, len);
}

/* Sanity check that the input only contains characters valid for a dimension (numbers with units, e.g. 5cm).
* This also protects the user against injecting XSS.
* Only accept [0-9]+[a-zA-Z]* */
static bool php_libxml_parse_dimension(const xmlChar *input, const xmlChar **unit_position)
{
if (!(*input >= '0' && *input <= '9')) {
return false;
}

input++;

while (*input) {
if (!(*input >= '0' && *input <= '9')) {
if ((*input >= 'a' && *input <= 'z') || (*input >= 'A' && *input <= 'Z')) {
break;
}
return false;
}
input++;
}

*unit_position = input;

while (*input) {
if (!((*input >= 'a' && *input <= 'z') || (*input >= 'A' && *input <= 'Z'))) {
return false;
}
input++;
}

return true;
}

zend_result php_libxml_svg_image_handle(php_stream *stream, struct php_gfxinfo **result)
{
if (php_stream_rewind(stream)) {
return FAILURE;
}

/* Early check before doing more expensive work */
if (php_stream_getc(stream) != '<') {
return FAILURE;
}

if (php_stream_rewind(stream)) {
return FAILURE;
}

PHP_LIBXML_SANITIZE_GLOBALS(reader_for_stream);
xmlTextReaderPtr reader = xmlReaderForIO(
php_libxml_svg_stream_read,
NULL,
stream,
NULL,
NULL,
XML_PARSE_NOWARNING | XML_PARSE_NOERROR | XML_PARSE_NONET
);
PHP_LIBXML_RESTORE_GLOBALS(reader_for_stream);

if (!reader) {
return FAILURE;
}

bool is_svg = false;
while (xmlTextReaderRead(reader) == 1) {
if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
/* Root must be an svg element */
const xmlChar *name = xmlTextReaderConstLocalName(reader);
if (!name || strcasecmp((const char *) name, "svg") != 0) {
break;
}

xmlChar *width = xmlTextReaderGetAttribute(reader, BAD_CAST "width");
xmlChar *height = xmlTextReaderGetAttribute(reader, BAD_CAST "height");
const xmlChar *width_unit_position, *height_unit_position;
if (!width || !height
|| !php_libxml_parse_dimension(width, &width_unit_position)
|| !php_libxml_parse_dimension(height, &height_unit_position)) {
xmlFree(width);
xmlFree(height);
break;
}

is_svg = true;
if (result) {
*result = ecalloc(1, sizeof(**result));
(*result)->width = ZEND_STRTOL((const char *) width, NULL, 10);
(*result)->height = ZEND_STRTOL((const char *) height, NULL, 10);
if (*width_unit_position) {
(*result)->width_unit = zend_string_init((const char*) width_unit_position,
xmlStrlen(width_unit_position), false);
}
if (*height_unit_position) {
(*result)->height_unit = zend_string_init((const char*) height_unit_position,
xmlStrlen(height_unit_position), false);
}
}

xmlFree(width);
xmlFree(height);
break;
}
}

xmlFreeTextReader(reader);

return is_svg ? SUCCESS : FAILURE;
}

zend_result php_libxml_svg_image_identify(php_stream *stream)
{
return php_libxml_svg_image_handle(stream, NULL);
}

struct php_gfxinfo *php_libxml_svg_image_get_info(php_stream *stream)
{
struct php_gfxinfo *result = NULL;
zend_result status = php_libxml_svg_image_handle(stream, &result);
ZEND_ASSERT((status == SUCCESS) == (result != NULL));
return result;
}

static const struct php_image_handler svg_image_handler = {
"image/svg+xml",
".svg",
PHP_IMAGE_CONST_NAME("SVG"),
php_libxml_svg_image_identify,
php_libxml_svg_image_get_info,
};

void php_libxml_register_image_svg_handler(void)
{
svg_image_type_id = php_image_register_handler(&svg_image_handler);
}

zend_result php_libxml_unregister_image_svg_handler(void)
{
return php_image_unregister_handler(svg_image_type_id);
}

#endif
Loading