Skip to content

Commit

Permalink
added symlink support (close #20)
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Donat committed May 10, 2014
1 parent 96c6794 commit 7114cff
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/VirtualFileSystem/Container.php
Expand Up @@ -156,6 +156,35 @@ public function createDir($path, $recursive = false, $mode = null)
return $newDirectory;
}

/**
* Creates link at given path
*
* @param string $path
* @param $destination
*
* @return Structure\File
*
*/
public function createLink($path, $destination)
{

$destination = $this->fileAt($destination);

try {
$file = $this->fileAt($path);
throw new \RuntimeException(sprintf('%s already exists', $path));
} catch (NotFoundException $e) {

}

$parent = $this->fileAt(dirname($path));

$parent->addLink($newLink = $this->factory()->getLink(basename($path), $destination));

return $newLink;

}

/**
* Creates file at given path
*
Expand Down
14 changes: 14 additions & 0 deletions src/VirtualFileSystem/Factory.php
Expand Up @@ -14,6 +14,7 @@
use VirtualFileSystem\Structure\Node;
use VirtualFileSystem\Structure\Root;
use VirtualFileSystem\Structure\File;
use VirtualFileSystem\Structure\Link;

/**
* Factory class to encapsulate object creation.
Expand Down Expand Up @@ -116,4 +117,17 @@ public function getFile($basename)
{
return $this->updateMetadata(new File($basename));
}

/**
* Creates Link object.
*
* @param string $basename
* @param Structure\Node $destination
*
* @return Link
*/
public function getLink($basename, Node $destination)
{
return $this->updateMetadata(new Link($basename, $destination));
}
}
13 changes: 13 additions & 0 deletions src/VirtualFileSystem/FileSystem.php
Expand Up @@ -161,4 +161,17 @@ public function createStructure(array $structure)
{
$this->container()->createStructure($structure);
}

/**
* Creates and returns a link
*
* @param string $path
* @param $destinationPath
*
* @return Link
*/
public function createLink($path, $destinationPath)
{
return $this->container()->createLink($path, $destinationPath);
}
}
10 changes: 10 additions & 0 deletions src/VirtualFileSystem/Structure/Directory.php
Expand Up @@ -63,6 +63,16 @@ public function addFile(File $file)
$this->addNode($file);
}

/**
* Adds child Link.
*
* @param Link $link
*/
public function addLink(Link $link)
{
$this->addNode($link);
}

/**
* Adds child Node.
*
Expand Down
61 changes: 61 additions & 0 deletions src/VirtualFileSystem/Structure/Link.php
@@ -0,0 +1,61 @@
<?php
/*
* This file is part of the php-vfs package.
*
* (c) Michael Donat <michael.donat@me.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace VirtualFileSystem\Structure;

/**
* Object representation of a Link.
*
* @author Michael Donat <michael.donat@me.com>
* @package php-vfs
*/
class Link extends Node
{
/**
* @see http://man7.org/linux/man-pages/man2/lstat.2.html
*/
const S_IFTYPE = 0120000;

/**
* @var Node
*/
protected $destination;

/**
* Class constructor.
*
* @param string $basename
*/
public function __construct($basename, Node $destination)
{
parent::__construct($basename);
$this->destination = $destination;
}

/**
* Returns Link size.
*
* The size is the length of the destination path
*
* @return mixed
*/
public function size()
{
return $this->destination->size();
}

/**
* @return Node
*/
public function getDestination()
{
return $this->destination;
}
}
5 changes: 5 additions & 0 deletions src/VirtualFileSystem/Wrapper.php
Expand Up @@ -12,6 +12,7 @@

use VirtualFileSystem\Structure\Directory;
use VirtualFileSystem\Structure\File;
use VirtualFileSystem\Structure\Link;
use VirtualFileSystem\Structure\Root;
use VirtualFileSystem\Wrapper\FileHandler;
use VirtualFileSystem\Wrapper\DirectoryHandler;
Expand Down Expand Up @@ -160,6 +161,10 @@ public function stream_open($path, $mode, $options, &$opened_path)

$file = $container->fileAt($path);

if($file instanceof Link) {
$file = $file->getDestination();
}

if (($extended || $writeMode || $appendMode) && $file instanceof Directory) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error(sprintf('fopen(%s): failed to open stream: Is a directory', $path), E_USER_WARNING);
Expand Down
23 changes: 23 additions & 0 deletions tests/VirtualFileSystem/ContainerTest.php
Expand Up @@ -237,4 +237,27 @@ public function testRemoveThrowsWhenDeletingDirectoriesWithRecursiveFlag()

$container->remove('/dir');
}

public function testLinkCreation()
{
$container = new Container(new Factory());
$container->createFile('/file');
$container->createLink('/link', '/file');

$this->assertInstanceOf('\VirtualFileSystem\Structure\Link', $container->fileAt('/link'));

}

public function testLinkCreationThrowsWhenTryingToOverride()
{
$container = new Container(new Factory());

$container->createFile('/file');
$container->createLink('/link', '/file');

$this->setExpectedException('\RuntimeException');

$container->createLink('/link', '/file');

}
}
26 changes: 26 additions & 0 deletions tests/VirtualFileSystem/Structure/LinkTest.php
@@ -0,0 +1,26 @@
<?php

namespace VirtualFileSystem\Structure;

class LinkTest extends \PHPUnit_Framework_TestCase
{
public function testFileSizeAssumesTargetSize()
{
$node = new File('file');
$node->setData('12345');

$link = new Link('link', $node);

$this->assertEquals($node->size(), $link->size());

$dir = new Directory('/d');

$link = new Link('link', $dir);

$this->assertEquals($dir->size(), $dir->size());

$dir->addFile($node);

$this->assertEquals($dir->size(), $dir->size());
}
}
101 changes: 101 additions & 0 deletions tests/VirtualFileSystem/WrapperTest.php
Expand Up @@ -4,6 +4,7 @@

use VirtualFileSystem\Structure\Directory;
use VirtualFileSystem\Structure\File;
use VirtualFileSystem\Structure\Link;

class WrapperTest extends \PHPUnit_Framework_TestCase
{
Expand Down Expand Up @@ -67,6 +68,15 @@ public function testIsDir()

}

public function testIsLink()
{
$fs = new FileSystem();
$fs->root()->addDirectory($d = new Directory('dir'));
$d->addLink(new Link('link', $d));

$this->assertTrue(is_link($fs->path('/dir/link')));
}

public function testIsFile()
{
$fs = new FileSystem();
Expand Down Expand Up @@ -1210,6 +1220,97 @@ public function testTouchNotAllowedIfNotOwnerOrNotWritable()
);
}

public function testLchown() {

if ($this->uid == 0) {
$this->markTestSkipped(
'No point testing if user is already root. \Php unit shouldn\'t be run as root user. (Unless you are a windows user!)'
);
}

$fs = new FileSystem();
$directory = $fs->createDirectory('/dir');
$link = new Link('link', $directory);
$directory->addLink($link);

$fs->container()->setPermissionHelper(new Wrapper\PermissionHelper(0, 0)); //forcing user to root

lchown($fs->path('/dir/link'), 'root');
$this->assertEquals('root', posix_getpwuid(fileowner($fs->path('/dir/link')))['name']);

}

public function testLchgrp()
{
if ($this->uid == 0) {
$this->markTestSkipped(
'No point testing if group is already root. Php unit shouldn\'t be run as root group. (Unless you are on Windows - then we skip)'
);
}

$fs = new FileSystem();
$directory = $fs->createDirectory('/dir');
$link = new Link('link', $directory);
$directory->addLink($link);

$fs->container()->setPermissionHelper(new Wrapper\PermissionHelper(0, 0)); //forcing user to root

//lets workout available group
//this is needed to find string name of group root belongs to
$group = posix_getgrgid(posix_getpwuid(0)['gid'])['name'];

chgrp($fs->path('/dir/link'), $group);

$this->assertEquals($group, posix_getgrgid(filegroup($fs->path('/dir/link')))['name']);
}

public function testFileCopy() {

$fs = new FileSystem();
$fs->createFile('/file', 'data');

copy($fs->path('/file'), $fs->path('/file2'));

$this->assertTrue(file_exists($fs->path('/file2')));

$this->assertEquals('data', $fs->container()->fileAt('/file2')->data());

}

public function testLinkCopyCreatesHardCopyOfFile() {

$fs = new FileSystem();
$fs->createFile('/file', 'data');
$fs->createLink('/link', '/file');

copy($fs->path('/link'), $fs->path('/file2'));

$this->assertTrue(file_exists($fs->path('/file2')));
$this->assertEquals('data', $fs->container()->fileAt('/file2')->data());

}

public function testLinkReading() {

$fs = new FileSystem();
$fs->createFile('/file', 'data');
$fs->createLink('/link', '/file');

$this->assertEquals('data', file_get_contents($fs->path('/link')));
}

public function tetsLinkWriting() {

$fs = new FileSystem();
$fs->createFile('/file', 'ubots!');
$fs->createLink('/link', '/file');

file_put_contents($fs->path('/link'), 'data');

$this->assertEquals('data', file_get_contents($fs->path('/link')));

}

public function testIsExecutableReturnsCorrectly()
{
$fs = new FileSystem();
Expand Down

0 comments on commit 7114cff

Please sign in to comment.