2
votes

Using file_put_contents to create and populate a file in a relatively located folder.

Noticing some odd quirks related to include paths and file creation.

The Short Version: looking at Iteration 2 and Iteration 3. Why does the latter work while the former fails?

Iteration 1

file_put_contents('../../public/remixes/screenshots/test.txt', $data);

This works just fine.

Iteration 2

But, I have the public directory in this module's "root" and want to reference it more directly using pre-set include paths. For example:

echo(get_include_path()); // this outputs "../../"
file_put_contents('public/remixes/screenshots/test.txt', $data, FILE_USE_INCLUDE_PATH);

ERROR: "failed to open stream: No such file or directory"

Iteration 3

Fine, PHP include paths are wonky, so let's do weird things and see what happens. I'll try using fopen to actually create the file first.

fopen('../../public/remixes/screenshots/test.txt', 'w');
file_put_contents('public/remixes/screenshots/test.txt', $data, FILE_USE_INCLUDE_PATH);

That works again... WAT?

Iteration 4

Interesting; I'm OK with doing an fopen first but I want it to use the include path too:

fopen('public/remixes/screenshots/test.txt', 'w', 1);
file_put_contents('public/remixes/screenshots/test.txt', $data, FILE_USE_INCLUDE_PATH);

This yields TWO "failed to open stream" errors. It's getting worse.

Iteration 5

I'll just spare myself the include path "magic" and build the path with it directly

file_put_contents(get_include_path().'public/remixes/screenshots/test.txt', $data);

This works, and is the best I can get.

Conclusion

I'm left wondering why Iteration 2 fails while Iteration 3 succeeds.

Iteration 4 implies that it is a problem with fopen (since file_put_contents is apparently just a wrapper for fopen / fwrite / fclose)

Thoughts?

1
instead of using the flag, what happens if you just use 1? It could be that the flag isn't set. - kennypu
Alas -- no difference; although note that iteration 4 fails on fopen without flags. - slifty
Check out this comment. Also, in the PHP official document, it says that when use_inlude_path is specified, it "search for the file in the include_path", so it looks like the doc is quite literal-accurate... - Passerby

1 Answers

0
votes

Just spent some time inspecting the PHP source.

fopen resolves to line 2007 of /main/streams/streams.c

I'll spare you the stack that gets to that point.

The following portion of the code (line 2026) handles the FILE_USE_INCLUDE_PATH parameter:

if (options & USE_PATH) {
    resolved_path = zend_resolve_path(path, strlen(path) TSRMLS_CC);
    if (resolved_path) {
        path = resolved_path;
        /* we've found this file, don't re-check include_path or run realpath */
        options |= STREAM_ASSUME_REALPATH;
        options &= ~USE_PATH;
    }
}

Indeed, as Passerby noted in his comment this logic will attempt to resolve the path. If the path resolves properly (to an existing file) the original path string is replaced with the "include-ified" path.

If the include-ified path doesn't point to an existing file then the original path is not replace, and the include_path gets ignored completely.

The documentation doesn't make it obvious that this is expected behavior, but as has been noted in the comments to this answer it isn't that crazy.

Mystery solved.