php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #53888 ftruncate() does not work with user-defined stream wrappers
Submitted: 2011-01-31 08:45 UTC Modified: 2011-02-17 01:25 UTC
From: ivan dot enderlin at hoa-project dot net Assigned: cataphract (profile)
Status: Closed Package: Filesystem function related
PHP Version: trunk-SVN-2011-01-31 (SVN) OS:
Private report: No CVE-ID: None
 [2011-01-31 08:45 UTC] ivan dot enderlin at hoa-project dot net
Description:
------------
Hey :-),

Seems like ftruncate() does not reconized an user-defined stream (with stream wrapper) as a valid stream. When we use it, it raises the following error: “Can't truncate this stream!”. All the related subject about this error on the Web talks about stream wrappers. Thus, it's seem to be the real problem.

Test script:
---------------
<?php

class StreamWrapper {

    private $_stream     = null;
    private $_streamName = null;
    public  $context     = null;

    public static function realPath ( $path ) {

        return substr($path, 6);
    }

    public function stream_close ( ) {

        if(true === @fclose($this->getStream())) {

            $this->_stream     = null;
            $this->_streamName = null;
        }

        return;
    }

    public function stream_eof ( ) {

        return feof($this->getStream());
    }

    public function stream_flush ( ) {

        return fflush($this->getStream());
    }

    public function stream_lock ( $operation ) {

        return flock($this->getStream(), $operation);
    }

    public function stream_open ( $path, $mode, $options, &$openedPath ) {

        $p = self::realPath($path);

        if(false === $p)
            return false;

        if(null === $this->context)
            $openedPath = fopen(
                $p,
                $mode,
                $options & STREAM_USE_PATH
            );
        else
            $openedPath = fopen(
                $p,
                $mode,
                $options & STREAM_USE_PATH,
                $this->context
            );

        $this->_stream     = $openedPath;
        $this->_streamName = $p;

        return true;
    }

    public function stream_read ( $count ) {

        return fread($this->getStream(), $count);
    }

    public function stream_seek ( $offset, $whence = SEEK_SET ) {

        return fseek($this->getStream(), $offset, $whence);
    }

    public function stream_stat ( ) {

        return fstat($this->getStream());
    }

    public function stream_tell ( ) {

        return ftell($this->getStream());
    }

    public function stream_write ( $data ) {

        return fwrite($this->getStream(), $data);
    }

    public function dir_closedir ( ) {

        if(true === $handle = @closedir($this->getStream())) {

            $this->_stream     = null;
            $this->_streamName = null;
        }

        return $handle;
    }

    public function dir_opendir ( $path, $options ) {

        $p      = self::realPath($path);
        $handle = null;

        if(null === $this->context)
            $handle = @opendir($p);
        else
            $handle = @opendir($p, $this->context);

        if(false === $handle)
            return false;

        $this->_stream     = $handle;
        $this->_streamName = $p;

        return true;
    }

    public function dir_readdir ( ) {

        return readdir($this->getStream());
    }

    public function dir_rewinddir ( ) {

        return rewinddir($this->getStream());
    }

    public function mkdir ( $path, $mode, $options ) {

        if(null === $this->context)
            return mkdir(
                self::realPath($path),
                $mode,
                $options | STREAM_MKDIR_RECURSIVE
            );

        return mkdir(
            self::realPath($path),
            $mode,
            $options | STREAM_MKDIR_RECURSIVE,
            $this->context
        );
    }

    public function rename ( $from, $to ) {

        if(null === $this->context)
            return rename(self::realPath($from), self::realPath($to));

        return rename(self::realPath($from), self::realPath($to), $this->context);
    }

    public function rmdir ( $path, $options ) {

        if(null === $this->context)
            return rmdir(self::realPath($path));
        
        return rmdir(self::realPath($path), $this->context);
    }

    public function unlink ( $path ) {

        if(null === $this->context)
            return unlink(self::realPath($path));

        return unlink(self::realPath($path), $this->context);
    }

    public function url_stat ( $path, $flags ) {

        if(false === $p = self::realPath($path))
            if($flags & STREAM_URL_STAT_QUIET)
                return array(); // Not sure…
            else
                return trigger_error(
                    'Path ' . $path . ' cannot be resolved.',
                    E_WARNING
                );

        if($flags & STREAM_URL_STAT_LINK)
            return @lstat($p);

        return @stat($p);
    }

    protected function getStream ( ) {

        return $this->_stream;
    }

    protected function getStreamName ( ) {

        return $this->_streamName;
    }
}

stream_wrapper_register('bug', 'StreamWrapper');


var_dump($fd = fopen('bug://Test.txt', 'w+'));

var_dump(fwrite($fd,    'Foobar1' . "\n"));
var_dump(fwrite($fd,    'Foobar2' . "\n"));
var_dump(fwrite($fd,    'Foobar3' . "\n"));
var_dump(ftruncate($fd, 3));

var_dump(fclose($fd));

Expected result:
----------------
Output:

resource(7) of type (stream)
int(8)
int(8)
int(8)
bool(true)
bool(true)

Test.txt:
Foo

Actual result:
--------------
Output:

resource(7) of type (stream)
int(8)
int(8)
int(8)
PHP Warning:  ftruncate(): Can't truncate this stream! in /Users/hywan/Development/Hoa/Laboratory/Bug/StreamWrapper.php on line 210

Warning: ftruncate(): Can't truncate this stream! in /Users/hywan/Development/Hoa/Laboratory/Bug/StreamWrapper.php on line 210
bool(false)
bool(true)

Test.txt:
Foobar1
Foobar2
Foobar3

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-02-01 05:33 UTC] aharvey@php.net
-Status: Open +Status: Duplicate
 [2011-02-01 05:33 UTC] aharvey@php.net
Duplicate of request #38025.
 [2011-02-01 08:46 UTC] ivan dot enderlin at hoa-project dot net
But the bug is still open no? And since 2006?
Is this bug affected to someone? Does an explanation of this behavior exist? Please, more details :-).
 [2011-02-01 08:49 UTC] pajoye@php.net
-Status: Duplicate +Status: Open
 [2011-02-01 08:49 UTC] pajoye@php.net
Let keep it open unless the other bug has clear tests cases and similar 
information, which is not the case now.
 [2011-02-01 10:25 UTC] ivan dot enderlin at hoa-project dot net
The bug is open since 2006-07-06, I think that the bug owner is either dead either away :-). Maybe we can focus on this bug which has a detailed example and a detailed description.
What do you think about it :-) ?
 [2011-02-01 13:57 UTC] cataphract@php.net
-Type: Bug +Type: Feature/Change Request
 [2011-02-01 13:57 UTC] cataphract@php.net
This is a feature request because it requires extending the user-land stream wrapper model.

I can take care of it, but I'm more inclined to make it trunk only.
 [2011-02-01 13:57 UTC] cataphract@php.net
-Assigned To: +Assigned To: cataphract
 [2011-02-01 17:52 UTC] ivan dot enderlin at hoa-project dot net
Ok, it's not a bug, it's a missing feature.
So, do you have any workaround to propose me :-p ?

If I can participate to the patch, I'm in.
 [2011-02-17 01:25 UTC] cataphract@php.net
Automatic comment from SVN on behalf of cataphract
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=308410
Log: - Classes that implement stream wrappers can define a method called
  stream_truncate that will respond to truncation, e.g. through ftruncate.
  Closes feature request #53888.
 [2011-02-17 01:25 UTC] cataphract@php.net
-Status: Assigned +Status: Closed
 [2011-02-17 01:25 UTC] cataphract@php.net
Implemented for trunk.
 [2011-02-18 11:45 UTC] ivan dot enderlin at hoa-project dot net
\o/

Thanks.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Mar 19 05:01:29 2024 UTC