php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #75931 Call stream filter constructor first and destructor last
Submitted: 2018-02-07 16:58 UTC Modified: 2023-12-17 16:45 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:1 (100.0%)
From: ivo at beerntea dot com Assigned:
Status: Verified Package: Streams related
PHP Version: 7.2.2 OS: Linux x64
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: ivo at beerntea dot com
New email:
PHP Version: OS:

 

 [2018-02-07 16:58 UTC] ivo at beerntea dot com
Description:
------------
A php_user_filter instance may be used by PHP's I/O functions after it (and anything it references) has been destructed. This happens when the stream the filter is bound to is closed while the process is exiting.

Allowing the stream to be garbage collected before the process exits (uncomment `$out = NULL;` in the example below) fixes the destruction issue, but still leaves the last (closing) call to the filter method with an invalid stream resource.

Manually closing the stream while it is still referenced results in correct behavior: the final filter call has a valid stream resource and destruction happens after the final call.

Also note that the constructor method is never called. It might be unexpected that an object can be created without its constructor getting called.

This is just a simplified test case. I was implementing a gzip stream filter (with gzip header), using the deflate_init/deflate_add methods when I ran into this problem. The deflate resource stored in a property is also cleaned up before the final filter call happens, so there is no opportunity to finalize the output.

Test script:
---------------
<?php
class test_filter extends php_user_filter {
  public function __construct() {
    fprintf(STDERR, "filter construct\n");
  }
  public function onCreate() {
    fprintf(STDERR, "filter onCreate\n");
  }
  public function filter($in, $out, &$consumed, $closing) {
    fprintf(STDERR, "filter consumed=$consumed closing=".($closing ? 'true' : 'false')." stream valid=".(is_resource($this->stream) ? 'true' : 'false')."\n");
    while ($bucket = stream_bucket_make_writeable($in)) {
      $consumed += $bucket->datalen;
      stream_bucket_append($out, $bucket);
    }
    return PSFS_PASS_ON;
  }
  public function onClose() {
    fprintf(STDERR, "filter onClose\n");
  }
  public function __destruct() {
    fprintf(STDERR, "filter destruct\n");
  }
}

stream_filter_register('test_filter', 'test_filter');

$in = fopen('php://stdin', 'r');
$out = fopen('php://stdout', 'w');
stream_filter_append($out, 'test_filter', STREAM_FILTER_WRITE, array());
stream_copy_to_stream($in, $out);

//fclose($out);
//$out = NULL;


Expected result:
----------------
filter construct
filter onCreate
filter consumed=0 closing=false stream valid=true
hello world
filter consumed=0 closing=true stream valid=true
filter onClose
filter destruct


Actual result:
--------------
filter onCreate
filter consumed=0 closing=false stream valid=true
hello world
filter destruct
filter consumed=0 closing=true stream valid=false
filter onClose


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-12-10 14:43 UTC] cmb@php.net
-Status: Open +Status: Verified
 [2020-12-10 14:43 UTC] cmb@php.net
> Also note that the constructor method is never called.

Documented as <http://svn.php.net/viewvc?view=revision&revision=352000>.

Other than that, yes, the behavior of php://stdin is broken wrt.
user filters.
 [2021-08-16 08:45 UTC] cmb@php.net
> Other than that, yes, the behavior of php://stdin is broken wrt.
> user filters.

Actually, it's not only about php://stdin, but streams in general.
 [2023-12-17 16:45 UTC] bukka@php.net
-Summary: User stream filter is used after destruction +Summary: Call stream filter constructor first and destructor last -Type: Bug +Type: Feature/Change Request
 [2023-12-17 16:45 UTC] bukka@php.net
I have just done some debugging and the object is not actually garbage collected. It is just that destructor function is first called and then the actual filter resource is dtored which calls the onClose. So I don't see any potential crash because of this and it is safe.

I agree that it would be logical to make the order as expected but that will be a feature request.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 16:01:28 2024 UTC