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
1 change: 1 addition & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ PHP NEWS
is now a boolean. (David Carlier)
. Fixed bug GH-16883 (gzopen() does not use the default stream context when
opening HTTP URLs). (nielsdos)
. Implemented GH-17668 (zlib streams should support locking). (nielsdos)


<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
4 changes: 4 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ PHP 8.5 UPGRADE NOTES
and is not a QName because in those cases the namespace is taken
from the namespace href or prefix respectively.

- Zlib:
. flock() is now supported on zlib streams. Previously, this always
failed to perform any locking action.

========================================
3. Changes in SAPI modules
========================================
Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_inheritance.c
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ static const char *zend_asymmetric_visibility_string(uint32_t fn_flags) /* {{{ *
}
}

static zend_string *resolve_class_name(zend_class_entry *scope, zend_string *name) {
static zend_string *resolve_class_name(const zend_class_entry *scope, zend_string *name) {
ZEND_ASSERT(scope);
if (zend_string_equals_literal_ci(name, "parent") && scope->parent) {
if (scope->ce_flags & ZEND_ACC_RESOLVED_PARENT) {
Expand Down
6 changes: 5 additions & 1 deletion ext/opcache/jit/zend_jit_ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -14733,7 +14733,11 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit,
}

if (may_throw) {
zend_jit_check_exception(jit);
if (Z_MODE(res_addr) == IS_REG) {
zend_jit_check_exception_undef_result(jit, opline);
} else {
zend_jit_check_exception(jit);
}
}

return 1;
Expand Down
23 changes: 23 additions & 0 deletions ext/opcache/tests/jit/gh17747.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
GH-17747 (Exception on reading property in register-based FETCH_OBJ_R breaks JIT)
--EXTENSIONS--
opcache
--INI--
opcache.jit=function
--FILE--
<?php
class C {
public int $a;
public function test() {
var_dump($this->a);
}
}
$test = new C;
$test->test();
?>
--EXPECTF--
Fatal error: Uncaught Error: Typed property C::$a must not be accessed before initialization in %s:%d
Stack trace:
#0 %s(%d): C->test()
#1 {main}
thrown in %s on line %d
Binary file added ext/zlib/tests/005.txt.gz
Binary file not shown.
20 changes: 20 additions & 0 deletions ext/zlib/tests/gh17745.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
GH-17745 (zlib extension incorrectly handles object arguments)
--EXTENSIONS--
zlib
--FILE--
<?php
$obj = new stdClass;
$obj->level = 3;
var_dump(deflate_init(ZLIB_ENCODING_RAW, $obj));

class Options {
public int $level = 3;
}
var_dump(deflate_init(ZLIB_ENCODING_RAW, new Options));
?>
--EXPECT--
object(DeflateContext)#2 (0) {
}
object(DeflateContext)#3 (0) {
}
52 changes: 52 additions & 0 deletions ext/zlib/tests/zlib_lock_mandatory_windows.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--TEST--
Test mandatory locking on Windows
--EXTENSIONS--
zlib
--SKIPIF--
<?php
if (PHP_OS_FAMILY !== "Windows") die("skip Only for Windows because it has mandatory locking");
?>
--FILE--
<?php

echo "without wrapper\n";
$f = __DIR__."/005.txt.gz";
$h = gzopen($f,'r');
$h2 = gzopen($f, 'r');
stream_set_chunk_size($h2,1);

var_dump(flock($h, LOCK_EX));
var_dump(fread($h2, 1));
var_dump(fread($h, 1));

gzclose($h);
gzclose($h2);

echo "\nwith wrapper\n";
$f = "compress.zlib://".__DIR__."/005.txt.gz";
$h = fopen($f,'r');
$h2 = fopen($f, 'r');
stream_set_chunk_size($h2, 1);

var_dump(flock($h, LOCK_EX));
var_dump(fread($h2, 1));
var_dump(fread($h, 1));

gzclose($h);
gzclose($h2);

?>
--EXPECTF--
without wrapper
bool(true)

Notice: fread(): Read of 1 bytes failed with errno=13 Permission denied in %s on line %d
bool(false)
string(1) "W"

with wrapper
bool(true)

Notice: fread(): Read of 1 bytes failed with errno=13 Permission denied in %s on line %d
bool(false)
string(1) "W"
6 changes: 3 additions & 3 deletions ext/zlib/tests/zlib_wrapper_flock_basic.phpt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
--TEST--
Test function stream_get_meta_data on a zlib stream
Test function flock on a zlib stream
--EXTENSIONS--
zlib
--FILE--
<?php
$f = __DIR__."/004.txt.gz";
$h = gzopen($f,'r');
var_dump(flock($h, LOCK_SH));
var_dump(flock($h, LOCK_EX));
gzclose($h);
?>
--EXPECT--
bool(false)
bool(true)
6 changes: 6 additions & 0 deletions ext/zlib/zlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,7 @@ static bool zlib_create_dictionary_string(HashTable *options, char **dict, size_
zval *option_buffer;

if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("dictionary"))) != NULL) {
ZVAL_DEINDIRECT(option_buffer);
ZVAL_DEREF(option_buffer);
switch (Z_TYPE_P(option_buffer)) {
case IS_STRING: {
Expand Down Expand Up @@ -871,6 +872,7 @@ PHP_FUNCTION(inflate_init)
}

if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("window"))) != NULL) {
ZVAL_DEINDIRECT(option_buffer);
window = zval_get_long(option_buffer);
}
if (window < 8 || window > 15) {
Expand Down Expand Up @@ -1089,6 +1091,7 @@ PHP_FUNCTION(deflate_init)
}

if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("level"))) != NULL) {
ZVAL_DEINDIRECT(option_buffer);
level = zval_get_long(option_buffer);
}
if (level < -1 || level > 9) {
Expand All @@ -1097,6 +1100,7 @@ PHP_FUNCTION(deflate_init)
}

if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("memory"))) != NULL) {
ZVAL_DEINDIRECT(option_buffer);
memory = zval_get_long(option_buffer);
}
if (memory < 1 || memory > 9) {
Expand All @@ -1105,6 +1109,7 @@ PHP_FUNCTION(deflate_init)
}

if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("window"))) != NULL) {
ZVAL_DEINDIRECT(option_buffer);
window = zval_get_long(option_buffer);
}
if (window < 8 || window > 15) {
Expand All @@ -1113,6 +1118,7 @@ PHP_FUNCTION(deflate_init)
}

if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("strategy"))) != NULL) {
ZVAL_DEINDIRECT(option_buffer);
strategy = zval_get_long(option_buffer);
}
switch (strategy) {
Expand Down
4 changes: 2 additions & 2 deletions ext/zlib/zlib.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,11 @@ function gzread($stream, int $length): string|false {}
*/
function gzgets($stream, ?int $length = null): string|false {}

function deflate_init(int $encoding, array $options = []): DeflateContext|false {}
function deflate_init(int $encoding, array|object $options = []): DeflateContext|false {}

function deflate_add(DeflateContext $context, string $data, int $flush_mode = ZLIB_SYNC_FLUSH): string|false {}

function inflate_init(int $encoding, array $options = []): InflateContext|false {}
function inflate_init(int $encoding, array|object $options = []): InflateContext|false {}

function inflate_add(InflateContext $context, string $data, int $flush_mode = ZLIB_SYNC_FLUSH): string|false {}

Expand Down
6 changes: 3 additions & 3 deletions ext/zlib/zlib_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 41 additions & 2 deletions ext/zlib/zlib_fopen_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,18 @@ struct php_gz_stream_data_t {
php_stream *stream;
};

static void php_gziop_report_errors(php_stream *stream, size_t count, const char *verb)
{
if (!(stream->flags & PHP_STREAM_FLAG_SUPPRESS_ERRORS)) {
struct php_gz_stream_data_t *self = stream->abstract;
int error = 0;
gzerror(self->gz_file, &error);
if (error == Z_ERRNO) {
php_error_docref(NULL, E_NOTICE, "%s of %zu bytes failed with errno=%d %s", verb, count, errno, strerror(errno));
}
}
}

static ssize_t php_gziop_read(php_stream *stream, char *buf, size_t count)
{
struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *) stream->abstract;
Expand All @@ -38,6 +50,11 @@ static ssize_t php_gziop_read(php_stream *stream, char *buf, size_t count)
/* XXX this needs to be looped for the case count > UINT_MAX */
read = gzread(self->gz_file, buf, count);

/* Notify user of error, like the standard file wrapper normally does (e.g. errno=13 on mandatory lock failure). */
if (UNEXPECTED(read < 0)) {
php_gziop_report_errors(stream, count, "Read");
}

if (gzeof(self->gz_file)) {
stream->eof = 1;
}
Expand All @@ -50,7 +67,14 @@ static ssize_t php_gziop_write(php_stream *stream, const char *buf, size_t count
struct php_gz_stream_data_t *self = (struct php_gz_stream_data_t *) stream->abstract;

/* XXX this needs to be looped for the case count > UINT_MAX */
return gzwrite(self->gz_file, (char *) buf, count);
int written = gzwrite(self->gz_file, (char *) buf, count);

/* Notify user of error, like the standard file wrapper normally does (e.g. errno=13 on mandatory lock failure). */
if (UNEXPECTED(written < 0)) {
php_gziop_report_errors(stream, count, "Write");
}

return written;
}

static int php_gziop_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
Expand Down Expand Up @@ -95,14 +119,29 @@ static int php_gziop_flush(php_stream *stream)
return gzflush(self->gz_file, Z_SYNC_FLUSH);
}

static int php_gziop_set_option(php_stream *stream, int option, int value, void *ptrparam)
{
struct php_gz_stream_data_t *self = stream->abstract;

switch (option) {
case PHP_STREAM_OPTION_LOCKING:
case PHP_STREAM_OPTION_META_DATA_API:
return self->stream->ops->set_option(self->stream, option, value, ptrparam);
default:
break;
}

return PHP_STREAM_OPTION_RETURN_NOTIMPL;
}

const php_stream_ops php_stream_gzio_ops = {
php_gziop_write, php_gziop_read,
php_gziop_close, php_gziop_flush,
"ZLIB",
php_gziop_seek,
NULL, /* cast */
NULL, /* stat */
NULL /* set_option */
php_gziop_set_option /* set_option */
};

php_stream *php_stream_gzopen(php_stream_wrapper *wrapper, const char *path, const char *mode, int options,
Expand Down