I have a script that checks a zipfile containing a number of matching PDF+textfiles. I want to unpack, or somehow read the textfiles from the zipfile, and just pick out some
I wanted to add a refinement to @Mario Mueller's answer, as his is subject to possible race conditions, however I believe the following should not be:
function tempdir(int $mode = 0700): string {
do { $tmp = sys_get_temp_dir() . '/' . mt_rand(); }
while (!@mkdir($tmp, $mode));
return $tmp;
}
This works because mkdir
returns false
if $tmp
already exists, causing the loop to repeat and try another name.
Note also that I've added handling for $mode
, with a default that ensures the directory is accessible to the current user only, as mkdir
's default is 0777
otherwise.
It is strongly advised that you use a shutdown function to ensure the directory is removed when no longer needed, even if your script exits by unexpected means*. To facilitate this, the full function that I use does this automatically unless the $auto_delete
argument is set to false
.
// Deletes a non-empty directory
function destroydir(string $dir): bool {
if (!is_dir($dir)) { return false; }
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
if (is_dir("$dir/$file")) { destroydir("$dir/$file"); }
else { unlink("$dir/$file"); }
}
return rmdir($dir);
}
function tempdir(int $mode = 0700, bool $auto_delete = true): string {
do { $tmp = sys_get_temp_dir() . '/' . mt_rand(); }
while (!@mkdir($tmp, $mode));
if ($auto_delete) {
register_shutdown_function(function() use ($tmp) { destroydir($tmp); });
}
return $tmp;
}
This means that by default any temporary directory created by tempdir()
will have permissions of 0700
and will be automatically deleted (along with its contents) when your script ends.
NOTE: *This may not be the case if the script is killed, for this you might need to look into registering a signal handler as well.
Another possibility is to use the temporal file as a kind of semaphore to guarantee the unicity of the directory name. Then, create a directory whose name is based on the file name.
define ('TMP_DIR', '/tmp'); // sys_get_temp_dir() PHP 5 >= 5.2.1
define ('TMP_DIR_PREFIX', 'tmpdir_');
define ('TMP_DIR_SUFFIX', '.d');
/* ************************************************************************** */
function createTmpDir() {
$tmpFile = tempnam(TMP_DIR, TMP_DIR_PREFIX);
$tmpDir = $tmpFile.TMP_DIR_SUFFIX;
mkdir($tmpDir);
return $tmpDir;
}
function rmTmpDir($tmpDir) {
$offsetSuffix = -1 * strlen(TMP_DIR_SUFFIX);
assert(strcmp(substr($tmpDir, $offsetSuffix), TMP_DIR_SUFFIX) === 0);
$tmpFile = substr($tmpDir, 0, $offsetSuffix);
// Removes non-empty directory
$command = "rm -rf $tmpDir/";
exec($command);
// rmdir($tmpDir);
unlink($tmpFile);
}
/* ************************************************************************** */
There are a lot of overkill answers to this question. One simple answer would be:
$tempdir = tempnam(sys_get_temp_dir()) . 'dir';
mkdir($tempdir);
So I first found a post by Ron Korving on PHP.net, which I then modified to make a bit safer (from endless loops, invalid characters, and unwritable parent dirs) and use a bit more entropy.
<?php
/**
* Creates a random unique temporary directory, with specified parameters,
* that does not already exist (like tempnam(), but for dirs).
*
* Created dir will begin with the specified prefix, followed by random
* numbers.
*
* @link https://php.net/manual/en/function.tempnam.php
*
* @param string|null $dir Base directory under which to create temp dir.
* If null, the default system temp dir (sys_get_temp_dir()) will be
* used.
* @param string $prefix String with which to prefix created dirs.
* @param int $mode Octal file permission mask for the newly-created dir.
* Should begin with a 0.
* @param int $maxAttempts Maximum attempts before giving up (to prevent
* endless loops).
* @return string|bool Full path to newly-created dir, or false on failure.
*/
function tempdir($dir = null, $prefix = 'tmp_', $mode = 0700, $maxAttempts = 1000)
{
/* Use the system temp dir by default. */
if (is_null($dir))
{
$dir = sys_get_temp_dir();
}
/* Trim trailing slashes from $dir. */
$dir = rtrim($dir, DIRECTORY_SEPARATOR);
/* If we don't have permission to create a directory, fail, otherwise we will
* be stuck in an endless loop.
*/
if (!is_dir($dir) || !is_writable($dir))
{
return false;
}
/* Make sure characters in prefix are safe. */
if (strpbrk($prefix, '\\/:*?"<>|') !== false)
{
return false;
}
/* Attempt to create a random directory until it works. Abort if we reach
* $maxAttempts. Something screwy could be happening with the filesystem
* and our loop could otherwise become endless.
*/
$attempts = 0;
do
{
$path = sprintf('%s%s%s%s', $dir, DIRECTORY_SEPARATOR, $prefix, mt_rand(100000, mt_getrandmax()));
} while (
!mkdir($path, $mode) &&
$attempts++ < $maxAttempts
);
return $path;
}
?>
So, let's try it out:
<?php
echo "\n";
$dir1 = tempdir();
echo $dir1, "\n";
var_dump(is_dir($dir1), is_writable($dir1));
var_dump(rmdir($dir1));
echo "\n";
$dir2 = tempdir('/tmp', 'stack_');
echo $dir2, "\n";
var_dump(is_dir($dir2), is_writable($dir2));
var_dump(rmdir($dir2));
echo "\n";
$dir3 = tempdir(null, 'stack_');
echo $dir3, "\n";
var_dump(is_dir($dir3), is_writable($dir3));
var_dump(rmdir($dir3));
?>
Result:
/var/folders/v4/647wm24x2ysdjwx6z_f07_kw0000gp/T/tmp_900342820
bool(true)
bool(true)
bool(true)
/tmp/stack_1102047767
bool(true)
bool(true)
bool(true)
/var/folders/v4/647wm24x2ysdjwx6z_f07_kw0000gp/T/stack_638989419
bool(true)
bool(true)
bool(true)
The "mkdir" function raises a warning if the directory already exists, so you can catch this using "@mkdir" and avoid any race condition:
function tempDir($parent = null)
{
// Prechecks
if ($parent === null) {
$parent = sys_get_temp_dir();
}
$parent = rtrim($parent, '/');
if (!is_dir($parent) || !is_writeable($parent)) {
throw new Exception(sprintf('Parent directory is not writable: %s', $parent));
}
// Create directory
do {
$directory = $parent . '/' . mt_rand();
$success = @mkdir($directory);
}
while (!$success);
return $directory;
}
quite easy (I took partly it from the PHP manual):
<?php
function tempdir() {
$tempfile=tempnam(sys_get_temp_dir(),'');
// you might want to reconsider this line when using this snippet.
// it "could" clash with an existing directory and this line will
// try to delete the existing one. Handle with caution.
if (file_exists($tempfile)) { unlink($tempfile); }
mkdir($tempfile);
if (is_dir($tempfile)) { return $tempfile; }
}
/*example*/
echo tempdir();
// returns: /tmp/8e9MLi
See: https://www.php.net/manual/en/function.tempnam.php
Please look at Will's solution below.
=> My answer should not be the accepted answer anymore.