PHP Conference Japan 2024

tempnam

(PHP 4, PHP 5, PHP 7, PHP 8)

tempnam创建具有唯一文件名的文件

说明

tempnam(string $directory, string $prefix): string|false

在指定目录中创建一个具有唯一文件名且访问权限设置为 0600 的文件。如果目录不存在或不可写,tempnam() 可能会在系统的临时目录中生成一个文件,并返回该文件的完整路径,包括文件名。

参数

directory

创建临时文件的目录。

prefix

生成的临时文件名的前缀。

注意: 仅使用前缀的前 63 个字符,其余部分将被忽略。Windows 仅使用前缀的前三个字符。

返回值

返回新的临时文件名(包含路径),失败时返回 false

更新日志

版本 说明
7.1.0 当回退到系统的临时目录时,tempnam() 现在会发出通知。

示例

示例 #1 tempnam() 示例

<?php
$tmpfname
= tempnam("/tmp", "FOO");

$handle = fopen($tmpfname, "w");
fwrite($handle, "写入临时文件");
fclose($handle);

// 在这里做一些事情

unlink($tmpfname);
?>

注释

注意: 如果 PHP 无法在指定的 directory 参数中创建文件,它将回退到系统默认值。在 NTFS 上,如果指定的 directory 包含超过 65534 个文件,也会发生这种情况。

参见

添加注释

用户贡献的注释 22 条

up
78
koyama
15 年前
注意使用空 $dir 作为在系统临时目录中创建临时文件的“技巧”。

<?php
$tmpfname
= tempnam('', 'FOO'); // 不好
?>

如果 open_basedir 限制生效,该技巧将不起作用。你会收到类似以下的警告消息:

Warning: tempnam() [function.tempnam]: open_basedir restriction in effect.
File() is not within the allowed path(s): (/var/www/vhosts/example.com/httpdocs:/tmp)

有效的方法是:

<?php
$tmpfname
= tempnam(sys_get_temp_dir(), 'FOO'); // 好
?>
up
25
Sebastian Kun
19 年前
如果你查看 C 函数 tempnam(3) 的 Linux 手册页,你会在最后看到“永远不要使用此函数。请改用 mkstemp(3)。”但是 php 的 tempnam() 函数实际上并不使用 tmpnam(3),所以没有问题(在 Linux 下,如果可用,它将使用 mkstemp(3))。
up
15
php at REMOVEMEkennel17 dot co dot uk
19 年前
请注意,tempnam 返回临时文件的完整路径,而不仅仅是文件名。
up
15
stanislav dot eckert at vizson dot de
7 年前
请注意,此函数可能会在 PHP 7.1.0 及更高版本中引发通知。这是一个错误修复:https://bugs.php.net/bug.php?id=69489

你可以添加一个地址运算符 (@) 来消除通知

<?php

if ($tmp = @tempnam() !== false) {
// ...
}

?>

或者你可以尝试在 php.ini 中将 "upload_tmp_dir" 设置为系统的临时文件夹路径。不确定最后一个是否可以防止通知。
up
7
Jason Pell
17 年前
我想保证文件将在指定目录中创建,否则函数应该返回 FALSE,我有一个简单的函数可以工作,但我不确定它是否存在潜在的安全问题。

function dir_tempnam($dir, $prefix)
{
$real_dir_path = realpath($dir);
if (substr($real_dir_path, -1) != '/')
$real_dir_path .= '/';

$tempfile = tempnam($real_dir_path, $prefix);
$name = basename($tempfile);

if(is_file($real_dir_path.$name))
return $name;
else
{
@unlink($name);
return FALSE;
}
}

此函数仅返回指定目录中临时文件的名称,或 FALSE。

显然它可以返回整个 $tempfile,但在我的情况下,我实际上想要单独的 basename 值。
up
13
Artur Graniszewski
14 年前
tempnam() 函数不支持 stream_register_wrapper() 注册的自定义流包装器。

例如,如果你尝试在 Windows 平台上使用 tempnam(),PHP 将尝试在 %TMP% 文件夹(通常是:C:\WINDOWS\Temp)中生成唯一文件名,而不会发出任何警告或通知。

<?php

// << ...自定义流包装器放在这里...>>

echo '<pre>';
error_reporting(E_ALL);
ini_set('display_errors', true);
clearstatcache();
stream_register_wrapper('test', 'MemoryStream');

mkdir('test://aaa');
mkdir('test://aaa/cc');
mkdir('test://aaa/dd');
echo
'PHP '.PHP_VERSION;
echo
'<br />节点存在: '.file_exists('test://aaa/cc');
echo
'<br />节点可写: '.is_writable('test://aaa/cc');
echo
'<br />节点是目录: '.is_dir('test://aaa/cc');
echo
'<br />目录中的临时名称: '.tempnam('test://aaa/cc', 'tmp');
echo
"<br /></pre>";

?>

输出
--------------------
PHP 5.2.13
节点存在: 1
节点可写: 1
节点是目录: 1
目录中的临时名称: C:\Windows\Temp\tmp1D03.tmp

如果要创建临时文件,则必须创建自己的函数(该函数可能会使用 opendir() 和 fopen($filename, "x") 函数)
up
6
anakin dot skyw at gmx dot de
20 年前
>在 UNIX 下(您可以重命名到现有文件,所以我使用了链接),您将必须同时删除链接和链接的目标。

你不能这样做吗?
<?php
if ($newFileCreated) {
unlink ($sysFileName);
return
$newFileName;
}
?>
并获得与 Windows 版本相同的语义?
up
9
Ron Korving
18 年前
此函数创建一个临时目录。如果 unlink() 和 mkdir() 之间某些进程创建了相同的目录或文件,则先前给出的示例可能会出现错误。此实现也更快。

<?php
function tempdir($dir, $prefix='', $mode=0700)
{
if (
substr($dir, -1) != '/') $dir .= '/';

do
{
$path = $dir.$prefix.mt_rand(0, 9999999);
} while (!
mkdir($path, $mode));

return
$path;
}
?>
up
8
bishop
20 年前
在动态网站上,通常需要使用特定扩展名创建临时文件。很大程度上,这种需求源于 Microsoft 浏览器根据文件的扩展名识别下载文件的 MIME 类型。

没有一个 PHP 函数可以使用特定扩展名创建临时文件名,并且,如已经显示的那样,除非您使用 PHP 原子原语,否则会涉及到竞争条件。

我在下面仅使用原语,并利用 OS 依赖行为来安全地创建具有特定后缀、前缀和目录的文件。请享用。

<?php
function secure_tmpname($postfix = '.tmp', $prefix = 'tmp', $dir = null) {
// 验证参数
if (! (isset($postfix) && is_string($postfix))) {
return
false;
}
if (! (isset(
$prefix) && is_string($prefix))) {
return
false;
}
if (! isset(
$dir)) {
$dir = getcwd();
}

// 查找临时名称
$tries = 1;
do {
// 获取一个已知的、唯一的临时文件名
$sysFileName = tempnam($dir, $prefix);
if (
$sysFileName === false) {
return
false;
}

// 附加扩展名
$newFileName = $sysFileName . $postfix;
if (
$sysFileName == $newFileName) {
return
$sysFileName;
}

// 移动或指向已创建的临时文件到新的文件名
// 注意:如果新文件名存在,这些操作将失败
$newFileCreated = (isWindows() ? @rename($sysFileName, $newFileName) : @link($sysFileName, $newFileName));
if (
$newFileCreated) {
return
$newFileName;
}

unlink ($sysFileName);
$tries++;
} while (
$tries <= 5);

return
false;
}
?>

isWindows 函数主要留给读者作为练习。一个起点如下

<?php
function isWindows() {
return (
DIRECTORY_SEPARATOR == '\\' ? true : false);
}
?>

与 tempnam() 一样,此函数要求您稍后自行清理文件。在 UNIX 下(您可以重命名到现有文件,所以我使用了链接),您将必须同时删除链接和链接的目标。清理工作完全留给读者。
up
8
divinity76+yaoiporn at gmail dot com
11 年前
如果您不想自己删除文件,并且不需要自定义前缀,则可以使用
$file_location=stream_get_meta_data(tmpfile())['uri'];
文件将被自动创建,并在脚本关闭时自动删除(感谢 tmpfile())我发现这对于 CURLOPT_COOKIEFILE 很有用(它需要文件位置,而不是句柄)
up
6
wapmorgan
10 年前
请注意,如果没有传输第二个参数 <prefix>,tempnam 将返回 NULL(不是 false)
<?php
var_dump
(tempnam(sys_get_temp_dir())); // NULL
?>
还会生成警告
警告:tempnam() 预期正好有 2 个参数,在 php shell 代码中给出了 1 个参数 ...
up
4
tux ARROBA cenobioracing PUNTO com
18 年前
请注意,在 Windows NT 和其他 Windows 上,如果您有一个变量 $work_dir,例如,它具有指向您的文档根目录(或任何其他目录)中某个目录的路径。请注意以下事项
<?php
$work_dir
= 'C:/some/path/to/document_root/dir';
file_exists($working_dir); // 返回 true
is_writable($working_dir); // 返回 true
$tempfile = tempnam($working_dir,'img');
//$temfile 现在包含一个系统范围的临时目录文件,例如 'C:/WINNT.SBS/img444.tmp',而不是我们传递给它的目录
//这是因为我们需要为 $working_dir 提供 I_USR(IIS 用户)用户写权限,尽管根据上述函数,它似乎已经拥有了该权限...
//如果您只想使用 tempnam 默认返回的系统范围的临时目录,您还需要为 I_USR 用户提供写权限才能写入该文件...
?>
up
5
tomas at slax dot org
14 年前
注意:函数不是原子的。如果许多进程同时调用同一个函数,您可能会遇到意外行为。

如果您需要自己的 tempnam 变体,请使用类似这样的内容

<?php
function tempnam_sfx($path, $suffix)
{
do
{
$file = $path."/".mt_rand().$suffix;
$fp = @fopen($file, 'x');
}
while(!
$fp);

fclose($fp);
return
$file;
}

// 可以像这样调用:
$file = tempnam_sfx("/tmp", ".jpg");
?>

如果需要,您可以用其他随机名称生成器替换 mt_rand()。
up
4
lreilly at lanl dot gov
22 年前
请注意正斜杠和反斜杠的使用。像这样的看似无害的代码……

$uploaddir = "C:/Program Files/Apache Group/Apache2/htdocs/sasdap/uploads/";
$tempFile = tempnam ($uploaddir, "TMPANAL");
$fp = fopen($tmpfname, "w");
fwrite($fp, $iqdata);
//fclose($fp);

……在回显 $tempFile 时可能会显示一些奇怪的内容;

例如:/Program Files/Apache Group/Apache2/htdocs/sasdap/uploads/\TMP3D.tmp

必须……记住……要……使用……反斜杠……

- Lee P. Reilly
up
2
匿名用户
12 年前
tempnam 不会在未经授权的区域创建文件。
这意味着您需要具有临时目录 ($dir) 的访问权限才能在那里创建文件。
up
3
phpdoc at rickbradley dot com
21 年前
下面提供的“newtempnam”配方(由“tempnam”于“2003 年 7 月 23 日 08:56”发布)至少有一个竞态条件。while 循环检查以确保所讨论的文件不存在,然后去创建文件。在存在性测试和 fopen() 调用之间,攻击者有机会创建所讨论的文件。

这是一个典型的竞态条件,虽然它似乎难以利用,但针对这种草率的文件创建方式存在许多众所周知的攻击。

在 PHP 的语言级别上,实现安全文件创建所需的原子原语不可用。这进一步强调了 PHP 语言开发人员需要依赖语言的安全原语(包括 tempnam() 和 tempfile()),而不是自行实现。
up
3
dmhouse at gmail dot com
17 年前
Guillaume Paramelle 下面的评论值得强调:tempnam() 不接受其第一个目录的相对路径。如果您传递了一个相对路径,它将(至少在 Windows XP 上)在系统临时目录中创建临时文件。

将相对路径转换为绝对路径的最简单方法是在前面加上 getcwd()

<?php
$file
= tempnam('files/temp', 'tmp'); // 错误!
$file = tempnam(getcwd() . 'files/tmp', 'tmp') // 正确。
?>
up
1
david at frankieandshadow dot com
6 年前
Debian 9 Stretch 添加了私有临时目录,因此如果您在 Apache 中以 mod_php 运行时使用此函数,期望该文件可用于第三方(例如,在 shell 命令或 MariaDB 从临时文件加载命令中),它将不再起作用:该文件将对其他进程不可见(尽管相同的 PHP 能够将其读回)。

要么您需要使用第一个参数将临时文件放在特定位置(HTML 文档树之外),并具有可以与其他方共享它的权限,要么更改私有 tmp 的设置,例如

cp /lib/systemd/system/apache2.service /etc/systemd/system/
# 编辑 /etc/systemd/system/ 将 PrivateTmp=true 改为 false
systemctl daemon-reload
service apache2 restart

在共享服务器上选择不同的位置更好 - 添加私有 tmp 文件是有原因的。

这是我在最近的操作系统升级中困惑了很久的事情。
up
2
hm2k at php dot net
15 年前
<?php

function tempdir($dir=false,$prefix='php') {
$tempfile=tempnam('','');
if (
file_exists($tempfile)) { unlink($tempfile); }
mkdir($tempfile);
if (
is_dir($tempfile)) { return $tempfile; }
}

/*示例*/

echo tempdir();
// 返回:/tmp/8e9MLi

?>
up
1
anthon at piwik dot org
14 年前
tempnam() 创建的文件的文件权限将反映应用于默认值的当前 umask(例如,0600 或 -rw-------)。无论 umask 是在启动 Web 服务器进程之前设置的,还是由之前对 PHP 的 umask() 函数的调用设置的,都是这种情况。

例如,如果当前 umask 为 0022,则临时文件将以权限 0600(所有者可读/写)创建。

此外,如果当前 umask 为 0222,则临时文件将以权限 0400(所有者只读)创建。(如果您的代码随后尝试打开临时文件进行写入,这将有问题。)

重要的是要记住 umask 会撤销权限。在上述两个示例中,均未设置组、其他或执行权限。

参见:umask()、chmod()。
up
-3
KOmaSHOOTER at gmx dot de
19 年前
此示例创建一个名为“user.txt”的文件
在目录 www.XXXXX.XX/restricted/ 中
<?php
$tmpfname
= tempnam($_ENV["DOCUMENT_ROOT"]."/restricted", "FOO");
$handle = fopen($tmpfname, "w");
fwrite($handle, "写入临时文件");
fclose($handle);

// 在这里做一些事情
//echo($_ENV["DOCUMENT_ROOT"]);
copy($tmpfname,'user.txt');
?>
up
-3
Nick Smith
19 年前
值得注意的是,如果您提供的“dir”不存在,那么它将被静默忽略并使用系统 /tmp 目录。至少在 Linux、PHP v4.1.2 下是这样。

我有一个脚本,在安全模式关闭时似乎可以正常工作,但我没有意识到我的“dir”参数有一个拼写错误(所以文件进入了 /tmp),一旦安全模式打开,我就开始收到错误,因为脚本的其余部分无法从系统 /tmp 文件夹中读取文件。
To Top