Skip to content

Commit f2b371e

Browse files
committed
Add new stream erros API
This introduces new stream error handling that allows configurable handling of stream errors. This can be configured in stream contex. All streams, ext/standard and ext/phar stream errors are converted to use the new API. RFC: https://wiki.php.net/rfc/stream_errors Closes GH-20524
1 parent ce4adcc commit f2b371e

47 files changed

Lines changed: 3064 additions & 492 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

NEWS

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,10 +234,15 @@ PHP NEWS
234234
http(s) stream wrapper). (David Carlier)
235235

236236
- Streams:
237+
. Added new stream errors API including new StreamException, StreamError
238+
classes, StreamErrorStore, StreamErrorMode, StreamErrorCode enums,
239+
stream_last_errors() and stream_clear_errors() functions, error_mode,
240+
error_store and error_handler stream context options and extending some
241+
stream functions with context param. (Jakub Zelenka)
237242
. Added so_keepalive, tcp_keepidle, tcp_keepintvl and tcp_keepcnt stream
238-
socket context options.
243+
socket context options. (Jakub Zelenka)
239244
. Added so_reuseaddr streams context socket option that allows disabling
240-
address reuse.
245+
address reuse. (Jakub Zelenka)
241246
. Fixed bug GH-20370 (User stream filters could violate typed property
242247
constraints). (alexandre-daubois)
243248
. Allowed filtered streams to be casted as fd for select. (Jakub Zelenka)

UPGRADING

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ PHP 8.6 UPGRADE NOTES
207207
This makes it possible to override the timestamp and names of files.
208208

209209
- Streams:
210+
. Added new stream errors API including new classes, enums, functions and
211+
internal API. It is controlled using error_mode, error_store and
212+
error_handler stream context options.
213+
RFC: https://wiki.php.net/rfc/stream_errors
210214
. Added stream socket context option so_reuseaddr that allows disabling
211215
address reuse (SO_REUSEADDR) and explicitly uses SO_EXCLUSIVEADDRUSE on
212216
Windows.
@@ -309,6 +313,8 @@ PHP 8.6 UPGRADE NOTES
309313
. `clamp()` returns the given value if in range, else return the nearest
310314
bound.
311315
RFC: https://wiki.php.net/rfc/clamp_v2
316+
. `stream_last_errors()` and `stream_clear_errors()`.
317+
RFC: https://wiki.php.net/rfc/stream_errors
312318

313319
- Zip:
314320
. Added ZipArchive::openString() method.
@@ -326,6 +332,12 @@ PHP 8.6 UPGRADE NOTES
326332
- Standard:
327333
. enum SortDirection
328334
RFC: https://wiki.php.net/rfc/sort_direction_enum
335+
. StreamError
336+
. StreamException
337+
. enum StreamErrorStore
338+
. enum StreamErrorMode
339+
. enum StreamErrorCode
340+
RFC: https://wiki.php.net/rfc/stream_errors
329341

330342
========================================
331343
8. Removed Extensions and SAPIs

Zend/Optimizer/zend_func_infos.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ static const func_info_t func_infos[] = {
596596
F1("stream_get_line", MAY_BE_STRING|MAY_BE_FALSE),
597597
F1("stream_resolve_include_path", MAY_BE_STRING|MAY_BE_FALSE),
598598
F1("stream_get_wrappers", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING),
599+
F1("stream_last_errors", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_OBJECT),
599600
F1("stream_get_transports", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING),
600601
#if defined(HAVE_GETTIMEOFDAY)
601602
F1("uniqid", MAY_BE_STRING),

build/gen_stub.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3385,6 +3385,7 @@ public function __construct(
33853385
private /* readonly */ array $propertyInfos,
33863386
public array $funcInfos,
33873387
private readonly array $enumCaseInfos,
3388+
private readonly bool $generateCNameTable,
33883389
public readonly ?string $cond,
33893390
public ?int $phpVersionIdMinimumCompatibility,
33903391
public readonly bool $isUndocumentable,
@@ -3599,6 +3600,25 @@ public function getCDeclarations(): string
35993600

36003601
$code .= "} {$cEnumName};\n";
36013602

3603+
$caseCount = count($this->enumCaseInfos);
3604+
3605+
if ($this->generateCNameTable && $caseCount > 0) {
3606+
$cPrefix = 'ZEND_ENUM_' . str_replace('\\', '_', $this->name->toString());
3607+
$useGuard = $cPrefix . '_USE_NAME_TABLE';
3608+
3609+
$code .= "\n#define {$cPrefix}_CASE_COUNT {$caseCount}\n";
3610+
$code .= "\n#ifdef {$useGuard}\n";
3611+
$code .= "static const char *{$cEnumName}_case_names[{$cPrefix}_CASE_COUNT + 1] = {\n";
3612+
3613+
foreach ($this->enumCaseInfos as $case) {
3614+
$cName = $cPrefix . '_' . $case->name->case;
3615+
$code .= "\t[{$cName}] = \"{$case->name->case}\",\n";
3616+
}
3617+
3618+
$code .= "};\n";
3619+
$code .= "#endif\n";
3620+
}
3621+
36023622
if ($this->cond) {
36033623
$code .= "#endif\n";
36043624
}
@@ -5149,6 +5169,7 @@ function parseClass(
51495169
$isStrictProperties = array_key_exists('strict-properties', $tagMap);
51505170
$isNotSerializable = array_key_exists('not-serializable', $tagMap);
51515171
$isUndocumentable = $isUndocumentable || array_key_exists('undocumentable', $tagMap);
5172+
$generateCNameTable = array_key_exists('c-name-table', $tagMap);
51525173
foreach ($tags as $tag) {
51535174
if ($tag->name === 'alias') {
51545175
$alias = $tag->getValue();
@@ -5210,6 +5231,7 @@ function parseClass(
52105231
$properties,
52115232
$methods,
52125233
$enumCases,
5234+
$generateCNameTable,
52135235
$cond,
52145236
$minimumPhpVersionIdCompatibility,
52155237
$isUndocumentable

configure.ac

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,6 +1689,7 @@ PHP_ADD_SOURCES([main/streams], m4_normalize([
16891689
memory.c
16901690
mmap.c
16911691
plain_wrapper.c
1692+
stream_errors.c
16921693
streams.c
16931694
transports.c
16941695
userspace.c

ext/phar/dirstream.c

Lines changed: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -247,27 +247,32 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path,
247247
{
248248
char *error;
249249

250-
php_url *resource = phar_parse_url(wrapper, path, mode, options);
251-
if (!resource) {
252-
php_stream_wrapper_log_error(wrapper, options, "phar url \"%s\" is unknown", path);
250+
php_url *resource = phar_parse_url(wrapper, context, path, mode, options);
251+
if (resource == NULL) {
252+
php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl,
253+
"phar url \"%s\" is unknown", path);
253254
return NULL;
254255
}
255256

256257
/* we must have at the very least phar://alias.phar/ */
257258
if (!resource->scheme || !resource->host || !resource->path) {
258259
if (resource->host && !resource->path) {
259-
php_stream_wrapper_log_error(wrapper, options, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", path, ZSTR_VAL(resource->host));
260+
php_stream_wrapper_log_warn(wrapper, context, options, InvalidPath,
261+
"phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)",
262+
path, ZSTR_VAL(resource->host));
260263
php_url_free(resource);
261264
return NULL;
262265
}
263266
php_url_free(resource);
264-
php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\", must have at least phar://%s/", path, path);
267+
php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl,
268+
"phar error: invalid url \"%s\", must have at least phar://%s/", path, path);
265269
return NULL;
266270
}
267271

268272
if (!zend_string_equals_literal_ci(resource->scheme, "phar")) {
269273
php_url_free(resource);
270-
php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar url \"%s\"", path);
274+
php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported,
275+
"phar error: not a phar url \"%s\"", path);
271276
return NULL;
272277
}
273278

@@ -276,10 +281,11 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path,
276281
const phar_archive_data *phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error);
277282
if (!phar) {
278283
if (error) {
279-
php_stream_wrapper_log_error(wrapper, options, "%s", error);
284+
php_stream_wrapper_log_warn(wrapper, context, options, NotFound, "%s", error);
280285
efree(error);
281286
} else {
282-
php_stream_wrapper_log_error(wrapper, options, "phar file \"%s\" is unknown", ZSTR_VAL(resource->host));
287+
php_stream_wrapper_log_warn(wrapper, context, options, NotFound,
288+
"phar file \"%s\" is unknown", ZSTR_VAL(resource->host));
283289
}
284290
php_url_free(resource);
285291
return NULL;
@@ -344,7 +350,8 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo
344350
/* pre-readonly check, we need to know if this is a data phar */
345351
zend_string *arch = phar_split_fname(url_from, strlen(url_from), NULL, 2, 2);
346352
if (!arch) {
347-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", no phar archive specified", url_from);
353+
php_stream_wrapper_log_warn(wrapper, context, options, NotFound,
354+
"phar error: cannot create directory \"%s\", no phar archive specified", url_from);
348355
return 0;
349356
}
350357

@@ -353,30 +360,35 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo
353360
zend_string_release_ex(arch, false);
354361

355362
if (PHAR_G(readonly) && (!phar || !phar->is_data)) {
356-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", write operations disabled", url_from);
363+
php_stream_wrapper_log_warn(wrapper, context, options, Readonly,
364+
"phar error: cannot create directory \"%s\", write operations disabled", url_from);
357365
return 0;
358366
}
359367

360-
if ((resource = phar_parse_url(wrapper, url_from, "w", options)) == NULL) {
368+
if ((resource = phar_parse_url(wrapper, context, url_from, "w", options)) == NULL) {
361369
return 0;
362370
}
363371

364372
/* we must have at the very least phar://alias.phar/internalfile.php */
365373
if (!resource->scheme || !resource->host || !resource->path) {
366374
php_url_free(resource);
367-
php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url_from);
375+
php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl,
376+
"phar error: invalid url \"%s\"", url_from);
368377
return 0;
369378
}
370379

371380
if (!zend_string_equals_literal_ci(resource->scheme, "phar")) {
372381
php_url_free(resource);
373-
php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url_from);
382+
php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported,
383+
"phar error: not a phar stream url \"%s\"", url_from);
374384
return 0;
375385
}
376386

377387
phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error);
378388
if (!phar) {
379-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error);
389+
php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed,
390+
"phar error: cannot create directory \"%s\" in phar \"%s\", error retrieving phar information: %s",
391+
ZSTR_VAL(resource->path) + 1, ZSTR_VAL(resource->host), error);
380392
efree(error);
381393
php_url_free(resource);
382394
return 0;
@@ -389,27 +401,35 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo
389401
zend_string_efree(e->filename);
390402
efree(e);
391403
}
392-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host));
404+
php_stream_wrapper_log_warn(wrapper, context, options, AlreadyExists,
405+
"phar error: cannot create directory \"%s\" in phar \"%s\", directory already exists",
406+
ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host));
393407
php_url_free(resource);
394408
return 0;
395409
}
396410

397411
if (error) {
398-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
412+
php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed,
413+
"phar error: cannot create directory \"%s\" in phar \"%s\", %s",
414+
ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
399415
efree(error);
400416
php_url_free(resource);
401417
return 0;
402418
}
403419

404420
if (phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1, 0, &error, true)) {
405421
/* entry exists as a file */
406-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", file already exists", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host));
422+
php_stream_wrapper_log_warn(wrapper, context, options, AlreadyExists,
423+
"phar error: cannot create directory \"%s\" in phar \"%s\", file already exists",
424+
ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host));
407425
php_url_free(resource);
408426
return 0;
409427
}
410428

411429
if (error) {
412-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
430+
php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed,
431+
"phar error: cannot create directory \"%s\" in phar \"%s\", %s",
432+
ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
413433
efree(error);
414434
php_url_free(resource);
415435
return 0;
@@ -437,17 +457,20 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo
437457
entry.flags = PHAR_ENT_PERM_DEF_DIR;
438458
entry.old_flags = PHAR_ENT_PERM_DEF_DIR;
439459

440-
void *had_been_added = zend_hash_add_mem(&phar->manifest, entry.filename, &entry, sizeof(phar_entry_info));
441-
if (!had_been_added) {
442-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname));
460+
if (NULL == zend_hash_add_mem(&phar->manifest, entry.filename, &entry, sizeof(phar_entry_info))) {
461+
php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed,
462+
"phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed",
463+
ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname));
443464
zend_string_efree(entry.filename);
444465
return 0;
445466
}
446467

447468
phar_flush(phar, &error);
448469

449470
if (error) {
450-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname), error);
471+
php_stream_wrapper_log_warn(wrapper, context, options, MkdirFailed,
472+
"phar error: cannot create directory \"%s\" in phar \"%s\", %s",
473+
ZSTR_VAL(entry.filename), ZSTR_VAL(phar->fname), error);
451474
zend_hash_del(&phar->manifest, entry.filename);
452475
efree(error);
453476
return 0;
@@ -468,7 +491,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options
468491
/* pre-readonly check, we need to know if this is a data phar */
469492
zend_string *arch = phar_split_fname(url, strlen(url), NULL, 2, 2);
470493
if (!arch) {
471-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url);
494+
php_stream_wrapper_log_warn(wrapper, context, options, NotFound,
495+
"phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url);
472496
return 0;
473497
}
474498

@@ -477,31 +501,36 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options
477501
zend_string_release_ex(arch, false);
478502

479503
if (PHAR_G(readonly) && (!phar || !phar->is_data)) {
480-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot rmdir directory \"%s\", write operations disabled", url);
504+
php_stream_wrapper_log_warn(wrapper, context, options, Readonly,
505+
"phar error: cannot rmdir directory \"%s\", write operations disabled", url);
481506
return 0;
482507
}
483508

484-
php_url *resource = phar_parse_url(wrapper, url, "w", options);
509+
php_url *resource = phar_parse_url(wrapper, context, url, "w", options);
485510
if (!resource) {
486511
return 0;
487512
}
488513

489514
/* we must have at the very least phar://alias.phar/internalfile.php */
490515
if (!resource->scheme || !resource->host || !resource->path) {
491516
php_url_free(resource);
492-
php_stream_wrapper_log_error(wrapper, options, "phar error: invalid url \"%s\"", url);
517+
php_stream_wrapper_log_warn(wrapper, context, options, InvalidUrl,
518+
"phar error: invalid url \"%s\"", url);
493519
return 0;
494520
}
495521

496522
if (!zend_string_equals_literal_ci(resource->scheme, "phar")) {
497523
php_url_free(resource);
498-
php_stream_wrapper_log_error(wrapper, options, "phar error: not a phar stream url \"%s\"", url);
524+
php_stream_wrapper_log_warn(wrapper, context, options, ProtocolUnsupported,
525+
"phar error: not a phar stream url \"%s\"", url);
499526
return 0;
500527
}
501528

502529
phar = phar_get_archive(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), NULL, 0, &error);
503530
if (!phar) {
504-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
531+
php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed,
532+
"phar error: cannot remove directory \"%s\" in phar \"%s\", error retrieving phar information: %s",
533+
ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
505534
efree(error);
506535
php_url_free(resource);
507536
return 0;
@@ -512,10 +541,14 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options
512541
phar_entry_info *entry = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, path_len, 2, &error, true);
513542
if (!entry) {
514543
if (error) {
515-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
544+
php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed,
545+
"phar error: cannot remove directory \"%s\" in phar \"%s\", %s",
546+
ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
516547
efree(error);
517548
} else {
518-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host));
549+
php_stream_wrapper_log_warn(wrapper, context, options, NotFound,
550+
"phar error: cannot remove directory \"%s\" in phar \"%s\", directory does not exist",
551+
ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host));
519552
}
520553
php_url_free(resource);
521554
return 0;
@@ -529,7 +562,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options
529562
zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len)
530563
&& IS_SLASH(ZSTR_VAL(str_key)[path_len])
531564
) {
532-
php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty");
565+
php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed,
566+
"phar error: Directory not empty");
533567
if (entry->is_temp_dir) {
534568
zend_string_efree(entry->filename);
535569
efree(entry);
@@ -545,7 +579,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options
545579
zend_string_starts_with_cstr(str_key, ZSTR_VAL(resource->path)+1, path_len)
546580
&& IS_SLASH(ZSTR_VAL(str_key)[path_len])
547581
) {
548-
php_stream_wrapper_log_error(wrapper, options, "phar error: Directory not empty");
582+
php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed,
583+
"phar error: Directory not empty");
549584
if (entry->is_temp_dir) {
550585
zend_string_efree(entry->filename);
551586
efree(entry);
@@ -566,7 +601,9 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options
566601
phar_flush(phar, &error);
567602

568603
if (error) {
569-
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(entry->filename), ZSTR_VAL(phar->fname), error);
604+
php_stream_wrapper_log_warn(wrapper, context, options, RmdirFailed,
605+
"phar error: cannot remove directory \"%s\" in phar \"%s\", %s",
606+
ZSTR_VAL(entry->filename), ZSTR_VAL(phar->fname), error);
570607
php_url_free(resource);
571608
efree(error);
572609
return 0;

ext/phar/dirstream.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options
2222
#ifdef PHAR_DIRSTREAM
2323
#include "ext/standard/url.h"
2424

25-
php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options);
25+
php_url* phar_parse_url(php_stream_wrapper *wrapper, php_stream_context *context, const char *filename, const char *mode, int options);
2626

2727
/* directory handlers */
2828
static ssize_t phar_dir_write(php_stream *stream, const char *buf, size_t count);

0 commit comments

Comments
 (0)