Merge pull request #14 from adlawson/feature/symlinks

Symlinks
This commit is contained in:
Andrew Lawson 2015-07-24 17:23:57 +01:00
commit 880950780b
13 changed files with 479 additions and 12 deletions

View File

@ -43,10 +43,10 @@
"phpunit/phpunit": "^4.7"
},
"scripts": {
"test": "vendor/bin/phpunit --colors=always",
"test:acceptance": "vendor/bin/phpunit --colors=always --testsuite acceptance",
"test:functional": "vendor/bin/phpunit --colors=always --testsuite functional",
"test:unit": "vendor/bin/phpunit --colors=always --testsuite unit",
"test": "vendor/bin/phpunit -v --colors=always",
"test:acceptance": "vendor/bin/phpunit -v --colors=always --testsuite acceptance",
"test:functional": "vendor/bin/phpunit -v --colors=always --testsuite functional",
"test:unit": "vendor/bin/phpunit -v --colors=always --testsuite unit",
"lint:fix": [
"vendor/bin/php-cs-fixer fix src --level=psr2",
"vendor/bin/php-cs-fixer fix test --level=psr2"

104
src/Node/DirectoryLink.php Normal file
View File

@ -0,0 +1,104 @@
<?php
/*
* This file is part of VFS
*
* Copyright (c) 2015 Andrew Lawson <http://adlawson.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Vfs\Node;
use DateTime;
class DirectoryLink implements NodeContainerInterface, LinkInterface
{
protected $dateAccessed;
protected $dateCreated;
protected $dateModified;
protected $file;
protected $mode;
/**
* @param NodeContainerInterface $directory
*/
public function __construct(NodeContainerInterface $directory)
{
$this->directory = $directory;
$this->mode = self::TYPE_LINK;
$this->dateAccessed = new DateTime();
$this->dateCreated = clone $this->dateAccessed;
$this->dateModified = clone $this->dateAccessed;
}
public function add($name, NodeInterface $node)
{
$this->directory->add($name, $node);
}
public function get($name)
{
return $this->directory->get($name);
}
public function has($name)
{
return $this->directory->has($name);
}
public function remove($name)
{
$this->directory->remove($name);
}
public function set($name, NodeInterface $node)
{
$this->directory->set($name, $node);
}
public function getDateAccessed()
{
return $this->dateAccessed;
}
public function setDateAccessed(DateTime $dateTime)
{
$this->dateAccessed = $dateTime;
}
public function getDateCreated()
{
return $this->dateCreated;
}
public function getDateModified()
{
return $this->dateModified;
}
public function setDateModified(DateTime $dateTime)
{
$this->dateModified = $dateTime;
}
public function getIterator()
{
return $this->directory->getIterator();
}
public function getMode()
{
return $this->mode;
}
public function getSize()
{
return $this->directory->getSize();
}
public function getTarget()
{
return $this->directory;
}
}

View File

@ -11,9 +11,11 @@ namespace Vfs\Node\Factory;
use LogicException;
use Vfs\Node\Directory;
use Vfs\Node\DirectoryLink;
use Vfs\Node\File;
use Vfs\Node\FileLink;
use Vfs\Node\FileInterface;
use Vfs\Node\NodeContainerInterface;
use Vfs\Node\NodeInterface;
class NodeFactory implements NodeFactoryInterface
{
@ -22,14 +24,19 @@ class NodeFactory implements NodeFactoryInterface
return new Directory($children);
}
public function buildDirectoryLink(NodeContainerInterface $target)
{
return new DirectoryLink($target);
}
public function buildFile($content = '')
{
return new File($content);
}
public function buildLink($content = '')
public function buildFileLink(FileInterface $target)
{
throw new LogicException('Symlinks aren\'t supported yet.');
return new FileLink($target);
}
public function buildTree(array $tree)

View File

@ -9,6 +9,8 @@
*/
namespace Vfs\Node\Factory;
use Vfs\Node\FileInterface;
use Vfs\Node\LinkInterface;
use Vfs\Node\NodeContainerInterface;
use Vfs\Node\NodeInterface;
@ -20,6 +22,12 @@ interface NodeFactoryInterface
*/
public function buildDirectory(array $children = []);
/**
* @param NodeContainerInterface $target
* @return LinkInterface
*/
public function buildDirectoryLink(NodeContainerInterface $target);
/**
* @param string $content
* @return NodeInterface
@ -27,10 +35,10 @@ interface NodeFactoryInterface
public function buildFile($content = '');
/**
* @param string $content
* @return NodeInterface
* @param FileInterface $file
* @return LinkInterface
*/
public function buildLink($content = '');
public function buildFileLink(FileInterface $target);
/**
* @param array $tree

84
src/Node/FileLink.php Normal file
View File

@ -0,0 +1,84 @@
<?php
/*
* This file is part of VFS
*
* Copyright (c) 2015 Andrew Lawson <http://adlawson.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Vfs\Node;
use DateTime;
class FileLink implements FileInterface, LinkInterface
{
protected $dateAccessed;
protected $dateCreated;
protected $dateModified;
protected $file;
protected $mode;
/**
* @param FileInterface $file
*/
public function __construct(FileInterface $file)
{
$this->file = $file;
$this->mode = self::TYPE_LINK;
$this->dateAccessed = new DateTime();
$this->dateCreated = clone $this->dateAccessed;
$this->dateModified = clone $this->dateAccessed;
}
public function getContent()
{
return $this->file->getContent();
}
public function setContent($content)
{
$this->file->setContent($content);
}
public function getDateAccessed()
{
return $this->dateAccessed;
}
public function setDateAccessed(DateTime $dateTime)
{
$this->dateAccessed = $dateTime;
}
public function getDateCreated()
{
return $this->dateCreated;
}
public function getDateModified()
{
return $this->dateModified;
}
public function setDateModified(DateTime $dateTime)
{
$this->dateModified = $dateTime;
}
public function getMode()
{
return $this->mode;
}
public function getSize()
{
return $this->file->getSize();
}
public function getTarget()
{
return $this->file;
}
}

View File

@ -9,6 +9,16 @@
*/
namespace Vfs\Node;
/**
* Link node type
*
* This is an implementation of a hard link rather than a symbolic link; the
* key difference being it links directly to a node and not a path. If the
* target node is moved or renamed the link remains intact.
*
* The implementation is a simple proxy, whereby most other method calls should
* proxy through to the target where suitable.
*/
interface LinkInterface extends NodeInterface
{
/**

View File

@ -41,6 +41,7 @@ interface StatInterface
const S_IWOTH = 0000002;
const S_IXOTH = 0000001;
const TYPE_MASK = self::S_IFMT;
const TYPE_SOCKET = self::S_IFSOCK;
const TYPE_LINK = self::S_IFLNK;
const TYPE_FILE = self::S_IFREG;

View File

@ -0,0 +1,41 @@
<?php
namespace Vfs\Stream\StreamWrapper;
use Vfs\Test\AcceptanceTestCase;
class SymlinkAcceptanceTest extends AcceptanceTestCase
{
protected $tree = [
'foo' => [
'bar' => 'baz'
]
];
public function testIsLink()
{
$factory = $this->fs->getNodeFactory();
$file = $this->fs->get('/foo/bar');
$this->fs->get('/')->add('symlink', $factory->buildFileLink($file));
$this->assertTrue(is_link("$this->scheme:///symlink"));
}
public function testDirectoryLink()
{
$this->markTestSkipped('`symlink()` isn\'t supported by PHP Stream Wrappers');
symlink("$this->scheme:///foo/bar", "$this->scheme:///symlink");
$this->assertTrue(is_link("$this->scheme:///symlink"));
}
public function testFileLink()
{
$this->markTestSkipped('`symlink()` isn\'t supported by PHP Stream Wrappers');
symlink("$this->scheme:///foo", "$this->scheme:///symlink");
$this->assertTrue(is_link("$this->scheme:///symlink"));
}
}

View File

@ -0,0 +1,118 @@
<?php
namespace Vfs\Node;
use Mockery;
use Vfs\Test\UnitTestCase;
class DirectoryLinkTest extends UnitTestCase
{
public function setUp()
{
$this->nodeA = $a = Mockery::mock('Vfs\Node\NodeInterface');
$this->nodeB = $b = Mockery::mock('Vfs\Node\NodeInterface');
$this->nodeC = $c = Mockery::mock('Vfs\Node\NodeInterface');
$this->nodes = ['foo' => $a, 'bar' => $b, 'baz' => $c];
}
public function testInstance()
{
$link = new DirectoryLink(new Directory());
$this->assertInstanceOf('Vfs\Node\NodeContainerInterface', $link);
$this->assertInstanceOf('Vfs\Node\LinkInterface', $link);
$this->assertInstanceOf('Vfs\Node\NodeInterface', $link);
$this->assertInstanceOf('Vfs\Node\StatInterface', $link);
}
public function testAdd()
{
$dir = new Directory();
$link = new DirectoryLink($dir);
$link->add('foo', $this->nodeA);
$this->assertSame($this->nodeA, $dir->get('foo'));
}
public function testGet()
{
$link = new DirectoryLink(new Directory($this->nodes));
$this->assertSame($this->nodeA, $link->get('foo'));
}
public function testGetThrowsMissingNode()
{
$link = new DirectoryLink(new Directory());
$this->setExpectedException('Vfs\Exception\MissingNodeException');
$link->get('foo');
}
public function testHasIsTrue()
{
$link = new DirectoryLink(new Directory($this->nodes));
$this->assertTrue($link->has('foo'));
}
public function testHasIsFalse()
{
$link = new DirectoryLink(new Directory());
$this->assertFalse($link->has('foo'));
}
public function testSet()
{
$dir = new Directory();
$link = new DirectoryLink($dir);
$link->set('foo', $this->nodeA);
$this->assertSame($this->nodeA, $dir->get('foo'));
}
public function testGetDateAccessed()
{
$dir = new Directory();
$link = new DirectoryLink($dir);
$this->assertInstanceOf('DateTime', $link->getDateAccessed());
$this->assertNotSame($link->getDateAccessed(), $dir->getDateAccessed());
}
public function testGetDateCreated()
{
$dir = new Directory();
$link = new DirectoryLink($dir);
$this->assertInstanceOf('DateTime', $link->getDateCreated());
$this->assertNotSame($link->getDateCreated(), $dir->getDateCreated());
}
public function testGetDateModified()
{
$dir = new Directory();
$link = new DirectoryLink($dir);
$this->assertInstanceOf('DateTime', $link->getDateModified());
$this->assertNotSame($link->getDateModified(), $dir->getDateModified());
}
public function testGetMode()
{
$link = new DirectoryLink(new Directory());
$this->assertEquals(StatInterface::TYPE_LINK, $link->getMode() & StatInterface::TYPE_MASK);
}
public function testGetSize()
{
$link = new DirectoryLink(new Directory($this->nodes));
$this->nodeA->shouldReceive('getSize')->once()->withNoArgs()->andReturn(1);
$this->nodeB->shouldReceive('getSize')->once()->withNoArgs()->andReturn(2);
$this->nodeC->shouldReceive('getSize')->once()->withNoArgs()->andReturn(3);
$this->assertEquals(6, $link->getSize());
}
}

View File

@ -120,7 +120,7 @@ class DirectoryTest extends UnitTestCase
{
$dir = new Directory();
$this->assertEquals(StatInterface::TYPE_DIR, $dir->getMode() & StatInterface::S_IFMT);
$this->assertEquals(StatInterface::TYPE_DIR, $dir->getMode() & StatInterface::TYPE_MASK);
}
public function testGetSize()

View File

@ -2,6 +2,8 @@
namespace Vfs\Node\Factory;
use Mockery;
use Vfs\Node\Directory;
use Vfs\Node\File;
use Vfs\Test\UnitTestCase;
class NodeFactoryTest extends UnitTestCase
@ -24,6 +26,15 @@ class NodeFactoryTest extends UnitTestCase
$this->assertEquals('foo', $file->getContent());
}
public function testBuildFileLink()
{
$file = new File();
$link = $this->factory->buildFileLink($file);
$this->assertInstanceOf('Vfs\Node\FileInterface', $link);
$this->assertInstanceOf('Vfs\Node\LinkInterface', $link);
}
public function testBuildDirectory()
{
$node = Mockery::mock('Vfs\Node\NodeInterface');
@ -33,6 +44,15 @@ class NodeFactoryTest extends UnitTestCase
$this->assertSame($node, $dir->get('foo'));
}
public function testBuildDirectoryLink()
{
$dir = new Directory();
$link = $this->factory->buildDirectoryLink($dir);
$this->assertInstanceOf('Vfs\Node\NodeContainerInterface', $link);
$this->assertInstanceOf('Vfs\Node\LinkInterface', $link);
}
public function testBuildTree()
{
$root = $this->factory->buildTree([

View File

@ -0,0 +1,74 @@
<?php
namespace Vfs\Node;
use Vfs\Test\UnitTestCase;
class FileLinkTest extends UnitTestCase
{
public function testInstance()
{
$link = new FileLink(new File());
$this->assertInstanceOf('Vfs\Node\FileInterface', $link);
$this->assertInstanceOf('Vfs\Node\LinkInterface', $link);
$this->assertInstanceOf('Vfs\Node\NodeInterface', $link);
$this->assertInstanceOf('Vfs\Node\StatInterface', $link);
}
public function testGetContent()
{
$link = new FileLink(new File('foo'));
$this->assertEquals('foo', $link->getContent());
}
public function testSetContent()
{
$file = new File('');
$link = new FileLink($file);
$link->setContent('foo');
$this->assertEquals('foo', $file->getContent());
}
public function testGetDateAccessed()
{
$file = new File();
$link = new FileLink($file);
$this->assertInstanceOf('DateTime', $link->getDateAccessed());
$this->assertNotSame($link->getDateAccessed(), $file->getDateAccessed());
}
public function testGetDateCreated()
{
$file = new File();
$link = new FileLink($file);
$this->assertInstanceOf('DateTime', $link->getDateCreated());
$this->assertNotSame($link->getDateCreated(), $file->getDateCreated());
}
public function testGetDateModified()
{
$file = new File();
$link = new FileLink($file);
$this->assertInstanceOf('DateTime', $link->getDateModified());
$this->assertNotSame($link->getDateModified(), $file->getDateModified());
}
public function testGetMode()
{
$link = new FileLink(new File());
$this->assertEquals(StatInterface::TYPE_LINK, $link->getMode() & StatInterface::TYPE_MASK);
}
public function testGetSize()
{
$link = new FileLink(new File('foo'));
$this->assertEquals(3, $link->getSize());
}
}

View File

@ -54,7 +54,7 @@ class FileTest extends UnitTestCase
{
$file = new File();
$this->assertEquals(StatInterface::TYPE_FILE, $file->getMode() & StatInterface::S_IFMT);
$this->assertEquals(StatInterface::TYPE_FILE, $file->getMode() & StatInterface::TYPE_MASK);
}
public function testGetSize()