diff --git a/src/Node/Directory.php b/src/Node/Directory.php index 57ba7ef..34981d5 100644 --- a/src/Node/Directory.php +++ b/src/Node/Directory.php @@ -33,8 +33,8 @@ class Directory implements NodeContainerInterface $this->mode = self::TYPE_BLOCK & self::TYPE_DIR; $this->dateAccessed = new DateTime(); - $this->dateCreated = new DateTime(); - $this->dateModified = new DateTime(); + $this->dateCreated = $this->dateAccessed; + $this->dateModified = $this->dateAccessed; foreach ($nodes as $name => $node) { $this->add($name, $node); @@ -108,6 +108,14 @@ class Directory implements NodeContainerInterface return $this->dateAccessed; } + /** + * @param DateTime $dateTime + */ + public function setDateAccessed(DateTime $dateTime) + { + $this->dateAccessed = $dateTime; + } + /** * {@inheritdoc} */ @@ -124,6 +132,14 @@ class Directory implements NodeContainerInterface return $this->dateModified; } + /** + * @param DateTime $dateTime + */ + public function setDateModified(DateTime $dateTime) + { + $this->dateModified = $dateTime; + } + /** * {@inheritdoc} */ diff --git a/src/Node/File.php b/src/Node/File.php index 5b37ded..342f066 100644 --- a/src/Node/File.php +++ b/src/Node/File.php @@ -28,8 +28,8 @@ class File implements FileInterface $this->mode = self::TYPE_BLOCK & self::TYPE_FILE; $this->dateAccessed = new DateTime(); - $this->dateCreated = new DateTime(); - $this->dateModified = new DateTime(); + $this->dateCreated = clone $this->dateAccessed; + $this->dateModified = clone $this->dateAccessed; } /** @@ -56,6 +56,14 @@ class File implements FileInterface return $this->dateAccessed; } + /** + * @param DateTime $dateTime + */ + public function setDateAccessed(DateTime $dateTime) + { + $this->dateAccessed = $dateTime; + } + /** * {@inheritdoc} */ @@ -72,6 +80,14 @@ class File implements FileInterface return $this->dateModified; } + /** + * @param DateTime $dateTime + */ + public function setDateModified(DateTime $dateTime) + { + $this->dateModified = $dateTime; + } + /** * {@inheritdoc} */ diff --git a/src/Node/NodeInterface.php b/src/Node/NodeInterface.php index c6be255..03c4166 100644 --- a/src/Node/NodeInterface.php +++ b/src/Node/NodeInterface.php @@ -18,6 +18,11 @@ interface NodeInterface extends StatInterface */ public function getDateAccessed(); + /** + * @param DateTime $dateTime + */ + public function setDateAccessed(DateTime $dateTime); + /** * @return DateTime */ @@ -28,6 +33,11 @@ interface NodeInterface extends StatInterface */ public function getDateModified(); + /** + * @param DateTime $dateTime + */ + public function setDateModified(DateTime $dateTime); + /** * @return integer */ diff --git a/src/Stream/FileHandle.php b/src/Stream/FileHandle.php index c3d739f..1858158 100644 --- a/src/Stream/FileHandle.php +++ b/src/Stream/FileHandle.php @@ -9,6 +9,7 @@ */ namespace Vfs\Stream; +use DateTime; use Vfs\Exception\UnopenedHandleException; use Vfs\Node\FileInterface; use Vfs\Node\NodeContainerInterface; @@ -89,6 +90,28 @@ class FileHandle extends AbstractHandle return substr($this->node->getContent(), $offset); } + /** + * @param DateTime $mtime + * @param DateTime $atime + * @return NodeInterface + */ + public function touch(DateTime $mtime = null, DateTime $atime = null) + { + $node = $this->findOrBuildNode(); + + if (!$node) { + throw new UnopenedHandleException($this, $this->url); + } + + $mtime = $mtime ?: new DateTime(); + $atime = $atime ?: clone $mtime; + + $node->setDateAccessed($atime); + $node->setDateModified($mtime); + + return $node; + } + /** * @param string $content * @return boolean diff --git a/src/Stream/StreamWrapper.php b/src/Stream/StreamWrapper.php index 7bd389e..c1709af 100644 --- a/src/Stream/StreamWrapper.php +++ b/src/Stream/StreamWrapper.php @@ -9,6 +9,7 @@ */ namespace Vfs\Stream; +use DateTime; use Vfs\Node\LinkInterface; use Vfs\FileSystemRegistry; @@ -132,6 +133,28 @@ class StreamWrapper return true; // Non-buffered writing } + /** + * @param string $url + * @param integer $option + * @param mixed $args + * @return boolean + */ + public function stream_metadata($url, $option, $args) + { + if (STREAM_META_TOUCH === $option) { + $this->handle = $this->buildFileHandle($url, HandleInterface::MODE_WRITE_NEW); + + $mtime = isset($args[0]) ? new DateTime(sprintf('@%s', $args[0])) : null; + $atime = isset($args[1]) ? new DateTime(sprintf('@%s', $args[1])) : $mtime; + + $this->handle->touch($mtime, $atime); + + return true; + } + + return false; + } + /** * @param string $url * @param string $mode diff --git a/test/unit/Stream/FileHandleTest.php b/test/unit/Stream/FileHandleTest.php index fe8cf6d..60214bc 100644 --- a/test/unit/Stream/FileHandleTest.php +++ b/test/unit/Stream/FileHandleTest.php @@ -44,6 +44,15 @@ class FileHandleTest extends UnitTestCase ]; } + public function dataTouch() + { + return [ + ['a'], ['ab'], ['a+'], ['at'], + ['w'], ['wb'], ['w+'], ['wt'], + ['c'], ['cb'], ['c+'], ['ct'] + ]; + } + public function testInterface() { $handle = new FileHandle($this->fs, ''); @@ -261,4 +270,52 @@ class FileHandleTest extends UnitTestCase $handle->write(''); } + + /** + * @dataProvider dataTouch + */ + public function testTouch($mode) + { + $handle = new FileHandle($this->fs, 'foo://bar', $mode); + $atime = Mockery::mock('DateTime'); + $mtime = Mockery::mock('DateTime'); + + $file = Mockery::mock('Vfs\Node\FileInterface'); + $file->shouldReceive('setDateAccessed')->once()->with($atime); + $file->shouldReceive('setDateModified')->once()->with($mtime); + + $factory = Mockery::mock('Vfs\Node\Factory\NodeFactoryInterface'); + $factory->shouldReceive('buildFile')->once()->withNoArgs()->andReturn($file); + + $root = Mockery::mock('Vfs\Node\NodeContainerInterface'); + $root->shouldReceive('set')->once()->with('bar', $file); + + $this->fs->shouldReceive('get')->once()->with('/')->andReturn($root); + $this->fs->shouldReceive('get')->once()->with('/bar'); + $this->fs->shouldReceive('getNodeFactory')->once()->withNoArgs()->andReturn($factory); + + $node = $handle->touch($mtime, $atime); + + $this->assertSame($file, $node); + } + + /** + * @dataProvider dataTouch + */ + public function testTouchExistingFile($mode) + { + $handle = new FileHandle($this->fs, 'foo://bar', $mode); + $atime = Mockery::mock('DateTime'); + $mtime = Mockery::mock('DateTime'); + + $file = Mockery::mock('Vfs\Node\FileInterface'); + $file->shouldReceive('setDateAccessed')->once()->with($atime); + $file->shouldReceive('setDateModified')->once()->with($mtime); + + $this->fs->shouldReceive('get')->once()->with('/bar')->andReturn($file); + + $node = $handle->touch($mtime, $atime); + + $this->assertSame($file, $node); + } }