Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[2.7][Filesystem] Changed dumpFile to allow dumping to streams... #14754

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Symfony/Component/Filesystem/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

2.7.0
-----

* added tempnam() a stream aware version of PHP's native tempnam()

2.6.0
-----

Expand Down
64 changes: 63 additions & 1 deletion src/Symfony/Component/Filesystem/Filesystem.php
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,53 @@ public function isAbsolutePath($file)
);
}

/**
* Creates a temporary file with support for custom stream wrappers.
*
* @param string $dir The directory where the temporary filename will be created.
* @param string $prefix The prefix of the generated temporary filename.
* Note: Windows uses only the first three characters of prefix.
*
* @return string The new temporary filename (with path), or false on failure.
*/
public function tempnam($dir, $prefix)
{
$limit = 10;
list($scheme, $hierarchy) = $this->getSchemeAndHierarchy($dir);

// If no scheme or scheme is "file" create temp file in local filesystem
if (null === $scheme || 'file' === $scheme) {
$tmpFile = tempnam($hierarchy, $prefix);

// If tempnam failed or no scheme return the filename otherwise prepend the scheme
return false === $tmpFile || null === $scheme ? $tmpFile : $scheme.'://'.$tmpFile;
}

// Loop until we create a valid temp file or have reached $limit attempts
for ($i = 0; $i < $limit; $i++) {

// Create a unique filename
$tmpFile = $dir.'/'.$prefix.uniqid(mt_rand(), true);

// Use fopen instead of file_exists as some streams do not support stat
// Use mode 'x' to atomically check existence and create to avoid a TOCTOU vulnerability
$handle = @fopen($tmpFile, 'x');

// If unsuccessful restart the loop
if (false === $handle) {
continue;
}

// Close the file if it was successfully opened
@fclose($handle);

return $tmpFile;

}

return false;
}

/**
* Atomically dumps content into a file.
*
Expand All @@ -468,7 +515,7 @@ public function dumpFile($filename, $content, $mode = 0666)
throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir);
}

$tmpFile = tempnam($dir, basename($filename));
$tmpFile = $this->tempnam($dir, basename($filename));

if (false === @file_put_contents($tmpFile, $content)) {
throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename);
Expand Down Expand Up @@ -497,4 +544,19 @@ private function toIterator($files)

return $files;
}

/**
* Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> array(file, tmp)).
*
* @param string $filename The filename to be parsed.
*
* @return array The filename scheme and hierarchical part
*/
private function getSchemeAndHierarchy($filename)
{
$components = explode('://', $filename, 2);

return count($components) >= 2 ? array($components[0], $components[1]) : array(null, $components[0]);
}

}
4 changes: 4 additions & 0 deletions src/Symfony/Component/Filesystem/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ $filesystem->rename($origin, $target);

$filesystem->symlink($originDir, $targetDir, $copyOnWindows = false);

$filesystem->tempnam($dir, $prefix);

$filesystem->makePathRelative($endPath, $startPath);

$filesystem->mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array());

$filesystem->isAbsolutePath($file);

$filesystem->dumpFile($file, $content);
```

Resources
Expand Down
134 changes: 134 additions & 0 deletions src/Symfony/Component/Filesystem/Tests/FilesystemTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Filesystem\Tests;

use Symfony\Component\Filesystem\Filesystem;
use Phar;

/**
* Test class for Filesystem.
Expand Down Expand Up @@ -955,6 +956,116 @@ public function providePathsForIsAbsolutePath()
);
}

public function testTempnam()
{
$dirname = $this->workspace;

$filename = $this->filesystem->tempnam($dirname, 'foo');

$this->assertNotFalse($filename);
$this->assertFileExists($filename);
}

public function testTempnamWithFileScheme()
{
$scheme = 'file://';
$dirname = $scheme.$this->workspace;

$filename = $this->filesystem->tempnam($dirname, 'foo');

$this->assertNotFalse($filename);
$this->assertStringStartsWith($scheme, $filename);
$this->assertFileExists($filename);
}

public function testTempnamWithMockScheme()
{
// We avoid autoloading via ClassLoader as stream_wrapper_register creates the object
if (!@include __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR.'MockStream'.DIRECTORY_SEPARATOR.'MockStream.php') {
$this->markTestSkipped('Unable to load mock:// stream.');
}

stream_wrapper_register('mock', 'MockStream\MockStream');

$scheme = 'mock://';
$dirname = $scheme.$this->workspace;

$filename = $this->filesystem->tempnam($dirname, 'foo');

$this->assertNotFalse($filename);
$this->assertStringStartsWith($scheme, $filename);
$this->assertFileExists($filename);
}

public function testTempnamWithZlibSchemeFails()
{
$scheme = 'compress.zlib://';
$dirname = $scheme.$this->workspace;

$filename = $this->filesystem->tempnam($dirname, 'bar');

// The compress.zlib:// stream does not support mode x: creates the file, errors "failed to open stream: operation failed" and returns false
$this->assertFalse($filename);
}

public function testTempnamWithPHPTempSchemeFails()
{
$scheme = 'php://temp';
$dirname = $scheme;

$filename = $this->filesystem->tempnam($dirname, 'bar');

$this->assertNotFalse($filename);
$this->assertStringStartsWith($scheme, $filename);

// The php://temp stream deletes the file after close
$this->assertFileNotExists($filename);
}

public function testTempnamWithPharSchemeFails()
{
// Skip test if Phar disabled phar.readonly must be 0 in php.ini
if (!Phar::canWrite()) {
$this->markTestSkipped('This test cannot run when phar.readonly is 1.');
}

$scheme = 'phar://';
$dirname = $scheme.$this->workspace;
$pharname = 'foo.phar';

$p = new Phar($this->workspace.'/'.$pharname, 0, $pharname);
$filename = $this->filesystem->tempnam($dirname, $pharname.'/bar');

// The phar:// stream does not support mode x: fails to create file, errors "failed to open stream: phar error: "$filename" is not a file in phar "$pharname"" and returns false
$this->assertFalse($filename);
}

public function testTempnamWithHTTPSchemeFails()
{
$scheme = 'http://';
$dirname = $scheme.$this->workspace;

$filename = $this->filesystem->tempnam($dirname, 'bar');

// The http:// scheme is read-only
$this->assertFalse($filename);
}

public function testTempnamOnUnwritableFallsBackToSysTmp()
{
$scheme = 'file://';
$dirname = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'does_not_exist';

$filename = $this->filesystem->tempnam($dirname, 'bar');

$this->assertNotFalse($filename);
$this->assertStringStartsWith(rtrim($scheme.sys_get_temp_dir(), DIRECTORY_SEPARATOR), $filename);
$this->assertFileExists($filename);

// Tear down
unlink($filename);
}

public function testDumpFile()
{
$filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';
Expand Down Expand Up @@ -1009,6 +1120,29 @@ public function testDumpFileOverwritesAnExistingFile()
$this->assertSame('bar', file_get_contents($filename));
}

public function testDumpFileWithFileScheme()
{
$scheme = 'file://';
$filename = $scheme.$this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';

$this->filesystem->dumpFile($filename, 'bar', null);

$this->assertFileExists($filename);
$this->assertSame('bar', file_get_contents($filename));
}

public function testDumpFileWithZlibScheme()
{
$scheme = 'compress.zlib://';
$filename = $this->workspace.DIRECTORY_SEPARATOR.'foo'.DIRECTORY_SEPARATOR.'baz.txt';

$this->filesystem->dumpFile($filename, 'bar', null);

// Zlib stat uses file:// wrapper so remove scheme
$this->assertFileExists(str_replace($scheme, '', $filename));
$this->assertSame('bar', file_get_contents($filename));
}

public function testCopyShouldKeepExecutionPermission()
{
$sourceFilePath = $this->workspace.DIRECTORY_SEPARATOR.'copy_source_file';
Expand Down
Loading