In PHP, how do tmpfile(), tempnam() and sys_get_temp_dir() differ, and why is a temp name built from time() or uniqid() unsafe?
answer
- handle versus path
- tmpfile() is deleted on close
- tempnam() creates the file, mode 0600
- falls back to the system temp dir with E_NOTICE
- sys_temp_dir ini, then TMPDIR
basics
~20 stmpfile() returns an open handle to a file deleted when closed; tempnam() atomically creates a uniquely named 0600 file and returns its path, which you must delete; sys_get_temp_dir() only names the directory. Guessable names can collide or be pre-created by another user.
solid answer
~50 s`tmpfile()` returns a read-write handle (`w+b`) to a uniquely named file that PHP deletes when the handle is closed or the script ends; use it when you only need scratch space through a handle. `tempnam($dir, $prefix)` creates the file itself, with a random name and mode `0600`, and returns its path or `false`; you need that when another program must open the path or you plan to `rename()` it into place, and you must `unlink()` it yourself. If `$dir` is missing or not writable it falls back to the system temp directory with an `E_NOTICE`. `sys_get_temp_dir()` just returns the directory, from the `sys_temp_dir` ini setting or else the `TMPDIR` environment variable. A name like `'/tmp/export_' . time()` collides when two requests run in the same second, and in a shared directory another user can create that name first, maybe as a symlink, before you open it.
go deeper
Recall that tmpfile() returns a self-deleting handle, tempnam() returns a path to a created file, and sys_get_temp_dir() returns a directory.
Explain tempnam()'s 0600 mode, its fallback with an E_NOTICE, and how sys_get_temp_dir() is resolved from sys_temp_dir and TMPDIR.
Show you avoid predictable names in shared directories, clean up in finally, and create temp files on the same file system as their destination.
Decide where temporary data may live per environment, including disk quotas, cleanup of orphans and whether shared /tmp is acceptable at all.
## Three functions, three jobs | Function | Returns | Creates a file? | Who deletes it | Typical use | |---|---|---|---|---| | `tmpfile()` | a stream handle (`w+b`) or `false` | yes | PHP, on `fclose()`, when the handle is released, or at script end | scratch data you only touch through the handle | | `tempnam($dir, $prefix)` | the full path, or `false` | yes, empty, mode `0600` | you, with `unlink()` or by renaming it | a path you hand to another program or move into place | | `sys_get_temp_dir()` | a directory path string | no | nothing to delete | deciding where temporary work goes | ## tmpfile() `tmpfile()` gives you a handle opened in `w+b` mode, so you can write, `rewind()` and read back. The file is removed automatically when the handle is closed, when its last reference goes away, or when the script ends. The manual warns that if the script terminates unexpectedly the file may be left behind, so a temp directory still needs occasional cleanup. Because you never learn the path in a portable way, `tmpfile()` is the wrong tool when another program has to open the file by name. ## tempnam() `tempnam(string $directory, string $prefix): string|false` builds a name from your prefix plus random characters and **creates the file** in one step, so no other process can claim the same name. Details from the manual and php-src: - The file is created with permissions `0600`: readable and writable by the owner only. - Only the first 63 characters of the prefix are used; Windows uses only the first three. - If `$directory` does not exist or is not writable, PHP falls back to the system temporary directory and emits an `E_NOTICE`, *file created in the system's temporary directory*. Code that assumed its own directory can end up writing somewhere else, so check `dirname()` of the result if the location matters. - The file stays until you delete or move it. The `0600` mode matters when you later `rename()` the file into a public directory: the web server or another user may not be able to read it until you `chmod()` it. ## sys_get_temp_dir() It returns the directory PHP uses for temporary files. The lookup order in php-src is: 1. the `sys_temp_dir` ini setting, if set (it is commented out in the shipped php.ini files); 2. on Unix, the `TMPDIR` environment variable; 3. otherwise a platform default such as `/tmp`. PHP-FPM clears the worker environment by default (the pool's `clear_env` setting), so `TMPDIR` set in your shell may not reach the worker; `sys_temp_dir` is the more reliable knob. ## Why guessable names are unsafe ```php <?php // Unsafe: predictable and not created atomically $path = sys_get_temp_dir() . '/export_' . time() . '.csv'; file_put_contents($path, $csv); ``` Problems with this pattern: - **Collisions.** Two requests in the same second get the same name and overwrite each other. `uniqid()` is based on the current time too, so it narrows the window without closing it. - **Pre-creation.** In a directory shared with other users, such as `/tmp`, another local user can create that name first, for instance as a symlink to a file you can write, and your `file_put_contents()` follows it. - **Check-then-create races.** Adding `if (!file_exists($path))` does not help, because the check and the write are two separate steps. The safe versions create the file atomically with an unpredictable name: ```php <?php declare(strict_types=1); $path = tempnam(sys_get_temp_dir(), 'export_'); if ($path === false) { throw new RuntimeException('Cannot create temp file'); } try { file_put_contents($path, $csv); // hand $path to another program, or rename() it into place } finally { if (is_file($path)) { unlink($path); } } ``` ## Choosing - Scratch data used only through a handle: `tmpfile()`. - A path another program must read, or a file you will `rename()` into its final place: `tempnam()` in the target file system, then `chmod()` if others must read it. - Never build temp names from `time()`, `uniqid()` or a counter in a shared directory.
- Why should the temp file for a rename-into-place be created in the destination directory rather than in sys_get_temp_dir()?`rename()` is only an atomic replace within one file system. If the temp directory is on a different mount, PHP falls back to copying the data into the destination path and deleting the source, so readers can see a half-written file. Creating the temp file with `tempnam()` next to the destination keeps the final `rename()` atomic.
- What happens when tempnam() is given a directory that does not exist?It does not fail right away: it creates the file in the system's temporary directory instead, emits an `E_NOTICE` saying so, and returns that path. Code that assumes the file is in its own directory can then move or serve the wrong location, so check the directory first or compare `dirname()` of the result.
saying these in an interview costs you the question
- tempnam() only returns a unique name; you still have to create the file.
- Files from tempnam() are deleted automatically when the script ends.
- uniqid() makes a temp filename unpredictable and collision-free.
- tmpfile() is the right choice when a shell command must open the file by path.
- sys_get_temp_dir() always returns /tmp on Linux.