Skip to content

[Bug]: The file:// stream wrapper changes plain file I/O: feof() is true one read early, and PharData emits a warning #102

Closed

Description

@fdaviddpt

Bug report

While TypePHP is booted, every fopen() / fgets() / feof() in the process goes through TypePHP\Internal\Io\StreamWrapper, including code that is not checked (no include match). Two behaviours differ from PHP's native stream:

1. feof() turns true one read early. The classic read loop loses its last iteration.

run.php (outside src/, so not even rewritten):

<?php

require __DIR__ . "/vendor/autoload.php";

$path = sys_get_temp_dir() . "/typephp-feof.txt";
file_put_contents($path, "a\nb\n");
$handle = fopen($path, "r");
$reads = [];
while (!feof($handle)) {
    $reads[] = fgets($handle);
}
fclose($handle);
var_export($reads);
Variant Result
TypePHP booted ['a' . PHP_EOL, 'b' . PHP_EOL] (2 reads)
TYPEPHP_DISABLE=1 ['a' . PHP_EOL, 'b' . PHP_EOL, false] (3 reads)
plain php -r, no autoload 3 reads

Native PHP only reports EOF after a read has hit the end, so the third fgets() happens and returns false. Code that counts lines this way, or that tails the last N lines of a file, gets a different answer when TypePHP is active (we have a component that shows the last N lines of a log: it returns a different window under TypePHP).

2. Creating an archive with PharData emits a warning.

$tar = new PharData($dir . "/archive.tar");
$tar->addFile($dir . "/a.txt", "a.txt");
$tar->compress(Phar::GZ);

The archive is created correctly, but TypePHP adds:

Warning: fopen(.../archive.tar): Failed to open stream: No such file or directory in .../vendor/typephp/typephp/src/Internal/Io/StreamWrapper.php on line 397

With TYPEPHP_DISABLE=1 there is no warning. PHPUnit marks such tests risky, and any setup that treats PHP warnings as failures fails.

Both tested on v0.10.26, main (88fcb12) and next-minor-released (f617372, PR #100), PHP 8.2, type cache cleared before each run. @fopen() on a missing file is correctly silent, so that part of the wrapper is fine.

Expected output

File I/O through the wrapper behaves like the native plainfiles stream: feof() only true after a read reached the end, and no extra warning when a phar archive is created. Ideally the wrapper only handles the files it actually rewrites (includes matching the config) and leaves other fopen() calls to the native stream.

Context: same codebase as #93 / #99 / #101; this one changes test results without any TypeError, which makes it hard to spot.

Activity

  1. fdaviddpt commented on Oct 4, 2026

    @fdaviddpt
    Author

    This one cannot be reproduced in the playground: both snippets behave natively there (feof() loop gets 3 reads, PharData prints no warning), because the playground does not go through TypePHP\Internal\Io\StreamWrapper. It needs a Composer install with TypePHP booted, as in the steps above.

  2. linked a pull request that will close this issueNext minor released #100on Oct 4, 2026
  3. self-assigned this
    on Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingnext minor releasefeature for next minor release

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions