├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── example └── eval.php ├── phpunit.xml.dist ├── src ├── Exception │ ├── ExceptionInterface.php │ ├── ExistingNodeException.php │ ├── MissingNodeException.php │ ├── RegisteredSchemeException.php │ ├── UnopenedHandleException.php │ └── UnregisteredSchemeException.php ├── FileSystem.php ├── FileSystemBuilder.php ├── FileSystemInterface.php ├── FileSystemRegistry.php ├── Logger │ └── PhpErrorLogger.php ├── Node │ ├── Directory.php │ ├── DirectoryLink.php │ ├── Factory │ │ ├── NodeFactory.php │ │ └── NodeFactoryInterface.php │ ├── File.php │ ├── FileInterface.php │ ├── FileLink.php │ ├── LinkInterface.php │ ├── NodeContainerInterface.php │ ├── NodeInterface.php │ ├── StatInterface.php │ └── Walker │ │ ├── NodeWalker.php │ │ └── NodeWalkerInterface.php ├── RegistryInterface.php └── Stream │ ├── AbstractHandle.php │ ├── DirectoryHandle.php │ ├── FileHandle.php │ ├── HandleInterface.php │ └── StreamWrapper.php └── test ├── acceptance └── Stream │ └── StreamWrapper │ ├── FileGetContentsAcceptanceTest.php │ ├── FopenAcceptanceTest.php │ ├── FreadAcceptanceTest.php │ ├── PermissionAcceptanceTest.php │ ├── ReadDirAcceptanceTest.php │ ├── RequireAcceptanceTest.php │ ├── StatAcceptanceTest.php │ └── SymlinkAcceptanceTest.php ├── functional └── FileSystemFunctionalTest.php ├── src ├── AcceptanceTestCase.php ├── FunctionalTestCase.php └── UnitTestCase.php └── unit ├── FileSystemBuilderTest.php ├── FileSystemRegistryTest.php ├── FileSystemTest.php ├── Logger └── PhpErrorLoggerTest.php ├── Node ├── DirectoryLinkTest.php ├── DirectoryTest.php ├── Factory │ └── NodeFactoryTest.php ├── FileLinkTest.php ├── FileTest.php └── Walker │ └── NodeWalkerTest.php └── Stream ├── DirectoryHandleTest.php └── FileHandleTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant 2 | composer.lock 3 | Vagrantfile 4 | vendor 5 | /.idea 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | sudo: false 3 | 4 | php: 5 | - 5.5 6 | - 5.6 7 | - 7.0 8 | - hhvm 9 | 10 | install: 11 | - travis_retry composer self-update 12 | - travis_retry composer install --no-interaction --ignore-platform-reqs 13 | 14 | script: 15 | - composer test 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Andrew Lawson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VFS (Virtual File System) 2 | 3 | Virtual File System 4 | 5 | [![Master branch build status][ico-build]][travis] 6 | [![Published version][ico-package]][package] 7 | [![PHP ~5.4][ico-engine]][lang] 8 | [![MIT Licensed][ico-license]][license] 9 | 10 | **VFS** is a virtual file system for PHP built using the stream wrapper API. 11 | Streams are exposed just as typical `file://` or `http://` streams are to PHP's 12 | built-in functions and keywords like `fopen` and `require`. This implementation 13 | attempts to stay true to the typical streams, including triggering warnings 14 | and handling edge cases appropriately. 15 | 16 | It can be installed in whichever way you prefer, but I recommend 17 | [Composer][package]. 18 | ```json 19 | { 20 | "require": { 21 | "adlawson/vfs": "*" 22 | } 23 | } 24 | ``` 25 | 26 | ## Documentation 27 | After creating and mounting the file system, you have the option of manipulating 28 | the virtual file system either via PHP's built-in functions, the VFS interfaces, 29 | or interfaces provided by another file system library. 30 | ```php 31 | mount(); 39 | 40 | // Add `/foo` and `/foo/bar.txt` 41 | $foo = new Directory(['bar.txt' => new File('Hello, World!')]); 42 | $fs->get('/')->add('foo', $foo); 43 | 44 | // Get contents of `/foo/bar.txt` 45 | $fs->get('/foo/bar.txt')->getContent(); // Hello, World! 46 | file_get_contents('vfs://foo/bar.txt'); // Hello, World! 47 | 48 | // Add `/foo/bar` and `/foo/bar/baz.php` 49 | mkdir('vfs://foo/bar'); 50 | file_put_contents('vfs://foo/bar.php', 'mkdir('vfs://foo/bar/baz'); 58 | $laravel = new Illuminate\Filesystem(); 59 | $laravel->isDirectory('vfs://foo/bar/baz'); //true 60 | 61 | // Triggers PHP warnings on error just like typical streams 62 | rename('vfs://path/to/nowhere', 'vfs://path/to/somewhere'); 63 | // PHP Warning: rename(vfs://path/to/nowhere,vfs://path/to/somewhere): No such file or directory in /srv/index.php on line 1; triggered in /srv/src/Logger/PhpErrorLogger.php on line 32 64 | ``` 65 | 66 | ### Example use cases 67 | If you need to ask what you'd use a virtual file system for, you probably don't 68 | need one, but just in case, I've compiled a small list of examples: 69 | - Testing file system libraries without writing to disc 70 | - Runtime evaluation without `eval` (via `write` and `require`) 71 | - ...we need some more! 72 | 73 | ### Todo 74 | Current tasks are listed on the [github issues][issues] page, but some are 75 | listed here for reference: 76 | - Symlinks 77 | - File locks 78 | - Permissions/ACL 79 | 80 | 81 | ## Contributing 82 | Contributions are accepted via Pull Request, but passing unit tests must be 83 | included before it will be considered for merge. 84 | ```bash 85 | $ curl -O https://raw.githubusercontent.com/adlawson/vagrantfiles/master/php/Vagrantfile 86 | $ vagrant up 87 | $ vagrant ssh 88 | ... 89 | 90 | $ cd /srv 91 | $ composer install 92 | $ vendor/bin/phpunit 93 | ``` 94 | 95 | ### License 96 | The content of this library is released under the **MIT License** by 97 | **Andrew Lawson**.
You can find a copy of this license in 98 | [`LICENSE`][license] or at http://opensource.org/licenses/mit. 99 | 100 | [travis]: https://travis-ci.org/adlawson/php-vfs 101 | [lang]: http://php.net 102 | [package]: https://packagist.org/packages/adlawson/vfs 103 | [ico-license]: http://img.shields.io/packagist/l/adlawson/vfs.svg?style=flat 104 | [ico-package]: http://img.shields.io/packagist/v/adlawson/vfs.svg?style=flat 105 | [ico-build]: http://img.shields.io/travis/adlawson/php-vfs/master.svg?style=flat 106 | [ico-engine]: http://img.shields.io/badge/php-~5.4-8892BF.svg?style=flat 107 | [issues]: https://github.com/adlawson/php-vfs/issues 108 | [license]: LICENSE 109 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adlawson/vfs", 3 | "description": "Virtual file system", 4 | "homepage": "https://github.com/adlawson/php-vfs", 5 | "license": "MIT", 6 | "type": "library", 7 | "keywords": [ 8 | "dir", 9 | "directory", 10 | "fs", 11 | "file", 12 | "read", 13 | "stream", 14 | "system", 15 | "virtual", 16 | "wrapper", 17 | "write" 18 | ], 19 | "authors": [ 20 | { 21 | "name": "Andrew Lawson", 22 | "homepage": "http://adlawson.com" 23 | } 24 | ], 25 | "autoload": { 26 | "psr-4": { 27 | "Vfs\\": "src/" 28 | } 29 | }, 30 | "autoload-dev": { 31 | "psr-4": { 32 | "Vfs\\Test\\": "test/src/" 33 | } 34 | }, 35 | "require": { 36 | "php": ">=5.5", 37 | "psr/log": "^1.0" 38 | }, 39 | "require-dev": { 40 | "adlawson/timezone": "^1.0", 41 | "fabpot/php-cs-fixer": "^1.9", 42 | "mockery/mockery": "^0.9", 43 | "phpunit/phpunit": "^4.7" 44 | }, 45 | "scripts": { 46 | "test": "vendor/bin/phpunit -v --colors=always", 47 | "test:acceptance": "vendor/bin/phpunit -v --colors=always --testsuite acceptance", 48 | "test:functional": "vendor/bin/phpunit -v --colors=always --testsuite functional", 49 | "test:unit": "vendor/bin/phpunit -v --colors=always --testsuite unit", 50 | "lint:fix": [ 51 | "vendor/bin/php-cs-fixer fix src --level=psr2", 52 | "vendor/bin/php-cs-fixer fix test --level=psr2" 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /example/eval.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | use Vfs\FileSystem; 11 | 12 | require __DIR__ . '/../vendor/autoload.php'; 13 | 14 | FileSystem::factory('eval://'); 15 | 16 | file_put_contents('eval://foo.php', <<<'EOF' 17 | name = $name; 26 | } 27 | 28 | public function getName() 29 | { 30 | return $this->name; 31 | } 32 | } 33 | EOF 34 | ); 35 | 36 | require 'eval://foo.php'; 37 | 38 | $foo = new Foo('bar'); 39 | 40 | var_dump($foo->getName()); 41 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | test/acceptance 10 | 11 | 12 | test/functional 13 | 14 | 15 | test/unit 16 | 17 | 18 | 19 | 20 | 21 | 22 | src 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/Exception/ExceptionInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Exception; 11 | 12 | interface ExceptionInterface 13 | { 14 | } 15 | -------------------------------------------------------------------------------- /src/Exception/ExistingNodeException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Exception; 11 | 12 | use Exception; 13 | use OutOfBoundsException; 14 | use Vfs\Node\NodeContainerInterface; 15 | 16 | class ExistingNodeException extends OutOfBoundsException implements ExceptionInterface 17 | { 18 | protected $container; 19 | protected $name; 20 | 21 | /** 22 | * @param string $name 23 | * @param NodeContainerInterface $container 24 | * @param integer $code 25 | * @param Exception $previous 26 | */ 27 | public function __construct($name, NodeContainerInterface $container, $code = 0, Exception $previous = null) 28 | { 29 | $this->container = $container; 30 | $this->name = $name; 31 | 32 | $message = sprintf('Node with name "%s" already exists in container.', $name); 33 | 34 | parent::__construct($message, $code, $previous); 35 | } 36 | 37 | /** 38 | * @return NodeContainerInterface 39 | */ 40 | public function getContainer() 41 | { 42 | return $this->container; 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function getName() 49 | { 50 | return $this->name; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Exception/MissingNodeException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Exception; 11 | 12 | use Exception; 13 | use OutOfRangeException; 14 | use Vfs\Node\NodeContainerInterface; 15 | 16 | class MissingNodeException extends OutOfRangeException implements ExceptionInterface 17 | { 18 | protected $container; 19 | protected $name; 20 | 21 | /** 22 | * @param string $name 23 | * @param NodeContainerInterface $container 24 | * @param integer $code 25 | * @param Exception $previous 26 | */ 27 | public function __construct($name, NodeContainerInterface $container, $code = 0, Exception $previous = null) 28 | { 29 | $this->container = $container; 30 | $this->name = $name; 31 | 32 | $message = sprintf('Node with name "%s" doesn\'t exist in container.', $name); 33 | 34 | parent::__construct($message, $code, $previous); 35 | } 36 | 37 | /** 38 | * @return NodeContainerInterface 39 | */ 40 | public function getContainer() 41 | { 42 | return $this->container; 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function getName() 49 | { 50 | return $this->name; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Exception/RegisteredSchemeException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Exception; 11 | 12 | use Exception; 13 | use OutOfBoundsException; 14 | 15 | class RegisteredSchemeException extends OutOfBoundsException implements ExceptionInterface 16 | { 17 | protected $scheme; 18 | 19 | /** 20 | * @param string $scheme 21 | * @param integer $code 22 | * @param Exception $previous 23 | */ 24 | public function __construct($scheme, $code = 0, Exception $previous = null) 25 | { 26 | $this->scheme = $scheme; 27 | 28 | $message = sprintf('File system with scheme "%s" has already been registered.', $scheme); 29 | 30 | parent::__construct($message, $code, $previous); 31 | } 32 | 33 | /** 34 | * @return string 35 | */ 36 | public function getScheme() 37 | { 38 | return $this->scheme; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Exception/UnopenedHandleException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Exception; 11 | 12 | use Exception; 13 | use RuntimeException; 14 | use Vfs\Stream\HandleInterface; 15 | 16 | class UnopenedHandleException extends RuntimeException implements ExceptionInterface 17 | { 18 | protected $handle; 19 | protected $url; 20 | 21 | /** 22 | * @param HandleInterface $handle 23 | * @param string $url 24 | * @param integer $code 25 | * @param Exception $previous 26 | */ 27 | public function __construct(HandleInterface $handle, $url, $code = 0, Exception $previous = null) 28 | { 29 | $this->handle = $handle; 30 | $this->url = $url; 31 | 32 | $message = sprintf('Handle at url "%s" hasn\'t been opened.', $url); 33 | 34 | parent::__construct($message, $code, $previous); 35 | } 36 | 37 | /** 38 | * @return HandleInterface 39 | */ 40 | public function getHandle() 41 | { 42 | return $this->handle; 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function getUrl() 49 | { 50 | return $this->url; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Exception/UnregisteredSchemeException.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Exception; 11 | 12 | use Exception; 13 | use OutOfRangeException; 14 | 15 | class UnregisteredSchemeException extends OutOfRangeException implements ExceptionInterface 16 | { 17 | protected $scheme; 18 | 19 | /** 20 | * @param string $scheme 21 | * @param integer $code 22 | * @param Exception $previous 23 | */ 24 | public function __construct($scheme, $code = 0, Exception $previous = null) 25 | { 26 | $this->scheme = $scheme; 27 | 28 | $message = sprintf('File system with scheme "%s" has not been registered.', $scheme); 29 | 30 | parent::__construct($message, $code, $previous); 31 | } 32 | 33 | /** 34 | * @return string 35 | */ 36 | public function getScheme() 37 | { 38 | return $this->scheme; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/FileSystem.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs; 11 | 12 | use Psr\Log\LoggerInterface; 13 | use Vfs\Exception\RegisteredSchemeException; 14 | use Vfs\Exception\UnregisteredSchemeException; 15 | use Vfs\Node\Factory\NodeFactoryInterface; 16 | use Vfs\Node\Walker\NodeWalkerInterface; 17 | 18 | class FileSystem implements FileSystemInterface 19 | { 20 | protected $logger; 21 | protected $nodeFactory; 22 | protected $nodeWalker; 23 | protected $registry; 24 | protected $scheme; 25 | protected $wrapperClass; 26 | protected $root; 27 | 28 | /** 29 | * @param string $scheme 30 | * @param string $wrapperClass 31 | * @param NodeFactoryInterface $nodeFactory 32 | * @param NodeWalkerInterface $nodeWalker 33 | * @param RegistryInterface $registry 34 | * @param LoggerInterface $logger 35 | */ 36 | public function __construct( 37 | $scheme, 38 | $wrapperClass, 39 | NodeFactoryInterface $nodeFactory, 40 | NodeWalkerInterface $nodeWalker, 41 | RegistryInterface $registry, 42 | LoggerInterface $logger 43 | ) { 44 | $this->wrapperClass = $wrapperClass; 45 | $this->scheme = rtrim($scheme, ':/\\'); 46 | $this->nodeWalker = $nodeWalker; 47 | $this->logger = $logger; 48 | $this->nodeFactory = $nodeFactory; 49 | $this->registry = $registry; 50 | 51 | $this->root = $nodeFactory->buildDirectory(); 52 | } 53 | 54 | /** 55 | * @param string $scheme 56 | * @return FileSystem 57 | */ 58 | public static function factory($scheme = self::SCHEME) 59 | { 60 | $builder = new FileSystemBuilder($scheme); 61 | 62 | return $builder->build(); 63 | } 64 | 65 | public function get($path) 66 | { 67 | return $this->nodeWalker->findNode($this->root, $path); 68 | } 69 | 70 | public function getLogger() 71 | { 72 | return $this->logger; 73 | } 74 | 75 | public function getNodeFactory() 76 | { 77 | return $this->nodeFactory; 78 | } 79 | 80 | public function getNodeWalker() 81 | { 82 | return $this->nodeWalker; 83 | } 84 | 85 | public function getScheme() 86 | { 87 | return $this->scheme; 88 | } 89 | 90 | public function mount() 91 | { 92 | if ($this->registry->has($this->scheme) || in_array($this->scheme, stream_get_wrappers())) { 93 | throw new RegisteredSchemeException($this->scheme); 94 | } 95 | 96 | if (stream_wrapper_register($this->scheme, $this->wrapperClass)) { 97 | $this->registry->add($this->scheme, $this); 98 | 99 | return true; 100 | } 101 | 102 | return false; 103 | } 104 | 105 | public function unmount() 106 | { 107 | if (!$this->registry->has($this->scheme) && !in_array($this->scheme, stream_get_wrappers())) { 108 | throw new UnregisteredSchemeException($this->scheme); 109 | } 110 | 111 | if (stream_wrapper_unregister($this->scheme)) { 112 | $this->registry->remove($this->scheme, $this); 113 | 114 | return true; 115 | } 116 | 117 | return false; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/FileSystemBuilder.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs; 11 | 12 | use Psr\Log\LoggerInterface; 13 | use Vfs\Logger\PhpErrorLogger; 14 | use Vfs\Node\Factory\NodeFactory; 15 | use Vfs\Node\Factory\NodeFactoryInterface; 16 | use Vfs\Node\Walker\NodeWalker; 17 | use Vfs\Node\Walker\NodeWalkerInterface; 18 | 19 | class FileSystemBuilder 20 | { 21 | protected $logger; 22 | protected $nodeFactory; 23 | protected $nodeWalker; 24 | protected $registry; 25 | protected $scheme; 26 | protected $wrapperClass; 27 | protected $tree = []; 28 | 29 | /** 30 | * @param string $scheme 31 | */ 32 | public function __construct($scheme = FileSystemInterface::SCHEME) 33 | { 34 | $this->setScheme($scheme); 35 | } 36 | 37 | /** 38 | * @return FileSystemInterface 39 | */ 40 | public function build() 41 | { 42 | $fs = new FileSystem( 43 | $this->getScheme(), 44 | $this->getStreamWrapper() ?: $this->buildDefaultStreamWrapper(), 45 | $this->getNodeFactory() ?: $this->buildDefaultNodeFactory(), 46 | $this->getNodeWalker() ?: $this->buildDefaultNodeWalker(), 47 | $this->getRegistry() ?: $this->buildDefaultRegistry(), 48 | $this->getLogger() ?: $this->buildDefaultLogger() 49 | ); 50 | 51 | $root = $fs->get('/'); 52 | $nodeFactory = $fs->getNodeFactory(); 53 | foreach ($nodeFactory->buildTree($this->getTree()) as $name => $node) { 54 | $root->set($name, $node); 55 | } 56 | 57 | return $fs; 58 | } 59 | 60 | /** 61 | * @return LoggerInterface 62 | */ 63 | public function getLogger() 64 | { 65 | return $this->logger; 66 | } 67 | 68 | /** 69 | * @param LoggerInterface $logger 70 | * @return FileSystemBuilder 71 | */ 72 | public function setLogger(LoggerInterface $logger) 73 | { 74 | $this->logger = $logger; 75 | 76 | return $this; 77 | } 78 | 79 | /** 80 | * @return NodeFactoryInterface 81 | */ 82 | public function getNodeFactory() 83 | { 84 | return $this->nodeFactory; 85 | } 86 | 87 | /** 88 | * @param NodeFactoryInterface $nodeFactory 89 | * @return FileSystemBuilder 90 | */ 91 | public function setNodeFactory(NodeFactoryInterface $nodeFactory) 92 | { 93 | $this->nodeFactory = $nodeFactory; 94 | 95 | return $this; 96 | } 97 | 98 | /** 99 | * @return NodeWalkerInterface 100 | */ 101 | public function getNodeWalker() 102 | { 103 | return $this->nodeWalker; 104 | } 105 | 106 | /** 107 | * @param NodeWalkerInterface $nodeWalker 108 | * @return FileSystemBuilder 109 | */ 110 | public function setNodeWalker(NodeWalkerInterface $nodeWalker) 111 | { 112 | $this->nodeWalker = $nodeWalker; 113 | 114 | return $this; 115 | } 116 | 117 | /** 118 | * @return RegistryInterface 119 | */ 120 | public function getRegistry() 121 | { 122 | return $this->registry; 123 | } 124 | 125 | /** 126 | * @param RegistryInterface $registry 127 | * @return FileSystemBuilder 128 | */ 129 | public function setRegistry(RegistryInterface $registry) 130 | { 131 | $this->registry = $registry; 132 | 133 | return $this; 134 | } 135 | 136 | /** 137 | * @return string 138 | */ 139 | public function getScheme() 140 | { 141 | return $this->scheme; 142 | } 143 | 144 | /** 145 | * @param string $scheme 146 | * @return FileSystemBuilder 147 | */ 148 | public function setScheme($scheme) 149 | { 150 | $this->scheme = $scheme; 151 | 152 | return $this; 153 | } 154 | 155 | /** 156 | * @return string 157 | */ 158 | public function getStreamWrapper() 159 | { 160 | return $this->wrapperClass; 161 | } 162 | 163 | /** 164 | * @param string $class 165 | * @return FileSystemBuilder 166 | */ 167 | public function setStreamWrapper($class) 168 | { 169 | $this->wrapperClass = $class; 170 | 171 | return $this; 172 | } 173 | 174 | /** 175 | * @return array 176 | */ 177 | public function getTree() 178 | { 179 | return $this->tree; 180 | } 181 | 182 | /** 183 | * @param array $tree 184 | * @return FileSystemBuilder 185 | */ 186 | public function setTree($tree) 187 | { 188 | $this->tree = $tree; 189 | 190 | return $this; 191 | } 192 | 193 | /** 194 | * @return LoggerInterface 195 | */ 196 | protected function buildDefaultLogger() 197 | { 198 | return new PhpErrorLogger(); 199 | } 200 | 201 | /** 202 | * @return NodeFactoryInterface 203 | */ 204 | protected function buildDefaultNodeFactory() 205 | { 206 | return new NodeFactory(); 207 | } 208 | 209 | /** 210 | * @return NodeWalkerInterface 211 | */ 212 | protected function buildDefaultNodeWalker() 213 | { 214 | return new NodeWalker(); 215 | } 216 | 217 | /** 218 | * @return RegistryInterface 219 | */ 220 | protected function buildDefaultRegistry() 221 | { 222 | return FileSystemRegistry::getInstance(); 223 | } 224 | 225 | /** 226 | * @return string 227 | */ 228 | protected function buildDefaultStreamWrapper() 229 | { 230 | return 'Vfs\\Stream\\StreamWrapper'; 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /src/FileSystemInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs; 11 | 12 | use Psr\Log\LoggerInterface; 13 | use Vfs\Exception\RegisteredSchemeException; 14 | use Vfs\Exception\UnregisteredSchemeException; 15 | use Vfs\Node\Factory\NodeFactoryInterface; 16 | use Vfs\Node\Walker\NodeWalkerInterface; 17 | use Vfs\Node\NodeInterface; 18 | 19 | interface FileSystemInterface 20 | { 21 | const SCHEME = 'vfs'; 22 | 23 | /** 24 | * @param $path 25 | * @return NodeInterface 26 | */ 27 | public function get($path); 28 | 29 | /** 30 | * @return LoggerInterface 31 | */ 32 | public function getLogger(); 33 | 34 | /** 35 | * @return NodeFactoryInterface 36 | */ 37 | public function getNodeFactory(); 38 | 39 | /** 40 | * @return NodeWalkerInterface 41 | */ 42 | public function getNodeWalker(); 43 | 44 | /** 45 | * @return string 46 | */ 47 | public function getScheme(); 48 | 49 | /** 50 | * @return boolean 51 | * @throws RegisteredSchemeException If a mounted file system exists at scheme 52 | */ 53 | public function mount(); 54 | 55 | /** 56 | * @return boolean 57 | * @throws UnregisteredSchemeException If a mounted file system doesn't exist at scheme 58 | */ 59 | public function unmount(); 60 | } 61 | -------------------------------------------------------------------------------- /src/FileSystemRegistry.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs; 11 | 12 | use Vfs\Exception\RegisteredSchemeException; 13 | use Vfs\Exception\UnregisteredSchemeException; 14 | 15 | class FileSystemRegistry implements RegistryInterface 16 | { 17 | protected static $instance; 18 | protected $registered = []; 19 | 20 | /** 21 | * @param FileSystemRegistry[] $fss 22 | */ 23 | public function __construct(array $fss = []) 24 | { 25 | foreach ($fss as $name => $fs) { 26 | $this->add($name, $fs); 27 | } 28 | } 29 | 30 | /** 31 | * @return FileSystemRegistry 32 | */ 33 | public static function getInstance() 34 | { 35 | if (!self::$instance) { 36 | self::$instance = new self(); 37 | } 38 | 39 | return self::$instance; 40 | } 41 | 42 | public function add($scheme, FileSystemInterface $fs) 43 | { 44 | if ($this->has($scheme)) { 45 | throw new RegisteredSchemeException($scheme); 46 | } 47 | 48 | $this->registered[$scheme] = $fs; 49 | } 50 | 51 | public function get($scheme) 52 | { 53 | if (!$this->has($scheme)) { 54 | throw new UnregisteredSchemeException($scheme); 55 | } 56 | 57 | return $this->registered[$scheme]; 58 | } 59 | 60 | public function has($scheme) 61 | { 62 | return isset($this->registered[$scheme]); 63 | } 64 | 65 | public function remove($scheme) 66 | { 67 | if (!$this->has($scheme)) { 68 | throw new UnregisteredSchemeException($scheme); 69 | } 70 | 71 | unset($this->registered[$scheme]); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Logger/PhpErrorLogger.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Logger; 11 | 12 | use Psr\Log\AbstractLogger; 13 | use Psr\Log\LogLevel; 14 | 15 | class PhpErrorLogger extends AbstractLogger 16 | { 17 | public function log($level, $message, array $context = []) 18 | { 19 | switch ($level) { 20 | case LogLevel::EMERGENCY: 21 | case LogLevel::ALERT: 22 | case LogLevel::CRITICAL: 23 | case LogLevel::ERROR: 24 | trigger_error($this->format($message, $context), E_USER_ERROR); 25 | break; 26 | case LogLevel::WARNING: 27 | trigger_error($this->format($message, $context), E_USER_WARNING); 28 | break; 29 | case LogLevel::NOTICE: 30 | case LogLevel::INFO: 31 | case LogLevel::DEBUG: 32 | trigger_error($this->format($message, $context), E_USER_NOTICE); 33 | break; 34 | } 35 | } 36 | 37 | /** 38 | * @param string $message 39 | * @param array $context 40 | * @return string 41 | */ 42 | protected function format($message, array $context) 43 | { 44 | foreach ($context as $key => $value) { 45 | $message = str_replace(sprintf('{%s}', $key), $value, $message); 46 | } 47 | 48 | return $message . $this->formatTrace(debug_backtrace(false)); 49 | } 50 | 51 | /** 52 | * @param array $backtrace 53 | * @return string 54 | */ 55 | protected function formatTrace(array $backtrace) 56 | { 57 | $index = min((count($backtrace) + 1), 6); 58 | $origin = $backtrace[$index]; 59 | 60 | $file = isset($origin['file']) ? $origin['file'] : 'unknown'; 61 | $line = isset($origin['line']) ? $origin['line'] : 0; 62 | 63 | return sprintf(' in %s on line %d; triggered', $file, $line); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Node/Directory.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node; 11 | 12 | use ArrayIterator; 13 | use DateTime; 14 | use Vfs\Exception\ExistingNodeException; 15 | use Vfs\Exception\MissingNodeException; 16 | 17 | class Directory implements NodeContainerInterface 18 | { 19 | const DOT_SELF = '.'; 20 | const DOT_UP = '..'; 21 | 22 | protected $dateAccessed; 23 | protected $dateCreated; 24 | protected $dateModified; 25 | protected $mode; 26 | protected $nodes = []; 27 | 28 | /** 29 | * @param NodeInterface[] $nodes 30 | */ 31 | public function __construct(array $nodes = []) 32 | { 33 | $this->mode = self::TYPE_DIR | self::OTHER_FULL | self::USER_FULL; 34 | 35 | $this->dateAccessed = new DateTime(); 36 | $this->dateCreated = $this->dateAccessed; 37 | $this->dateModified = $this->dateAccessed; 38 | 39 | foreach ($nodes as $name => $node) { 40 | $this->add($name, $node); 41 | } 42 | 43 | $this->set(self::DOT_SELF, $this); 44 | } 45 | 46 | public function add($name, NodeInterface $node) 47 | { 48 | if ($this->has($name)) { 49 | throw new ExistingNodeException($name, $this); 50 | } 51 | 52 | $this->set($name, $node); 53 | } 54 | 55 | public function get($name) 56 | { 57 | if (!$this->has($name)) { 58 | throw new MissingNodeException($name, $this); 59 | } 60 | 61 | return $this->nodes[$name]; 62 | } 63 | 64 | public function has($name) 65 | { 66 | return isset($this->nodes[$name]); 67 | } 68 | 69 | public function remove($name) 70 | { 71 | if (!$this->has($name)) { 72 | throw new MissingNodeException($name, $this); 73 | } 74 | 75 | unset($this->nodes[$name]); 76 | } 77 | 78 | public function set($name, NodeInterface $node) 79 | { 80 | $this->nodes[$name] = $node; 81 | 82 | if (self::DOT_UP !== $name && $node instanceof NodeContainerInterface) { 83 | $node->set(self::DOT_UP, $this); 84 | } 85 | } 86 | 87 | public function getDateAccessed() 88 | { 89 | return $this->dateAccessed; 90 | } 91 | 92 | public function setDateAccessed(DateTime $dateTime) 93 | { 94 | $this->dateAccessed = $dateTime; 95 | } 96 | 97 | public function getDateCreated() 98 | { 99 | return $this->dateCreated; 100 | } 101 | 102 | public function getDateModified() 103 | { 104 | return $this->dateModified; 105 | } 106 | 107 | public function setDateModified(DateTime $dateTime) 108 | { 109 | $this->dateModified = $dateTime; 110 | } 111 | 112 | public function getIterator() 113 | { 114 | return new ArrayIterator($this->nodes); 115 | } 116 | 117 | public function getMode() 118 | { 119 | return $this->mode; 120 | } 121 | 122 | public function getSize() 123 | { 124 | $size = 0; 125 | 126 | foreach ($this->nodes as $name => $node) { 127 | if (!in_array($name, [self::DOT_SELF, self::DOT_UP])) { 128 | $size += $node->getSize(); 129 | } 130 | } 131 | 132 | return $size; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/Node/DirectoryLink.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node; 11 | 12 | use DateTime; 13 | 14 | class DirectoryLink implements NodeContainerInterface, LinkInterface 15 | { 16 | protected $dateAccessed; 17 | protected $dateCreated; 18 | protected $dateModified; 19 | protected $file; 20 | protected $mode; 21 | 22 | /** 23 | * @param NodeContainerInterface $directory 24 | */ 25 | public function __construct(NodeContainerInterface $directory) 26 | { 27 | $this->directory = $directory; 28 | $this->mode = self::TYPE_LINK | self::OTHER_FULL | self::USER_FULL; 29 | 30 | $this->dateAccessed = new DateTime(); 31 | $this->dateCreated = clone $this->dateAccessed; 32 | $this->dateModified = clone $this->dateAccessed; 33 | } 34 | 35 | public function add($name, NodeInterface $node) 36 | { 37 | $this->directory->add($name, $node); 38 | } 39 | 40 | public function get($name) 41 | { 42 | return $this->directory->get($name); 43 | } 44 | 45 | public function has($name) 46 | { 47 | return $this->directory->has($name); 48 | } 49 | 50 | public function remove($name) 51 | { 52 | $this->directory->remove($name); 53 | } 54 | 55 | public function set($name, NodeInterface $node) 56 | { 57 | $this->directory->set($name, $node); 58 | } 59 | 60 | public function getDateAccessed() 61 | { 62 | return $this->dateAccessed; 63 | } 64 | 65 | public function setDateAccessed(DateTime $dateTime) 66 | { 67 | $this->dateAccessed = $dateTime; 68 | } 69 | 70 | public function getDateCreated() 71 | { 72 | return $this->dateCreated; 73 | } 74 | 75 | public function getDateModified() 76 | { 77 | return $this->dateModified; 78 | } 79 | 80 | public function setDateModified(DateTime $dateTime) 81 | { 82 | $this->dateModified = $dateTime; 83 | } 84 | 85 | public function getIterator() 86 | { 87 | return $this->directory->getIterator(); 88 | } 89 | 90 | public function getMode() 91 | { 92 | return $this->mode; 93 | } 94 | 95 | public function getSize() 96 | { 97 | return $this->directory->getSize(); 98 | } 99 | 100 | public function getTarget() 101 | { 102 | return $this->directory; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Node/Factory/NodeFactory.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node\Factory; 11 | 12 | use LogicException; 13 | use Vfs\Node\Directory; 14 | use Vfs\Node\DirectoryLink; 15 | use Vfs\Node\File; 16 | use Vfs\Node\FileLink; 17 | use Vfs\Node\FileInterface; 18 | use Vfs\Node\NodeContainerInterface; 19 | 20 | class NodeFactory implements NodeFactoryInterface 21 | { 22 | public function buildDirectory(array $children = []) 23 | { 24 | return new Directory($children); 25 | } 26 | 27 | public function buildDirectoryLink(NodeContainerInterface $target) 28 | { 29 | return new DirectoryLink($target); 30 | } 31 | 32 | public function buildFile($content = '') 33 | { 34 | return new File($content); 35 | } 36 | 37 | public function buildFileLink(FileInterface $target) 38 | { 39 | return new FileLink($target); 40 | } 41 | 42 | public function buildTree(array $tree) 43 | { 44 | $nodes = []; 45 | 46 | foreach ($tree as $name => $content) { 47 | if (is_array($content)) { 48 | $nodes[$name] = $this->buildTree($content); 49 | } else { 50 | $nodes[$name] = $this->buildFile($content); 51 | } 52 | } 53 | 54 | return $this->buildDirectory($nodes); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Node/Factory/NodeFactoryInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node\Factory; 11 | 12 | use Vfs\Node\FileInterface; 13 | use Vfs\Node\LinkInterface; 14 | use Vfs\Node\NodeContainerInterface; 15 | use Vfs\Node\NodeInterface; 16 | 17 | interface NodeFactoryInterface 18 | { 19 | /** 20 | * @param NodeInterface[] $children 21 | * @return NodeContainerInterface 22 | */ 23 | public function buildDirectory(array $children = []); 24 | 25 | /** 26 | * @param NodeContainerInterface $target 27 | * @return LinkInterface 28 | */ 29 | public function buildDirectoryLink(NodeContainerInterface $target); 30 | 31 | /** 32 | * @param string $content 33 | * @return NodeInterface 34 | */ 35 | public function buildFile($content = ''); 36 | 37 | /** 38 | * @param FileInterface $file 39 | * @return LinkInterface 40 | */ 41 | public function buildFileLink(FileInterface $target); 42 | 43 | /** 44 | * @param array $tree 45 | * @return NodeContainerInterface 46 | */ 47 | public function buildTree(array $tree); 48 | } 49 | -------------------------------------------------------------------------------- /src/Node/File.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node; 11 | 12 | use DateTime; 13 | 14 | class File implements FileInterface 15 | { 16 | protected $dateAccessed; 17 | protected $dateCreated; 18 | protected $dateModified; 19 | protected $content; 20 | protected $mode; 21 | 22 | /** 23 | * @param string $content 24 | */ 25 | public function __construct($content = '') 26 | { 27 | $this->content = (string) $content; 28 | $this->mode = self::TYPE_FILE | self::OTHER_FULL | self::USER_FULL; 29 | 30 | $this->dateAccessed = new DateTime(); 31 | $this->dateCreated = clone $this->dateAccessed; 32 | $this->dateModified = clone $this->dateAccessed; 33 | } 34 | 35 | public function getContent() 36 | { 37 | return $this->content; 38 | } 39 | 40 | public function setContent($content) 41 | { 42 | $this->content = (string) $content; 43 | } 44 | 45 | public function getDateAccessed() 46 | { 47 | return $this->dateAccessed; 48 | } 49 | 50 | public function setDateAccessed(DateTime $dateTime) 51 | { 52 | $this->dateAccessed = $dateTime; 53 | } 54 | 55 | public function getDateCreated() 56 | { 57 | return $this->dateCreated; 58 | } 59 | 60 | public function getDateModified() 61 | { 62 | return $this->dateModified; 63 | } 64 | 65 | public function setDateModified(DateTime $dateTime) 66 | { 67 | $this->dateModified = $dateTime; 68 | } 69 | 70 | public function getMode() 71 | { 72 | return $this->mode; 73 | } 74 | 75 | public function getSize() 76 | { 77 | return strlen($this->content); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Node/FileInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node; 11 | 12 | interface FileInterface extends NodeInterface 13 | { 14 | /** 15 | * @return string 16 | */ 17 | public function getContent(); 18 | 19 | /** 20 | * @param string $content 21 | */ 22 | public function setContent($content); 23 | } 24 | -------------------------------------------------------------------------------- /src/Node/FileLink.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node; 11 | 12 | use DateTime; 13 | 14 | class FileLink implements FileInterface, LinkInterface 15 | { 16 | protected $dateAccessed; 17 | protected $dateCreated; 18 | protected $dateModified; 19 | protected $file; 20 | protected $mode; 21 | 22 | /** 23 | * @param FileInterface $file 24 | */ 25 | public function __construct(FileInterface $file) 26 | { 27 | $this->file = $file; 28 | $this->mode = self::TYPE_LINK | self::OTHER_FULL | self::USER_FULL; 29 | 30 | $this->dateAccessed = new DateTime(); 31 | $this->dateCreated = clone $this->dateAccessed; 32 | $this->dateModified = clone $this->dateAccessed; 33 | } 34 | 35 | public function getContent() 36 | { 37 | return $this->file->getContent(); 38 | } 39 | 40 | public function setContent($content) 41 | { 42 | $this->file->setContent($content); 43 | } 44 | 45 | public function getDateAccessed() 46 | { 47 | return $this->dateAccessed; 48 | } 49 | 50 | public function setDateAccessed(DateTime $dateTime) 51 | { 52 | $this->dateAccessed = $dateTime; 53 | } 54 | 55 | public function getDateCreated() 56 | { 57 | return $this->dateCreated; 58 | } 59 | 60 | public function getDateModified() 61 | { 62 | return $this->dateModified; 63 | } 64 | 65 | public function setDateModified(DateTime $dateTime) 66 | { 67 | $this->dateModified = $dateTime; 68 | } 69 | 70 | public function getMode() 71 | { 72 | return $this->mode; 73 | } 74 | 75 | public function getSize() 76 | { 77 | return $this->file->getSize(); 78 | } 79 | 80 | public function getTarget() 81 | { 82 | return $this->file; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Node/LinkInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node; 11 | 12 | /** 13 | * Link node type 14 | * 15 | * This is an implementation of a hard link rather than a symbolic link; the 16 | * key difference being it links directly to a node and not a path. If the 17 | * target node is moved or renamed the link remains intact. 18 | * 19 | * The implementation is a simple proxy, whereby most other method calls should 20 | * proxy through to the target where suitable. 21 | */ 22 | interface LinkInterface extends NodeInterface 23 | { 24 | /** 25 | * @return NodeInterface 26 | */ 27 | public function getTarget(); 28 | } 29 | -------------------------------------------------------------------------------- /src/Node/NodeContainerInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node; 11 | 12 | use IteratorAggregate; 13 | use Vfs\Exception\ExistingNodeException; 14 | use Vfs\Exception\MissingNodeException; 15 | 16 | interface NodeContainerInterface extends NodeInterface, IteratorAggregate 17 | { 18 | /** 19 | * @param string $name 20 | * @param NodeInterface $node 21 | * @throws ExistingNodeException If a node exists in container with name 22 | */ 23 | public function add($name, NodeInterface $node); 24 | 25 | /** 26 | * @param string $name 27 | * @throws MissingNodeException If a node doesn't exist in container with name 28 | */ 29 | public function get($name); 30 | 31 | /** 32 | * @param string $name 33 | * @return boolean 34 | */ 35 | public function has($name); 36 | 37 | /** 38 | * @param string $name 39 | * @throws MissingNodeException If a node doesn't exist in container with name 40 | */ 41 | public function remove($name); 42 | 43 | /** 44 | * @param string $name 45 | * @param NodeInterface $node 46 | */ 47 | public function set($name, NodeInterface $node); 48 | } 49 | -------------------------------------------------------------------------------- /src/Node/NodeInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node; 11 | 12 | use DateTime; 13 | 14 | interface NodeInterface extends StatInterface 15 | { 16 | /** 17 | * @return DateTime 18 | */ 19 | public function getDateAccessed(); 20 | 21 | /** 22 | * @param DateTime $dateTime 23 | */ 24 | public function setDateAccessed(DateTime $dateTime); 25 | 26 | /** 27 | * @return DateTime 28 | */ 29 | public function getDateCreated(); 30 | 31 | /** 32 | * @return DateTime 33 | */ 34 | public function getDateModified(); 35 | 36 | /** 37 | * @param DateTime $dateTime 38 | */ 39 | public function setDateModified(DateTime $dateTime); 40 | 41 | /** 42 | * @return integer 43 | */ 44 | public function getSize(); 45 | } 46 | -------------------------------------------------------------------------------- /src/Node/StatInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node; 11 | 12 | /** 13 | * Mode bits for node description and permissions 14 | * 15 | * @link http://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html 16 | * @link http://man7.org/linux/man-pages/man2/stat.2.html 17 | */ 18 | interface StatInterface 19 | { 20 | const S_IFMT = 0170000; 21 | const S_IFSOCK = 0140000; 22 | const S_IFLNK = 0120000; 23 | const S_IFREG = 0100000; 24 | const S_IFBLK = 0060000; 25 | const S_IFDIR = 0040000; 26 | const S_IFCHR = 0020000; 27 | const S_IFIFO = 0010000; 28 | const S_ISUID = 0004000; 29 | const S_ISGID = 0002000; 30 | const S_ISVTX = 0001000; 31 | const S_IRWXU = 0000700; 32 | const S_IRUSR = 0000400; 33 | const S_IWUSR = 0000200; 34 | const S_IXUSR = 0000100; 35 | const S_IRWXG = 0000070; 36 | const S_IRGRP = 0000040; 37 | const S_IWGRP = 0000020; 38 | const S_IXGRP = 0000010; 39 | const S_IRWXO = 0000007; 40 | const S_IROTH = 0000004; 41 | const S_IWOTH = 0000002; 42 | const S_IXOTH = 0000001; 43 | 44 | const TYPE_MASK = self::S_IFMT; 45 | const TYPE_SOCKET = self::S_IFSOCK; 46 | const TYPE_LINK = self::S_IFLNK; 47 | const TYPE_FILE = self::S_IFREG; 48 | const TYPE_DIR = self::S_IFDIR; 49 | const TYPE_PIPE = self::S_IFIFO; 50 | const DEV_BLOCK = self::S_IFBLK; 51 | const DEV_CHAR = self::S_IFCHR; 52 | 53 | const SET_GROUP = self::S_ISGID; 54 | const SET_USER = self::S_ISUID; 55 | const SET_STICKY = self::S_ISVTX; 56 | 57 | const GROUP_EXEC = self::S_IXGRP; 58 | const GROUP_FULL = self::S_IRWXG; 59 | const GROUP_WRITE = self::S_IWGRP; 60 | const GROUP_READ = self::S_IRGRP; 61 | const OTHER_EXEC = self::S_IXOTH; 62 | const OTHER_FULL = self::S_IRWXO; 63 | const OTHER_WRITE = self::S_IWOTH; 64 | const OTHER_READ = self::S_IROTH; 65 | const USER_EXEC = self::S_IXUSR; 66 | const USER_FULL = self::S_IRWXU; 67 | const USER_READ = self::S_IRUSR; 68 | const USER_WRITE = self::S_IWUSR; 69 | 70 | /** 71 | * @return integer 72 | */ 73 | public function getMode(); 74 | } 75 | -------------------------------------------------------------------------------- /src/Node/Walker/NodeWalker.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node\Walker; 11 | 12 | use Vfs\Node\NodeContainerInterface; 13 | use Vfs\Node\NodeInterface; 14 | 15 | class NodeWalker implements NodeWalkerInterface 16 | { 17 | protected $separator; 18 | 19 | /** 20 | * @param string $separator 21 | */ 22 | public function __construct($separator = DIRECTORY_SEPARATOR) 23 | { 24 | $this->separator = $separator; 25 | } 26 | 27 | public function findNode(NodeInterface $root, $path) 28 | { 29 | return $this->walkPath($root, $path, function (NodeInterface $node, $name) { 30 | if (!$node instanceof NodeContainerInterface || !$node->has($name)) { 31 | return null; 32 | } 33 | 34 | return $node->get($name); 35 | }); 36 | } 37 | 38 | public function walkPath(NodeInterface $root, $path, callable $fn) 39 | { 40 | $parts = $this->splitPath($path); 41 | $name = current($parts); 42 | $node = $root; 43 | 44 | while ($node && $name) { 45 | $node = $fn($node, $name); 46 | $name = next($parts); 47 | } 48 | 49 | return $node; 50 | } 51 | 52 | /** 53 | * @param string $path 54 | * @return string[] 55 | */ 56 | protected function splitPath($path) 57 | { 58 | $path = str_replace('/', DIRECTORY_SEPARATOR, $path); 59 | 60 | return array_filter(explode($this->separator, $path)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Node/Walker/NodeWalkerInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Node\Walker; 11 | 12 | use Vfs\Node\NodeInterface; 13 | 14 | interface NodeWalkerInterface 15 | { 16 | /** 17 | * @param NodeInterface $root 18 | * @param string $path 19 | * @return NodeInterface 20 | */ 21 | public function findNode(NodeInterface $root, $path); 22 | 23 | /** 24 | * @param NodeInterface $root 25 | * @param string $path 26 | * @param callable $fn 27 | * @return NodeInterface 28 | */ 29 | public function walkPath(NodeInterface $root, $path, callable $fn); 30 | } 31 | -------------------------------------------------------------------------------- /src/RegistryInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs; 11 | 12 | use Vfs\Exception\RegisteredSchemeException; 13 | use Vfs\Exception\UnregisteredSchemeException; 14 | 15 | interface RegistryInterface 16 | { 17 | /** 18 | * @param string $scheme 19 | * @param FileSystemInterface $fs 20 | * @throws RegisteredSchemeException If a mounted file system exists at scheme 21 | */ 22 | public function add($scheme, FileSystemInterface $fs); 23 | 24 | /** 25 | * @param string $scheme 26 | * @return FileSystemInterface 27 | * @throws UnregisteredSchemeException If a mounted file system doesn't exist at scheme 28 | */ 29 | public function get($scheme); 30 | 31 | /** 32 | * @param string $scheme 33 | * @return boolean 34 | */ 35 | public function has($scheme); 36 | 37 | /** 38 | * @param string $scheme 39 | * @return FileSystemInterface 40 | * @throws UnregisteredSchemeException If a mounted file system doesn't exist at scheme 41 | */ 42 | public function remove($scheme); 43 | } 44 | -------------------------------------------------------------------------------- /src/Stream/AbstractHandle.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Stream; 11 | 12 | use Vfs\FileSystemInterface; 13 | use Vfs\Node\NodeInterface; 14 | 15 | abstract class AbstractHandle implements HandleInterface 16 | { 17 | protected $fs; 18 | protected $node; 19 | protected $mode; 20 | protected $modifier; 21 | protected $path; 22 | protected $scheme; 23 | protected $url; 24 | 25 | /** 26 | * @param FileSystemInterface $fs 27 | * @param string $url 28 | * @param string $mode 29 | */ 30 | public function __construct(FileSystemInterface $fs, $url, $mode = null) 31 | { 32 | $this->fs = $fs; 33 | $this->url = $url; 34 | 35 | list($this->mode, $this->modifier) = $this->parseMode($mode); 36 | list($this->scheme, $this->path) = $this->parseUrl($url); 37 | } 38 | 39 | public function getNode() 40 | { 41 | return $this->node; 42 | } 43 | 44 | public function rename($target) 45 | { 46 | $this->node = $this->findNode($this->path); 47 | $parent = $this->fs->get(dirname($this->path)); 48 | 49 | list($_, $targetPath) = $this->parseUrl($target); 50 | $targetParent = $this->fs->get(dirname($targetPath)); 51 | 52 | if (!$this->node || !$targetPath) { 53 | $this->node = null; 54 | $this->warn('rename({origin},{target}): No such file or directory', [ 55 | 'origin' => $this->url, 56 | 'target' => $target 57 | ]); 58 | } else { 59 | $parent->remove(basename($this->path)); 60 | $targetParent->add(basename($targetPath), $this->node); 61 | } 62 | 63 | return $this->node; 64 | } 65 | 66 | /** 67 | * @return NodeInterface 68 | */ 69 | protected function findNode() 70 | { 71 | return $this->fs->get($this->path); 72 | } 73 | 74 | /** 75 | * @param string $mode 76 | * @return string[] 77 | */ 78 | protected function parseMode($mode) 79 | { 80 | return [substr($mode, 0, 1), substr($mode, 1, 2)]; 81 | } 82 | 83 | /** 84 | * @param string $url 85 | * @return string[] 86 | */ 87 | protected function parseUrl($url) 88 | { 89 | $parts = parse_url($url); 90 | $path = null; 91 | $scheme = null; 92 | 93 | if (isset($parts['scheme'])) { 94 | $scheme = $parts['scheme']; 95 | } else { 96 | $scheme = strstr($url, '://', true); 97 | } 98 | 99 | $path = '/' . ltrim(substr($url, strlen($scheme)), ':\/'); 100 | 101 | return [$scheme, $path]; 102 | } 103 | 104 | /** 105 | * @param string $message 106 | * @param array $context 107 | */ 108 | protected function warn($message, array $context = []) 109 | { 110 | $this->fs->getLogger()->warning($message, $context); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/Stream/DirectoryHandle.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Stream; 11 | 12 | use Vfs\Node\NodeContainerInterface; 13 | use Vfs\Exception\UnopenedHandleException; 14 | 15 | class DirectoryHandle extends AbstractHandle 16 | { 17 | public function canRead() 18 | { 19 | return true; 20 | } 21 | 22 | public function create($perms, $recursive = false) 23 | { 24 | $this->node = $this->findNode(); 25 | 26 | if (!$this->node) { 27 | $parentPath = dirname($this->path); 28 | 29 | $parent = $this->fs->get($parentPath); 30 | if (!$parent && (boolean) $recursive) { 31 | $parent = $this->buildNodesRecursive($this->fs->get('/')); 32 | } 33 | 34 | if ($parent) { 35 | $this->node = $this->fs->getNodeFactory()->buildDirectory(); 36 | $parent->add(basename($this->path), $this->node); 37 | } else { 38 | $this->warn('mkdir({url}): No such file or directory', [ 39 | 'url' => $this->url 40 | ]); 41 | } 42 | } else { 43 | $this->warn('mkdir({url}): File exists', ['url' => $this->url]); 44 | $this->node = null; 45 | } 46 | 47 | return $this->node; 48 | } 49 | 50 | public function destroy() 51 | { 52 | $this->node = $this->findNode(); 53 | 54 | if (!$this->node) { 55 | return (boolean) $this->warn('rmdir({url}): No such file or directory', [ 56 | 'url' => $this->url 57 | ]); 58 | } elseif (!$this->node instanceof NodeContainerInterface) { 59 | return (boolean) $this->warn('rmdir({url}): Not a directory', [ 60 | 'url' => $this->url 61 | ]); 62 | } 63 | 64 | $parent = $fs->get(dirname($this->path)); 65 | $parent->remove(basename($this->path)); 66 | 67 | return true; 68 | } 69 | 70 | public function open() 71 | { 72 | return $this->node = $this->findNode(); 73 | } 74 | 75 | public function read($offset = 0) 76 | { 77 | if (!$this->node) { 78 | throw new UnopenedHandleException($this, $this->url); 79 | } 80 | 81 | $i = 0; 82 | foreach ($this->node as $name => $node) { 83 | if ($i++ === $offset) { 84 | return $name; 85 | } 86 | } 87 | 88 | return false; 89 | } 90 | 91 | public function write($content) 92 | { 93 | return false; 94 | } 95 | 96 | /** 97 | * @param NodeContainerInterface $root 98 | * @return NodeContainerInterface 99 | */ 100 | protected function buildNodesRecursive(NodeContainerInterface $root) 101 | { 102 | $factory = $this->fs->getNodeFactory(); 103 | $walker = $this->fs->getNodeWalker(); 104 | 105 | return $walker->walkPath($root, $this->path, function ($node, $name) use ($factory) { 106 | if (!$node->has($name)) { 107 | $node->add($name, $factory->buildDirectory()); 108 | } 109 | 110 | return $node->get($name); 111 | }); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/Stream/FileHandle.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Stream; 11 | 12 | use DateTime; 13 | use Vfs\Exception\UnopenedHandleException; 14 | use Vfs\Node\FileInterface; 15 | use Vfs\Node\NodeContainerInterface; 16 | 17 | class FileHandle extends AbstractHandle 18 | { 19 | public function canRead() 20 | { 21 | return self::MODE_READ === $this->mode || self::MOD_EXTENDED === $this->modifier; 22 | } 23 | 24 | public function create($perms) 25 | { 26 | return $this->node = $this->findOrBuildNode(); 27 | } 28 | 29 | public function destroy() 30 | { 31 | $this->node = $this->findNode(); 32 | 33 | if (!$this->node) { 34 | return (boolean) $this->warn('unlink({url}): No such file or directory', [ 35 | 'url' => $this->url 36 | ]); 37 | } elseif ($this->node instanceof NodeContainerInterface) { 38 | return (boolean) $this->warn('unlink({url}): Is a directory', [ 39 | 'url' => $this->url 40 | ]); 41 | } 42 | 43 | $parent = $this->fs->get(dirname($this->path)); 44 | $parent->remove(basename($this->path)); 45 | 46 | return true; 47 | } 48 | 49 | public function open() 50 | { 51 | $this->node = $this->findOrBuildNode(); 52 | 53 | if ($this->node instanceof FileInterface && self::MODE_TRUNCATE === $this->mode) { 54 | $this->node->setContent(''); 55 | } 56 | 57 | return $this->node; 58 | } 59 | 60 | public function read($offset = 0, $length = null) 61 | { 62 | if (!$this->node) { 63 | throw new UnopenedHandleException($this, $this->url); 64 | } elseif (!$this->node instanceof FileInterface) { 65 | return ''; 66 | } 67 | 68 | if (null !== $length) { 69 | return substr($this->node->getContent(), $offset, $length); 70 | } 71 | 72 | return substr($this->node->getContent(), $offset); 73 | } 74 | 75 | public function touch(DateTime $mtime = null, DateTime $atime = null) 76 | { 77 | $node = $this->findOrBuildNode(); 78 | 79 | if (!$node) { 80 | throw new UnopenedHandleException($this, $this->url); 81 | } 82 | 83 | $mtime = $mtime ?: new DateTime(); 84 | $atime = $atime ?: clone $mtime; 85 | 86 | $node->setDateAccessed($atime); 87 | $node->setDateModified($mtime); 88 | 89 | return $node; 90 | } 91 | 92 | public function write($content) 93 | { 94 | if (!$this->node) { 95 | throw new UnopenedHandleException($this, $this->url); 96 | } 97 | 98 | $this->node->setContent($content); 99 | 100 | return true; 101 | } 102 | 103 | /** 104 | * @return NodeInterface 105 | */ 106 | protected function findOrBuildNode() 107 | { 108 | $this->node = $this->fs->get($this->path); 109 | 110 | if ($this->node && self::MODE_WRITE === $this->mode) { 111 | $this->node = null; 112 | } elseif (!$this->node && in_array($this->mode, [ 113 | self::MODE_APPEND, 114 | self::MODE_TRUNCATE, 115 | self::MODE_WRITE, 116 | self::MODE_WRITE_NEW 117 | ])) { 118 | $dir = $this->fs->get(dirname($this->path)); 119 | 120 | if ($dir && $dir instanceof NodeContainerInterface) { 121 | $this->node = $this->fs->getNodeFactory()->buildFile(); 122 | $dir->set(basename($this->path), $this->node); 123 | } 124 | } 125 | 126 | return $this->node; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/Stream/HandleInterface.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Stream; 11 | 12 | use Vfs\Node\NodeInterface; 13 | 14 | interface HandleInterface 15 | { 16 | const MODE_APPEND = 'a'; 17 | const MODE_READ = 'r'; 18 | const MODE_TRUNCATE = 'w'; 19 | const MODE_WRITE = 'x'; 20 | const MODE_WRITE_NEW = 'c'; 21 | const MOD_BINARY = 'b'; 22 | const MOD_EXTENDED = '+'; 23 | const MOD_TEXT = 't'; 24 | 25 | /** 26 | * @return boolean 27 | */ 28 | public function canRead(); 29 | 30 | /** 31 | * @param integer $perms 32 | * @return NodeInterface 33 | */ 34 | public function create($perms); 35 | 36 | /** 37 | * @return boolean 38 | */ 39 | public function destroy(); 40 | 41 | /** 42 | * @return NodeInterface 43 | */ 44 | public function getNode(); 45 | 46 | /** 47 | * @return NodeInterface 48 | */ 49 | public function open(); 50 | 51 | /** 52 | * @param string $origin 53 | * @param string $target 54 | * @return NodeInterface 55 | */ 56 | public function rename($target); 57 | 58 | /** 59 | * @param integer $offset 60 | * @return string 61 | */ 62 | public function read($offset = 0); 63 | 64 | /** 65 | * @param string $content 66 | * @return boolean 67 | */ 68 | public function write($content); 69 | } 70 | -------------------------------------------------------------------------------- /src/Stream/StreamWrapper.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Stream; 11 | 12 | use DateTime; 13 | use Vfs\Node\LinkInterface; 14 | use Vfs\FileSystemRegistry; 15 | 16 | class StreamWrapper 17 | { 18 | protected $cursor = 0; 19 | protected $handle; 20 | protected $mode; 21 | protected $url; 22 | 23 | /** 24 | * @return boolean 25 | */ 26 | public function dir_closedir() 27 | { 28 | $this->stream_close(); 29 | 30 | return true; 31 | } 32 | 33 | /** 34 | * @param string $url 35 | * @param integer $options 36 | * @return boolean 37 | */ 38 | public function dir_opendir($url, $options) 39 | { 40 | $this->handle = $this->buildDirectoryHandle($url); 41 | 42 | return (boolean) $this->handle->open(); 43 | } 44 | 45 | /** 46 | * @return string 47 | */ 48 | public function dir_readdir() 49 | { 50 | return $this->handle->read($this->cursor++); 51 | } 52 | 53 | /** 54 | * @return boolean 55 | */ 56 | public function dir_rewinddir() 57 | { 58 | $this->cursor = 0; 59 | 60 | return true; 61 | } 62 | 63 | /** 64 | * @param string $url 65 | * @param integer $perms 66 | * @param integer $flags 67 | * @return boolean 68 | */ 69 | public function mkdir($url, $perms, $flags) 70 | { 71 | $this->handle = $this->buildDirectoryHandle($url); 72 | $recursive = $this->checkBit($flags, STREAM_MKDIR_RECURSIVE); 73 | 74 | return (boolean) $this->handle->create($perms, $recursive); 75 | } 76 | 77 | /** 78 | * @param string $origin 79 | * @param string $target 80 | * @return boolean 81 | */ 82 | public function rename($origin, $target) 83 | { 84 | $this->handle = $this->buildFileHandle($origin); 85 | 86 | return (boolean) $this->handle->rename($target); 87 | } 88 | 89 | /** 90 | * @param string $url 91 | * @param integer $options 92 | * @return boolean 93 | */ 94 | public function rmdir($url, $options) 95 | { 96 | $this->handle = $this->buildDirectoryHandle($url); 97 | 98 | return (boolean) $this->handle->destroy(); 99 | } 100 | 101 | /** 102 | * @param integer $cast 103 | * @return resource|boolean 104 | */ 105 | public function stream_cast($cast) 106 | { 107 | return false; // No underlying resource 108 | } 109 | 110 | /** 111 | */ 112 | public function stream_close() 113 | { 114 | $this->cursor = 0; 115 | $this->handle = null; 116 | } 117 | 118 | /** 119 | * @return boolean 120 | */ 121 | public function stream_eof() 122 | { 123 | $node = $this->handle->getNode(); 124 | 125 | return !$node || $node->getSize() <= $this->cursor; 126 | } 127 | 128 | /** 129 | * @return boolean 130 | */ 131 | public function stream_flush() 132 | { 133 | return true; // Non-buffered writing 134 | } 135 | 136 | /** 137 | * @param string $url 138 | * @param integer $option 139 | * @param mixed $args 140 | * @return boolean 141 | */ 142 | public function stream_metadata($url, $option, $args) 143 | { 144 | if (STREAM_META_TOUCH === $option) { 145 | $this->handle = $this->buildFileHandle($url, HandleInterface::MODE_WRITE_NEW); 146 | 147 | $mtime = isset($args[0]) ? new DateTime(sprintf('@%s', $args[0])) : null; 148 | $atime = isset($args[1]) ? new DateTime(sprintf('@%s', $args[1])) : $mtime; 149 | 150 | $this->handle->touch($mtime, $atime); 151 | 152 | return true; 153 | } 154 | 155 | return false; 156 | } 157 | 158 | /** 159 | * @param string $url 160 | * @param string $mode 161 | * @param integer $options 162 | * @param string $openedPath 163 | * @return boolean 164 | */ 165 | public function stream_open($url, $mode, $options, &$openedPath) 166 | { 167 | $this->cursor = 0; 168 | $this->handle = $this->buildFileHandle($url, $mode); 169 | $node = $this->handle->open(); 170 | 171 | if ($node && $this->checkBit($options, STREAM_USE_PATH)) { 172 | $openedPath = $url; 173 | } 174 | 175 | if (isset($mode[0]) && $node && HandleInterface::MODE_APPEND === $mode[0]) { 176 | $this->cursor = $node->getSize(); 177 | } 178 | 179 | return (boolean) $node; 180 | } 181 | 182 | /** 183 | * @param integer $length 184 | * @return string|boolean 185 | */ 186 | public function stream_read($length) 187 | { 188 | if ($this->handle->canRead()) { 189 | $out = $this->handle->read($this->cursor, $length); 190 | $this->cursor += strlen($out); 191 | 192 | return $out; 193 | } 194 | 195 | return false; 196 | } 197 | 198 | /** 199 | * @param integer $offset 200 | * @param integer $whence 201 | * @return boolean 202 | */ 203 | public function stream_seek($offset, $whence = SEEK_SET) 204 | { 205 | switch ($whence) { 206 | case SEEK_SET: 207 | $this->cursor = (integer) $offset; 208 | break; 209 | case SEEK_CUR: 210 | $this->cursor += (integer) $offset; 211 | break; 212 | case SEEK_END: 213 | $length = strlen($this->wrapper->read()); 214 | $this->cursor = $length + (integer) $offset; 215 | break; 216 | default: 217 | return false; 218 | } 219 | 220 | return true; 221 | } 222 | 223 | /** 224 | * @param integer $option 225 | * @param integer $arg1 226 | * @param integer $arg2 227 | * @return boolean 228 | */ 229 | public function stream_set_option($option, $arg1, $arg2) 230 | { 231 | return true; 232 | } 233 | 234 | /** 235 | * @param boolean $followLink 236 | * @return array|boolean 237 | */ 238 | public function stream_stat($followLink = false) 239 | { 240 | $node = $this->handle->getNode(); 241 | 242 | if (!$node) { 243 | return false; 244 | } elseif ($followLink && $node instanceof LinkInterface) { 245 | $node = $node->getTarget(); 246 | } 247 | 248 | $stat = [ 249 | 'dev' => 0, 250 | 'ino' => 0, 251 | 'mode' => $node->getMode(), 252 | 'nlink' => 0, 253 | 'uid' => 0, 254 | 'gid' => 0, 255 | 'rdev' => 0, 256 | 'size' => $node->getSize(), 257 | 'atime' => $node->getDateAccessed()->getTimestamp(), 258 | 'mtime' => $node->getDateModified()->getTimestamp(), 259 | 'ctime' => $node->getDateCreated()->getTimestamp(), 260 | 'blksize' => -1, 261 | 'blocks' => -1 262 | ]; 263 | 264 | return array_values($stat) + $stat; 265 | } 266 | 267 | /** 268 | * @return integer 269 | */ 270 | public function stream_tell() 271 | { 272 | return $this->cursor; 273 | } 274 | 275 | /** 276 | * @param integer $size 277 | * @return boolean 278 | */ 279 | public function stream_truncate($size) 280 | { 281 | if ($size > $current) { 282 | $this->handle->write($this->handle->read() . str_repeat("\0", $size - $current)); 283 | } else { 284 | $this->handle->write($this->handle->read(0, $size)); 285 | } 286 | 287 | return true; 288 | } 289 | 290 | /** 291 | * @param string $data 292 | * @return integer 293 | */ 294 | public function stream_write($data) 295 | { 296 | $content = substr($this->handle->read(0, $this->cursor), 0, $this->cursor) . $data; 297 | $written = $this->handle->write($content); 298 | 299 | $this->cursor = strlen($content); 300 | 301 | return $written ? strlen($data) : 0; 302 | } 303 | 304 | /** 305 | * @param string $url 306 | * @return boolean 307 | */ 308 | public function unlink($url) 309 | { 310 | $this->handle = $this->buildFileHandle($url); 311 | 312 | return (boolean) $this->handle->destroy(); 313 | } 314 | 315 | /** 316 | * @param string $url 317 | * @param integer $flags 318 | * @return array 319 | */ 320 | public function url_stat($url, $flags) 321 | { 322 | $this->handle = $this->buildFileHandle($url); 323 | $this->handle->open(); 324 | 325 | return $this->stream_stat(!$this->checkBit($flags, STREAM_URL_STAT_LINK)); 326 | } 327 | 328 | /** 329 | * @param string $url 330 | * @return DirectoryHandle 331 | */ 332 | protected function buildDirectoryHandle($url) 333 | { 334 | return new DirectoryHandle($this->getFileSystemForUrl($url), $url); 335 | } 336 | 337 | /** 338 | * @param string $url 339 | * @param string $mode 340 | * @return FileHandle 341 | */ 342 | protected function buildFileHandle($url, $mode = null) 343 | { 344 | return new FileHandle($this->getFileSystemForUrl($url), $url, $mode); 345 | } 346 | 347 | /** 348 | * @param integer $mask 349 | * @param integer $bit 350 | * @return boolean 351 | */ 352 | protected function checkBit($mask, $bit) 353 | { 354 | return ($mask & $bit) === $bit; 355 | } 356 | 357 | /** 358 | * @param string $url 359 | * @return FileSystemInterface 360 | */ 361 | protected function getFileSystemForUrl($url) 362 | { 363 | $parts = parse_url($url); 364 | $scheme = isset($parts['scheme']) ? $parts['scheme'] : strstr($url, '://', true); 365 | 366 | return FileSystemRegistry::getInstance()->get($scheme); 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /test/acceptance/Stream/StreamWrapper/FileGetContentsAcceptanceTest.php: -------------------------------------------------------------------------------- 1 | [ 10 | 'bar' => 'baz' 11 | ] 12 | ]; 13 | 14 | public function testGetDirectory() 15 | { 16 | $this->assertEquals('', file_get_contents("$this->scheme:///foo")); 17 | } 18 | 19 | public function testGetFile() 20 | { 21 | $this->assertEquals($this->tree['foo']['bar'], file_get_contents("$this->scheme:///foo/bar")); 22 | } 23 | 24 | public function testPutFile() 25 | { 26 | file_put_contents("$this->scheme:///foo/bar", 'bar'); 27 | 28 | $this->assertEquals('bar', $this->fs->get('/foo/bar')->getContent()); 29 | } 30 | 31 | public function testPutExistingFile() 32 | { 33 | file_put_contents("$this->scheme:///foo/bar", '_updated'); 34 | 35 | $this->assertEquals('_updated', $this->fs->get('/foo/bar')->getContent()); 36 | } 37 | 38 | public function testPutAppendExistingFile() 39 | { 40 | file_put_contents("$this->scheme:///foo/bar", '_updated', FILE_APPEND); 41 | 42 | $this->assertEquals($this->tree['foo']['bar'] . '_updated', $this->fs->get('/foo/bar')->getContent()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/acceptance/Stream/StreamWrapper/FopenAcceptanceTest.php: -------------------------------------------------------------------------------- 1 | [ 10 | 'bar' => 'baz' 11 | ] 12 | ]; 13 | 14 | public function dataFopen() 15 | { 16 | return [ 17 | ['a' ], ['r' ], ['w' ], ['c' ], 18 | ['ab'], ['rb'], ['wb'], ['cb'], 19 | ['a+'], ['r+'], ['w+'], ['c+'], 20 | ['at'], ['rt'], ['wt'], ['ct'] 21 | ]; 22 | } 23 | 24 | public function dataFopenError() 25 | { 26 | return [ 27 | ['x'], ['xb'], ['x+'], ['xt'] 28 | ]; 29 | } 30 | 31 | public function dataFopenMissing() 32 | { 33 | return [ 34 | ['a' ], ['w' ], ['c' ], ['x' ], 35 | ['ab'], ['wb'], ['cb'], ['xb'], 36 | ['a+'], ['w+'], ['c+'], ['x+'], 37 | ['at'], ['wt'], ['ct'], ['xt'] 38 | ]; 39 | } 40 | 41 | public function dataFopenMissingError() 42 | { 43 | return [ 44 | ['r'], ['rb'], ['r+'], ['rt'] 45 | ]; 46 | } 47 | 48 | /** 49 | * @dataProvider dataFopen 50 | */ 51 | public function testFopenFile($mode) 52 | { 53 | $this->assertTrue(is_resource(fopen("$this->scheme:///foo/bar", $mode))); 54 | } 55 | 56 | /** 57 | * @dataProvider dataFopenError 58 | */ 59 | public function testFopenFileError($mode) 60 | { 61 | $this->setExpectedException('PHPUnit_Framework_Error_Warning'); 62 | 63 | fopen("$this->scheme:///foo/bar", $mode); 64 | } 65 | 66 | /** 67 | * @dataProvider dataFopenMissing 68 | */ 69 | public function testFopenMissingFile($mode) 70 | { 71 | $this->assertTrue(is_resource(fopen("$this->scheme:///foo/baz", $mode))); 72 | } 73 | 74 | /** 75 | * @dataProvider dataFopenMissingError 76 | */ 77 | public function testFopenMissingFileError($mode) 78 | { 79 | $this->setExpectedException('PHPUnit_Framework_Error_Warning'); 80 | 81 | fopen("$this->scheme:///foo/baz", $mode); 82 | } 83 | 84 | /** 85 | * @dataProvider dataFopen 86 | */ 87 | public function testFopenDirectory($mode) 88 | { 89 | $this->assertTrue(is_resource(fopen("$this->scheme:///foo", $mode))); 90 | } 91 | 92 | /** 93 | * @dataProvider dataFopenError 94 | */ 95 | public function testFopenDirectoryError($mode) 96 | { 97 | $this->setExpectedException('PHPUnit_Framework_Error_Warning'); 98 | 99 | fopen("$this->scheme:///foo", $mode); 100 | } 101 | 102 | /** 103 | * @dataProvider dataFopenMissing 104 | */ 105 | public function testFopenMissingDirectory($mode) 106 | { 107 | $this->assertTrue(is_resource(fopen("$this->scheme:///baz", $mode))); 108 | } 109 | 110 | /** 111 | * @dataProvider dataFopenMissingError 112 | */ 113 | public function testFopenMissingDirectoryError($mode) 114 | { 115 | $this->setExpectedException('PHPUnit_Framework_Error_Warning'); 116 | 117 | fopen("$this->scheme:///baz", $mode); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /test/acceptance/Stream/StreamWrapper/FreadAcceptanceTest.php: -------------------------------------------------------------------------------- 1 | [ 10 | 'bar' => 'baz' 11 | ] 12 | ]; 13 | 14 | public function dataFreadFile() 15 | { 16 | $b = 'bar'; 17 | return [ 18 | ['a', $b, 1, '' ], ['a', $b, 2, '' ], ['a', $b, 3, '' ], ['a', $b, 4, '' ], 19 | ['r', $b, 1, 'b'], ['r', $b, 2, 'ba'], ['r', $b, 3, 'baz'], ['r', $b, 4, 'baz'], 20 | ['w', $b, 1, '' ], ['w', $b, 2, '' ], ['w', $b, 3, '' ], ['w', $b, 4, '' ], 21 | ['c', $b, 1, '' ], ['c', $b, 2, '' ], ['c', $b, 3, '' ], ['c', $b, 4, '' ], 22 | ['ab', $b, 1, '' ], ['ab', $b, 2, '' ], ['ab', $b, 3, '' ], ['ab', $b, 4, '' ], 23 | ['rb', $b, 1, 'b'], ['rb', $b, 2, 'ba'], ['rb', $b, 3, 'baz'], ['rb', $b, 4, 'baz'], 24 | ['wb', $b, 1, '' ], ['wb', $b, 2, '' ], ['wb', $b, 3, '' ], ['wb', $b, 4, '' ], 25 | ['cb', $b, 1, '' ], ['cb', $b, 2, '' ], ['cb', $b, 3, '' ], ['cb', $b, 4, '' ], 26 | ['a+', $b, 1, '' ], ['a+', $b, 2, '' ], ['a+', $b, 3, '' ], ['a+', $b, 4, '' ], 27 | ['r+', $b, 1, 'b'], ['r+', $b, 2, 'ba'], ['r+', $b, 3, 'baz'], ['r+', $b, 4, 'baz'], 28 | ['w+', $b, 1, '' ], ['w+', $b, 2, '' ], ['w+', $b, 3, '' ], ['w+', $b, 4, '' ], 29 | ['c+', $b, 1, 'b'], ['c+', $b, 2, 'ba'], ['c+', $b, 3, 'baz'], ['c+', $b, 4, 'baz'], 30 | ['at', $b, 1, '' ], ['at', $b, 2, '' ], ['at', $b, 3, '' ], ['at', $b, 4, '' ], 31 | ['rt', $b, 1, 'b'], ['rt', $b, 2, 'ba'], ['rt', $b, 3, 'baz'], ['rt', $b, 4, 'baz'], 32 | ['wt', $b, 1, '' ], ['wt', $b, 2, '' ], ['wt', $b, 3, '' ], ['wt', $b, 4, '' ], 33 | ['ct', $b, 1, '' ], ['ct', $b, 2, '' ], ['ct', $b, 3, '' ], ['ct', $b, 4, '' ] 34 | ]; 35 | } 36 | 37 | public function dataFreadMissingFile() 38 | { 39 | $b = 'bar'; 40 | return [ 41 | ['a', ''], 42 | ['x', ''], 43 | ['w', ''], 44 | ['c', ''], 45 | ['ab', ''], 46 | ['xb', ''], 47 | ['wb', ''], 48 | ['cb', ''], 49 | ['a+', ''], 50 | ['x+', ''], 51 | ['w+', ''], 52 | ['c+', ''], 53 | ['at', ''], 54 | ['xt', ''], 55 | ['wt', ''], 56 | ['ct', ''] 57 | ]; 58 | } 59 | 60 | /** 61 | * @dataProvider dataFreadFile 62 | */ 63 | public function testFreadFile($mode, $content, $size, $expectation) 64 | { 65 | $resource = fopen("$this->scheme:///foo/bar", $mode); 66 | 67 | $this->assertEquals($expectation, fread($resource, $size)); 68 | 69 | fclose($resource); 70 | } 71 | 72 | /** 73 | * @dataProvider dataFreadMissingFile 74 | */ 75 | public function testFreadMissingFile($mode) 76 | { 77 | $resource = fopen("$this->scheme:///bar", $mode); 78 | 79 | $this->assertEquals('', fread($resource, 10)); 80 | 81 | fclose($resource); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /test/acceptance/Stream/StreamWrapper/PermissionAcceptanceTest.php: -------------------------------------------------------------------------------- 1 | [ 10 | 'bar' => 'baz' 11 | ] 12 | ]; 13 | 14 | public function testDirIsReadable() 15 | { 16 | $this->assertTrue(is_readable("$this->scheme:///foo")); 17 | } 18 | 19 | public function testDirIsWritable() 20 | { 21 | $this->assertTrue(is_writable("$this->scheme:///foo")); 22 | } 23 | 24 | public function testDirIsExecutable() 25 | { 26 | // Directory can't be executable 27 | $this->assertFalse(is_executable("$this->scheme:///foo")); 28 | } 29 | 30 | public function testFileIsReadable() 31 | { 32 | $this->assertTrue(is_readable("$this->scheme:///foo/bar")); 33 | } 34 | 35 | public function testFileIsWritable() 36 | { 37 | $this->assertTrue(is_writable("$this->scheme:///foo/bar")); 38 | } 39 | 40 | public function testFileIsExecutable() 41 | { 42 | $this->assertTrue(is_executable("$this->scheme:///foo/bar")); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/acceptance/Stream/StreamWrapper/ReadDirAcceptanceTest.php: -------------------------------------------------------------------------------- 1 | [ 10 | 'bar' => 'baz' 11 | ] 12 | ]; 13 | 14 | public function testReadDirectory() 15 | { 16 | $dHandler = opendir("$this->scheme:///foo"); 17 | $expects = ['bar' => true, '.' => true, '..' => true]; 18 | while(($file = readdir($dHandler)) !== false) { 19 | $this->assertArrayHasKey($file, $expects); 20 | } 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /test/acceptance/Stream/StreamWrapper/RequireAcceptanceTest.php: -------------------------------------------------------------------------------- 1 | [ 13 | 'bar.php' => 'assertEquals('baz', include "$this->scheme:///foo/bar.php"); 20 | } 21 | 22 | public function testRequireFile() 23 | { 24 | $this->assertEquals('baz', require "$this->scheme:///foo/bar.php"); 25 | } 26 | 27 | public function testIncludeMissingFile() 28 | { 29 | $this->setExpectedException('PHPUnit_Framework_Error_Warning'); 30 | 31 | include "$this->scheme:///bar.php"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/acceptance/Stream/StreamWrapper/StatAcceptanceTest.php: -------------------------------------------------------------------------------- 1 | [ 10 | 'bar' => 'baz' 11 | ] 12 | ]; 13 | 14 | public function testIsDir() 15 | { 16 | $this->assertTrue(is_dir("$this->scheme:///foo")); 17 | } 18 | 19 | public function testIsFile() 20 | { 21 | $this->assertTrue(is_file("$this->scheme:///foo/bar")); 22 | } 23 | 24 | public function testTouch() 25 | { 26 | $this->assertTrue(touch("$this->scheme://foo/bar")); 27 | } 28 | 29 | public function testTouchNew() 30 | { 31 | $this->assertTrue(touch("$this->scheme://foo/baz")); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/acceptance/Stream/StreamWrapper/SymlinkAcceptanceTest.php: -------------------------------------------------------------------------------- 1 | [ 10 | 'bar' => 'baz' 11 | ] 12 | ]; 13 | 14 | public function testIsLink() 15 | { 16 | $factory = $this->fs->getNodeFactory(); 17 | 18 | $file = $this->fs->get('/foo/bar'); 19 | $this->fs->get('/')->add('symlink', $factory->buildFileLink($file)); 20 | 21 | $this->assertTrue(is_link("$this->scheme:///symlink")); 22 | } 23 | 24 | public function testDirectoryLink() 25 | { 26 | $this->markTestSkipped('`symlink()` isn\'t supported by PHP Stream Wrappers'); 27 | 28 | symlink("$this->scheme:///foo/bar", "$this->scheme:///symlink"); 29 | 30 | $this->assertTrue(is_link("$this->scheme:///symlink")); 31 | } 32 | 33 | public function testFileLink() 34 | { 35 | $this->markTestSkipped('`symlink()` isn\'t supported by PHP Stream Wrappers'); 36 | 37 | symlink("$this->scheme:///foo", "$this->scheme:///symlink"); 38 | 39 | $this->assertTrue(is_link("$this->scheme:///symlink")); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/functional/FileSystemFunctionalTest.php: -------------------------------------------------------------------------------- 1 | assertFalse($this->isMounted($this->scheme)); 12 | 13 | $this->fs->mount(); 14 | 15 | $this->assertTrue($this->isMounted($this->scheme)); 16 | } 17 | 18 | public function testUnmount() 19 | { 20 | $this->assertFalse($this->isMounted($this->scheme)); 21 | 22 | $this->fs->mount(); 23 | 24 | $this->assertTrue($this->isMounted($this->scheme)); 25 | 26 | $this->fs->unmount(); 27 | 28 | $this->assertFalse($this->isMounted($this->scheme)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/src/AcceptanceTestCase.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Test; 11 | 12 | use PHPUnit_Framework_TestCase as TestCase; 13 | use Vfs\FileSystemBuilder; 14 | use Vfs\FileSystemInterface; 15 | 16 | class AcceptanceTestCase extends TestCase 17 | { 18 | protected $fs; 19 | protected $tree = []; 20 | protected $scheme = 'vfs'; 21 | protected $wrapperClass = 'Vfs\Stream\StreamWrapper'; 22 | 23 | public function setUp() 24 | { 25 | $this->fs = $this->buildFileSystem(); 26 | } 27 | 28 | public function tearDown() 29 | { 30 | if (in_array($this->scheme, stream_get_wrappers())) { 31 | $this->fs->unmount(); 32 | } 33 | } 34 | 35 | protected function buildFileSystem() 36 | { 37 | $builder = new FileSystemBuilder($this->scheme); 38 | $builder->setStreamWrapper($this->wrapperClass); 39 | $builder->setTree($this->tree); 40 | 41 | $fs = $builder->build(); 42 | $fs->mount(); 43 | 44 | return $fs; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/src/FunctionalTestCase.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Test; 11 | 12 | use PHPUnit_Framework_TestCase as TestCase; 13 | use RuntimeException; 14 | use Vfs\FileSystemBuilder; 15 | 16 | class FunctionalTestCase extends TestCase 17 | { 18 | protected $scheme = 'foo'; 19 | 20 | public function setUp() 21 | { 22 | $builder = new FileSystemBuilder($this->scheme); 23 | $this->fs = $builder->build(); 24 | } 25 | 26 | public function tearDown() 27 | { 28 | if ($this->isMounted($this->scheme)) { 29 | $this->fs->unmount(); 30 | 31 | if ($this->isMounted($this->scheme)) { 32 | throw new RuntimeException('Problem unmounting file system ' . $this->scheme); 33 | } 34 | } 35 | } 36 | 37 | protected function isMounted($scheme) 38 | { 39 | return in_array($scheme, stream_get_wrappers()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /test/src/UnitTestCase.php: -------------------------------------------------------------------------------- 1 | 6 | * 7 | * For the full copyright and license information, please view the LICENSE 8 | * file that was distributed with this source code. 9 | */ 10 | namespace Vfs\Test; 11 | 12 | use PHPUnit_Framework_TestCase as TestCase; 13 | 14 | class UnitTestCase extends TestCase 15 | { 16 | protected $factory; 17 | protected $fs; 18 | protected $logger; 19 | protected $registry; 20 | protected $scheme; 21 | protected $walker; 22 | protected $wrapperClass; 23 | } 24 | -------------------------------------------------------------------------------- /test/unit/FileSystemBuilderTest.php: -------------------------------------------------------------------------------- 1 | builder = new FileSystemBuilder(); 13 | } 14 | 15 | public function testGetLogger() 16 | { 17 | $this->assertNull($this->builder->getLogger()); 18 | } 19 | 20 | public function testSetLogger() 21 | { 22 | $factory = Mockery::mock('Psr\Log\LoggerInterface'); 23 | $this->builder->setLogger($factory); 24 | 25 | $this->assertSame($factory, $this->builder->getLogger()); 26 | } 27 | 28 | public function testGetNodeFactory() 29 | { 30 | $this->assertNull($this->builder->getNodeFactory()); 31 | } 32 | 33 | public function testSetNodeFactory() 34 | { 35 | $factory = Mockery::mock('Vfs\Node\Factory\NodeFactoryInterface'); 36 | $this->builder->setNodeFactory($factory); 37 | 38 | $this->assertSame($factory, $this->builder->getNodeFactory()); 39 | } 40 | 41 | public function testGetNodeWalker() 42 | { 43 | $this->assertNull($this->builder->getNodeWalker()); 44 | } 45 | 46 | public function testSetNodeWalker() 47 | { 48 | $walker = Mockery::mock('Vfs\Node\Walker\NodeWalkerInterface'); 49 | $this->builder->setNodeWalker($walker); 50 | 51 | $this->assertSame($walker, $this->builder->getNodeWalker()); 52 | } 53 | 54 | public function testGetRegistry() 55 | { 56 | $this->assertNull($this->builder->getRegistry()); 57 | } 58 | 59 | public function testSetRegistry() 60 | { 61 | $registry = Mockery::mock('Vfs\RegistryInterface'); 62 | $this->builder->setRegistry($registry); 63 | 64 | $this->assertSame($registry, $this->builder->getRegistry()); 65 | } 66 | 67 | public function testGetScheme() 68 | { 69 | $this->assertEquals('vfs', $this->builder->getScheme()); 70 | } 71 | 72 | public function testSetScheme() 73 | { 74 | $scheme = 'foo'; 75 | $this->builder->setScheme($scheme); 76 | 77 | $this->assertEquals($scheme, $this->builder->getScheme()); 78 | } 79 | 80 | public function testGetStreamWrapper() 81 | { 82 | $this->assertNull($this->builder->getStreamWrapper()); 83 | } 84 | 85 | public function testSetStreamWrapper() 86 | { 87 | $wrapperClass = 'Vfs\Stream\StreamWrapper'; 88 | $this->builder->setStreamWrapper($wrapperClass); 89 | 90 | $this->assertEquals($wrapperClass, $this->builder->getStreamWrapper()); 91 | } 92 | 93 | public function testBuild() 94 | { 95 | $scheme = 'foo'; 96 | $wrapperClass = 'Vfs\Stream\StreamWrapper'; 97 | $root = Mockery::mock('Vfs\Node\NodeContainerInterface'); 98 | $tree = Mockery::mock('Vfs\Node\NodeContainerInterface', ['getIterator' => new ArrayIterator([])]); 99 | $factory = Mockery::mock('Vfs\Node\Factory\NodeFactoryInterface'); 100 | $walker = Mockery::mock('Vfs\Node\Walker\NodeWalkerInterface'); 101 | $logger = Mockery::mock('Psr\Log\LoggerInterface'); 102 | $registry = Mockery::mock('Vfs\RegistryInterface'); 103 | 104 | $this->builder->setScheme($scheme); 105 | $this->builder->setLogger($logger); 106 | $this->builder->setNodeFactory($factory); 107 | $this->builder->setNodeWalker($walker); 108 | $this->builder->setRegistry($registry); 109 | $this->builder->setStreamWrapper($wrapperClass); 110 | 111 | $factory->shouldReceive('buildDirectory')->once()->withNoArgs()->andReturn($root); 112 | $factory->shouldReceive('buildTree')->once()->with([])->andReturn($tree); 113 | $walker->shouldReceive('findNode')->once()->with($root, '/')->andReturn($root); 114 | 115 | $fs = $this->builder->build(); 116 | 117 | $this->assertInstanceOf('Vfs\FileSystemInterface', $fs); 118 | $this->assertSame($factory, $fs->getNodeFactory()); 119 | $this->assertSame($walker, $fs->getNodeWalker()); 120 | } 121 | 122 | public function testBuildWithDefaults() 123 | { 124 | $fs = $this->builder->build(); 125 | 126 | $this->assertInstanceOf('Vfs\FileSystemInterface', $fs); 127 | $this->assertEquals('vfs', $fs->getScheme()); 128 | $this->assertInstanceOf('Psr\Log\LoggerInterface', $fs->getLogger()); 129 | $this->assertInstanceOf('Vfs\Node\Factory\NodeFactoryInterface', $fs->getNodeFactory()); 130 | $this->assertInstanceOf('Vfs\Node\Walker\NodeWalkerInterface', $fs->getNodeWalker()); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /test/unit/FileSystemRegistryTest.php: -------------------------------------------------------------------------------- 1 | fsA = $a = Mockery::mock('Vfs\FileSystemInterface'); 12 | $this->fsB = $b = Mockery::mock('Vfs\FileSystemInterface'); 13 | $this->fsC = $c = Mockery::mock('Vfs\FileSystemInterface'); 14 | $this->fss = ['foo' => $a, 'bar' => $b, 'baz' => $c]; 15 | 16 | $this->registry = new FileSystemRegistry($this->fss); 17 | } 18 | 19 | public function testInterface() 20 | { 21 | $this->assertInstanceOf('Vfs\RegistryInterface', $this->registry); 22 | } 23 | 24 | public function testAdd() 25 | { 26 | $fs = Mockery::mock('Vfs\FileSystemInterface'); 27 | $this->registry->add('bam', $fs); 28 | 29 | $this->assertSame($fs, $this->registry->get('bam')); 30 | } 31 | 32 | public function testAddThrowsWhenSchemeRegistered() 33 | { 34 | $fs = Mockery::mock('Vfs\FileSystemInterface'); 35 | 36 | $this->setExpectedException('Vfs\Exception\RegisteredSchemeException'); 37 | 38 | $this->registry->add('foo', $fs); 39 | } 40 | 41 | public function testGet() 42 | { 43 | $this->assertSame($this->fsA, $this->registry->get('foo')); 44 | } 45 | 46 | public function testGetThrowsWhenSchemeUnregistered() 47 | { 48 | $this->setExpectedException('Vfs\Exception\UnregisteredSchemeException'); 49 | 50 | $this->registry->get('bam'); 51 | } 52 | 53 | public function testHasIsTrue() 54 | { 55 | $this->assertTrue($this->registry->has('foo')); 56 | } 57 | 58 | public function testHasIsFalse() 59 | { 60 | $this->assertFalse($this->registry->has('bam')); 61 | } 62 | 63 | public function testRemove() 64 | { 65 | $this->registry->remove('foo'); 66 | 67 | $this->assertFalse($this->registry->has('foo')); 68 | } 69 | 70 | public function testRemoveThrowsWhenSchemeUnregistered() 71 | { 72 | $this->setExpectedException('Vfs\Exception\UnregisteredSchemeException'); 73 | 74 | $this->registry->remove('bam'); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/unit/FileSystemTest.php: -------------------------------------------------------------------------------- 1 | scheme = 'foo'; 12 | $this->wrapperClass = 'Vfs\Stream\StreamWrapper'; 13 | $this->logger = Mockery::mock('Psr\Log\LoggerInterface'); 14 | $this->walker = Mockery::mock('Vfs\Node\Walker\NodeWalkerInterface'); 15 | $this->factory = Mockery::mock('Vfs\Node\Factory\NodeFactoryInterface'); 16 | $this->registry = Mockery::mock('Vfs\RegistryInterface'); 17 | $this->root = Mockery::mock('Vfs\Node\NodeContainerInterface'); 18 | 19 | $this->factory->shouldReceive('buildDirectory')->once()->withNoArgs()->andReturn($this->root); 20 | 21 | $this->fs = new FileSystem($this->scheme, $this->wrapperClass, $this->factory, $this->walker, $this->registry, $this->logger); 22 | } 23 | 24 | public function testInterface() 25 | { 26 | $this->assertInstanceOf('Vfs\FileSystemInterface', $this->fs); 27 | } 28 | 29 | public function testGet() 30 | { 31 | $path = '/foo/bar/baz.txt'; 32 | $node = Mockery::mock('Vfs\Node\NodeInterface'); 33 | 34 | $this->walker->shouldReceive('findNode')->once()->with($this->root, $path)->andReturn($node); 35 | 36 | $this->assertSame($node, $this->fs->get($path)); 37 | } 38 | 39 | public function testGetLogger() 40 | { 41 | $this->assertSame($this->logger, $this->fs->getLogger()); 42 | } 43 | 44 | public function testGetNodeFactory() 45 | { 46 | $this->assertSame($this->factory, $this->fs->getNodeFactory()); 47 | } 48 | 49 | public function testGetNodeWalker() 50 | { 51 | $this->assertSame($this->walker, $this->fs->getNodeWalker()); 52 | } 53 | 54 | public function testMountThrowsRegisteredException() 55 | { 56 | $this->registry->shouldReceive('has')->once()->with($this->scheme)->andReturn(true); 57 | 58 | $this->setExpectedException('Vfs\Exception\RegisteredSchemeException'); 59 | 60 | $this->fs->mount(); 61 | } 62 | 63 | public function testUnmountThrowsUnregisteredException() 64 | { 65 | $this->registry->shouldReceive('has')->once()->with($this->scheme)->andReturn(false); 66 | 67 | $this->setExpectedException('Vfs\Exception\UnregisteredSchemeException'); 68 | 69 | $this->fs->unmount(); 70 | } 71 | 72 | public function testGetScheme() 73 | { 74 | $this->assertSame($this->scheme, $this->fs->getScheme()); 75 | } 76 | 77 | public function testGetSchemeWithoutColonSlashSlash() 78 | { 79 | $factory = Mockery::mock('Vfs\Node\Factory\NodeFactoryInterface'); 80 | $factory->shouldReceive('buildDirectory')->once()->withNoArgs()->andReturn($this->root); 81 | 82 | $fs = new FileSystem("foo://", $this->wrapperClass, $factory, $this->walker, $this->registry, $this->logger); 83 | 84 | $this->assertSame($this->scheme, $fs->getScheme()); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /test/unit/Logger/PhpErrorLoggerTest.php: -------------------------------------------------------------------------------- 1 | logger = new PhpErrorLogger(); 13 | } 14 | 15 | public function dataLog() 16 | { 17 | return [ 18 | ['emergency', 'PHPUnit_Framework_Error'], 19 | ['alert', 'PHPUnit_Framework_Error'], 20 | ['critical', 'PHPUnit_Framework_Error'], 21 | ['error', 'PHPUnit_Framework_Error'], 22 | ['warning', 'PHPUnit_Framework_Error_Warning'], 23 | ['notice', 'PHPUnit_Framework_Error_Notice'], 24 | ['info', 'PHPUnit_Framework_Error_Notice'], 25 | ['debug', 'PHPUnit_Framework_Error_Notice'] 26 | ]; 27 | } 28 | 29 | public function testInterface() 30 | { 31 | $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->logger); 32 | } 33 | 34 | /** 35 | * @dataProvider dataLog 36 | */ 37 | public function testLog($level, $expectation) 38 | { 39 | $this->setExpectedException($expectation); 40 | 41 | $this->logger->log($level, 'foo', []); 42 | } 43 | 44 | public function testLogReplacesPlaceholders() 45 | { 46 | try { 47 | $this->logger->log('debug', 'foo {bar} baz', ['bar' => 'BAR']); 48 | } catch (\PHPUnit_Framework_Error_Notice $e) { 49 | return $this->assertRegexp('/foo BAR baz/', $e->getMessage()); 50 | } 51 | 52 | $this->fail('A PHP Notice should have been triggered'); 53 | } 54 | 55 | public function testEmergency() 56 | { 57 | $this->setExpectedException('PHPUnit_Framework_Error'); 58 | 59 | $this->logger->emergency('foo', []); 60 | } 61 | 62 | public function testAlert() 63 | { 64 | $this->setExpectedException('PHPUnit_Framework_Error'); 65 | 66 | $this->logger->alert('foo', []); 67 | } 68 | 69 | public function testCritical() 70 | { 71 | $this->setExpectedException('PHPUnit_Framework_Error'); 72 | 73 | $this->logger->critical('foo', []); 74 | } 75 | 76 | public function testError() 77 | { 78 | $this->setExpectedException('PHPUnit_Framework_Error'); 79 | 80 | $this->logger->error('foo', []); 81 | } 82 | 83 | public function testWarning() 84 | { 85 | $this->setExpectedException('PHPUnit_Framework_Error_Warning'); 86 | 87 | $this->logger->warning('foo', []); 88 | } 89 | 90 | public function testNotice() 91 | { 92 | $this->setExpectedException('PHPUnit_Framework_Error_Notice'); 93 | 94 | $this->logger->notice('foo', []); 95 | } 96 | 97 | public function testInfo() 98 | { 99 | $this->setExpectedException('PHPUnit_Framework_Error_Notice'); 100 | 101 | $this->logger->info('foo', []); 102 | } 103 | 104 | public function testDebug() 105 | { 106 | $this->setExpectedException('PHPUnit_Framework_Error_Notice'); 107 | 108 | $this->logger->debug('foo', []); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /test/unit/Node/DirectoryLinkTest.php: -------------------------------------------------------------------------------- 1 | nodeA = $a = Mockery::mock('Vfs\Node\NodeInterface'); 12 | $this->nodeB = $b = Mockery::mock('Vfs\Node\NodeInterface'); 13 | $this->nodeC = $c = Mockery::mock('Vfs\Node\NodeInterface'); 14 | $this->nodes = ['foo' => $a, 'bar' => $b, 'baz' => $c]; 15 | } 16 | 17 | public function testInstance() 18 | { 19 | $link = new DirectoryLink(new Directory()); 20 | 21 | $this->assertInstanceOf('Vfs\Node\NodeContainerInterface', $link); 22 | $this->assertInstanceOf('Vfs\Node\LinkInterface', $link); 23 | $this->assertInstanceOf('Vfs\Node\NodeInterface', $link); 24 | $this->assertInstanceOf('Vfs\Node\StatInterface', $link); 25 | } 26 | 27 | public function testAdd() 28 | { 29 | $dir = new Directory(); 30 | $link = new DirectoryLink($dir); 31 | $link->add('foo', $this->nodeA); 32 | 33 | $this->assertSame($this->nodeA, $dir->get('foo')); 34 | } 35 | 36 | public function testGet() 37 | { 38 | $link = new DirectoryLink(new Directory($this->nodes)); 39 | 40 | $this->assertSame($this->nodeA, $link->get('foo')); 41 | } 42 | 43 | public function testGetThrowsMissingNode() 44 | { 45 | $link = new DirectoryLink(new Directory()); 46 | $this->setExpectedException('Vfs\Exception\MissingNodeException'); 47 | 48 | $link->get('foo'); 49 | } 50 | 51 | public function testHasIsTrue() 52 | { 53 | $link = new DirectoryLink(new Directory($this->nodes)); 54 | 55 | $this->assertTrue($link->has('foo')); 56 | } 57 | 58 | public function testHasIsFalse() 59 | { 60 | $link = new DirectoryLink(new Directory()); 61 | 62 | $this->assertFalse($link->has('foo')); 63 | } 64 | 65 | public function testSet() 66 | { 67 | $dir = new Directory(); 68 | $link = new DirectoryLink($dir); 69 | $link->set('foo', $this->nodeA); 70 | 71 | $this->assertSame($this->nodeA, $dir->get('foo')); 72 | } 73 | 74 | public function testGetDateAccessed() 75 | { 76 | $dir = new Directory(); 77 | $link = new DirectoryLink($dir); 78 | 79 | $this->assertInstanceOf('DateTime', $link->getDateAccessed()); 80 | $this->assertNotSame($link->getDateAccessed(), $dir->getDateAccessed()); 81 | } 82 | 83 | public function testGetDateCreated() 84 | { 85 | $dir = new Directory(); 86 | $link = new DirectoryLink($dir); 87 | 88 | $this->assertInstanceOf('DateTime', $link->getDateCreated()); 89 | $this->assertNotSame($link->getDateCreated(), $dir->getDateCreated()); 90 | } 91 | 92 | public function testGetDateModified() 93 | { 94 | $dir = new Directory(); 95 | $link = new DirectoryLink($dir); 96 | 97 | $this->assertInstanceOf('DateTime', $link->getDateModified()); 98 | $this->assertNotSame($link->getDateModified(), $dir->getDateModified()); 99 | } 100 | 101 | public function testGetMode() 102 | { 103 | $link = new DirectoryLink(new Directory()); 104 | 105 | $this->assertEquals(StatInterface::TYPE_LINK, $link->getMode() & StatInterface::TYPE_MASK); 106 | } 107 | 108 | public function testGetSize() 109 | { 110 | $link = new DirectoryLink(new Directory($this->nodes)); 111 | 112 | $this->nodeA->shouldReceive('getSize')->once()->withNoArgs()->andReturn(1); 113 | $this->nodeB->shouldReceive('getSize')->once()->withNoArgs()->andReturn(2); 114 | $this->nodeC->shouldReceive('getSize')->once()->withNoArgs()->andReturn(3); 115 | 116 | $this->assertEquals(6, $link->getSize()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /test/unit/Node/DirectoryTest.php: -------------------------------------------------------------------------------- 1 | nodeA = $a = Mockery::mock('Vfs\Node\NodeInterface'); 12 | $this->nodeB = $b = Mockery::mock('Vfs\Node\NodeInterface'); 13 | $this->nodeC = $c = Mockery::mock('Vfs\Node\NodeInterface'); 14 | $this->nodes = ['foo' => $a, 'bar' => $b, 'baz' => $c]; 15 | } 16 | 17 | public function testInstance() 18 | { 19 | $dir = new Directory(); 20 | 21 | $this->assertInstanceOf('Vfs\Node\NodeContainerInterface', $dir); 22 | $this->assertInstanceOf('Vfs\Node\NodeInterface', $dir); 23 | $this->assertInstanceOf('Vfs\Node\StatInterface', $dir); 24 | } 25 | 26 | public function testConstructSetsDotReference() 27 | { 28 | $dir = new Directory(); 29 | 30 | $this->assertSame($dir, $dir->get('.')); 31 | } 32 | 33 | public function testAdd() 34 | { 35 | $dir = new Directory(); 36 | $dir->add('foo', $this->nodeA); 37 | 38 | $this->assertSame($this->nodeA, $dir->get('foo')); 39 | } 40 | 41 | public function testAddContainerSetsDotReference() 42 | { 43 | $dir = new Directory(); 44 | $node = Mockery::mock('Vfs\Node\NodeContainerInterface'); 45 | 46 | $node->shouldReceive('set')->once()->with('..', $dir); 47 | 48 | $dir->add('foo', $node); 49 | } 50 | 51 | public function testGet() 52 | { 53 | $dir = new Directory($this->nodes); 54 | 55 | $this->assertSame($this->nodeA, $dir->get('foo')); 56 | } 57 | 58 | public function testGetThrowsMissingNode() 59 | { 60 | $dir = new Directory(); 61 | $this->setExpectedException('Vfs\Exception\MissingNodeException'); 62 | 63 | $dir->get('foo'); 64 | } 65 | 66 | public function testHasIsTrue() 67 | { 68 | $dir = new Directory($this->nodes); 69 | 70 | $this->assertTrue($dir->has('foo')); 71 | } 72 | 73 | public function testHasIsFalse() 74 | { 75 | $dir = new Directory(); 76 | 77 | $this->assertFalse($dir->has('foo')); 78 | } 79 | 80 | public function testSet() 81 | { 82 | $dir = new Directory(); 83 | $dir->set('foo', $this->nodeA); 84 | 85 | $this->assertSame($this->nodeA, $dir->get('foo')); 86 | } 87 | 88 | public function testSetContainerSetsDotReference() 89 | { 90 | $dir = new Directory(); 91 | $node = Mockery::mock('Vfs\Node\NodeContainerInterface'); 92 | 93 | $node->shouldReceive('set')->once()->with('..', $dir); 94 | 95 | $dir->set('foo', $node); 96 | } 97 | 98 | public function testGetDateAccessed() 99 | { 100 | $dir = new Directory(); 101 | 102 | $this->assertInstanceOf('DateTime', $dir->getDateAccessed()); 103 | } 104 | 105 | public function testGetDateCreated() 106 | { 107 | $dir = new Directory(); 108 | 109 | $this->assertInstanceOf('DateTime', $dir->getDateCreated()); 110 | } 111 | 112 | public function testGetDateModified() 113 | { 114 | $dir = new Directory(); 115 | 116 | $this->assertInstanceOf('DateTime', $dir->getDateModified()); 117 | } 118 | 119 | public function testGetMode() 120 | { 121 | $dir = new Directory(); 122 | 123 | $this->assertEquals(StatInterface::TYPE_DIR, $dir->getMode() & StatInterface::TYPE_MASK); 124 | } 125 | 126 | public function testGetSize() 127 | { 128 | $dir = new Directory($this->nodes); 129 | 130 | $this->nodeA->shouldReceive('getSize')->once()->withNoArgs()->andReturn(1); 131 | $this->nodeB->shouldReceive('getSize')->once()->withNoArgs()->andReturn(2); 132 | $this->nodeC->shouldReceive('getSize')->once()->withNoArgs()->andReturn(3); 133 | 134 | $this->assertEquals(6, $dir->getSize()); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /test/unit/Node/Factory/NodeFactoryTest.php: -------------------------------------------------------------------------------- 1 | factory = new NodeFactory(); 14 | } 15 | 16 | public function testInterface() 17 | { 18 | $this->assertInstanceOf('Vfs\Node\Factory\NodeFactoryInterface', $this->factory); 19 | } 20 | 21 | public function testBuildFile() 22 | { 23 | $file = $this->factory->buildFile('foo'); 24 | 25 | $this->assertInstanceOf('Vfs\Node\FileInterface', $file); 26 | $this->assertEquals('foo', $file->getContent()); 27 | } 28 | 29 | public function testBuildFileLink() 30 | { 31 | $file = new File(); 32 | $link = $this->factory->buildFileLink($file); 33 | 34 | $this->assertInstanceOf('Vfs\Node\FileInterface', $link); 35 | $this->assertInstanceOf('Vfs\Node\LinkInterface', $link); 36 | } 37 | 38 | public function testBuildDirectory() 39 | { 40 | $node = Mockery::mock('Vfs\Node\NodeInterface'); 41 | $dir = $this->factory->buildDirectory(['foo' => $node]); 42 | 43 | $this->assertInstanceof('Vfs\Node\NodeContainerInterface', $dir); 44 | $this->assertSame($node, $dir->get('foo')); 45 | } 46 | 47 | public function testBuildDirectoryLink() 48 | { 49 | $dir = new Directory(); 50 | $link = $this->factory->buildDirectoryLink($dir); 51 | 52 | $this->assertInstanceOf('Vfs\Node\NodeContainerInterface', $link); 53 | $this->assertInstanceOf('Vfs\Node\LinkInterface', $link); 54 | } 55 | 56 | public function testBuildTree() 57 | { 58 | $root = $this->factory->buildTree([ 59 | 'foo' => [ 60 | 'bar' => [ 61 | 'baz' => 'foobarbaz' 62 | ] 63 | ], 64 | 'bar' => [ 65 | 'baz' => 'barbaz' 66 | ] 67 | ]); 68 | 69 | $this->assertInstanceof('Vfs\Node\NodeContainerInterface', $root); 70 | $this->assertInstanceof('Vfs\Node\NodeContainerInterface', $root->get('foo')); 71 | $this->assertInstanceof('Vfs\Node\NodeContainerInterface', $root->get('bar')); 72 | $this->assertInstanceof('Vfs\Node\NodeContainerInterface', $root->get('foo')->get('bar')); 73 | $this->assertInstanceof('Vfs\Node\FileInterface', $root->get('foo')->get('bar')->get('baz')); 74 | $this->assertInstanceof('Vfs\Node\FileInterface', $root->get('bar')->get('baz')); 75 | $this->assertEquals('foobarbaz', $root->get('foo')->get('bar')->get('baz')->getContent()); 76 | $this->assertEquals('barbaz', $root->get('bar')->get('baz')->getContent()); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/unit/Node/FileLinkTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Vfs\Node\FileInterface', $link); 13 | $this->assertInstanceOf('Vfs\Node\LinkInterface', $link); 14 | $this->assertInstanceOf('Vfs\Node\NodeInterface', $link); 15 | $this->assertInstanceOf('Vfs\Node\StatInterface', $link); 16 | } 17 | 18 | public function testGetContent() 19 | { 20 | $link = new FileLink(new File('foo')); 21 | 22 | $this->assertEquals('foo', $link->getContent()); 23 | } 24 | 25 | public function testSetContent() 26 | { 27 | $file = new File(''); 28 | $link = new FileLink($file); 29 | $link->setContent('foo'); 30 | 31 | $this->assertEquals('foo', $file->getContent()); 32 | } 33 | 34 | public function testGetDateAccessed() 35 | { 36 | $file = new File(); 37 | $link = new FileLink($file); 38 | 39 | $this->assertInstanceOf('DateTime', $link->getDateAccessed()); 40 | $this->assertNotSame($link->getDateAccessed(), $file->getDateAccessed()); 41 | } 42 | 43 | public function testGetDateCreated() 44 | { 45 | $file = new File(); 46 | $link = new FileLink($file); 47 | 48 | $this->assertInstanceOf('DateTime', $link->getDateCreated()); 49 | $this->assertNotSame($link->getDateCreated(), $file->getDateCreated()); 50 | } 51 | 52 | public function testGetDateModified() 53 | { 54 | $file = new File(); 55 | $link = new FileLink($file); 56 | 57 | $this->assertInstanceOf('DateTime', $link->getDateModified()); 58 | $this->assertNotSame($link->getDateModified(), $file->getDateModified()); 59 | } 60 | 61 | public function testGetMode() 62 | { 63 | $link = new FileLink(new File()); 64 | 65 | $this->assertEquals(StatInterface::TYPE_LINK, $link->getMode() & StatInterface::TYPE_MASK); 66 | } 67 | 68 | public function testGetSize() 69 | { 70 | $link = new FileLink(new File('foo')); 71 | 72 | $this->assertEquals(3, $link->getSize()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /test/unit/Node/FileTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Vfs\Node\FileInterface', $file); 13 | $this->assertInstanceOf('Vfs\Node\NodeInterface', $file); 14 | $this->assertInstanceOf('Vfs\Node\StatInterface', $file); 15 | } 16 | 17 | public function testGetContent() 18 | { 19 | $file = new File('foo'); 20 | 21 | $this->assertEquals('foo', $file->getContent()); 22 | } 23 | 24 | public function testSetContent() 25 | { 26 | $file = new File(''); 27 | $file->setContent('foo'); 28 | 29 | $this->assertEquals('foo', $file->getContent()); 30 | } 31 | 32 | public function testGetDateAccessed() 33 | { 34 | $file = new File(); 35 | 36 | $this->assertInstanceOf('DateTime', $file->getDateAccessed()); 37 | } 38 | 39 | public function testGetDateCreated() 40 | { 41 | $file = new File(); 42 | 43 | $this->assertInstanceOf('DateTime', $file->getDateCreated()); 44 | } 45 | 46 | public function testGetDateModified() 47 | { 48 | $file = new File(); 49 | 50 | $this->assertInstanceOf('DateTime', $file->getDateModified()); 51 | } 52 | 53 | public function testGetMode() 54 | { 55 | $file = new File(); 56 | 57 | $this->assertEquals(StatInterface::TYPE_FILE, $file->getMode() & StatInterface::TYPE_MASK); 58 | } 59 | 60 | public function testGetSize() 61 | { 62 | $file = new File('foo'); 63 | 64 | $this->assertEquals(3, $file->getSize()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/unit/Node/Walker/NodeWalkerTest.php: -------------------------------------------------------------------------------- 1 | walker = new NodeWalker(); 12 | } 13 | 14 | public function dataFindNodeReturnsRoot() 15 | { 16 | return [[''], ['/'], ['.'], ['./']]; 17 | } 18 | 19 | public function testInterface() 20 | { 21 | $this->assertInstanceOf('Vfs\Node\Walker\NodeWalkerInterface', $this->walker); 22 | } 23 | 24 | public function testFindNode() 25 | { 26 | $bar = Mockery::mock('Vfs\Node\NodeInterface'); 27 | 28 | $foo = Mockery::mock('Vfs\Node\NodeContainerInterface'); 29 | $foo->shouldReceive('has')->once()->with('bar')->andReturn(true); 30 | $foo->shouldReceive('get')->once()->with('bar')->andReturn($bar); 31 | 32 | $root = Mockery::mock('Vfs\Node\NodeContainerInterface'); 33 | $root->shouldReceive('has')->once()->with('foo')->andReturn(true); 34 | $root->shouldReceive('get')->once()->with('foo')->andReturn($foo); 35 | 36 | $this->assertSame($bar, $this->walker->findNode($root, '/foo/bar')); 37 | } 38 | 39 | /** 40 | * @dataProvider dataFindNodeReturnsRoot 41 | */ 42 | public function testFindNodeReturnsRoot($path) 43 | { 44 | $root = Mockery::mock('Vfs\Node\NodeContainerInterface'); 45 | 46 | if ($path && '.' == $path[0]) { 47 | $root->shouldReceive('has')->once()->with('.')->andReturn(true); 48 | $root->shouldReceive('get')->once()->with('.')->andReturn($root); 49 | } 50 | 51 | $this->assertSame($root, $this->walker->findNode($root, $path)); 52 | } 53 | 54 | public function testFindNodeWithDotPaths() 55 | { 56 | $baz = Mockery::mock('Vfs\Node\NodeInterface'); 57 | 58 | $bar = Mockery::mock('Vfs\Node\NodeContainerInterface'); 59 | $bar->shouldReceive('has')->once()->with('baz')->andReturn(true); 60 | $bar->shouldReceive('get')->once()->with('baz')->andReturn($baz); 61 | 62 | $foo = Mockery::mock('Vfs\Node\NodeContainerInterface'); 63 | $foo->shouldReceive('has')->times(2)->with('bar')->andReturn(true); 64 | $foo->shouldReceive('get')->times(2)->with('bar')->andReturn($bar); 65 | 66 | $root = Mockery::mock('Vfs\Node\NodeContainerInterface'); 67 | $root->shouldReceive('has')->times(3)->with('foo')->andReturn(true); 68 | $root->shouldReceive('get')->times(3)->with('foo')->andReturn($foo); 69 | 70 | $bar->shouldReceive('has')->once()->with('.')->andReturn(true); 71 | $bar->shouldReceive('get')->once()->with('.')->andReturn($bar); 72 | $bar->shouldReceive('has')->once()->with('..')->andReturn(true); 73 | $bar->shouldReceive('get')->once()->with('..')->andReturn($foo); 74 | $foo->shouldReceive('has')->once()->with('.')->andReturn(true); 75 | $foo->shouldReceive('get')->once()->with('.')->andReturn($foo); 76 | $foo->shouldReceive('has')->times(2)->with('..')->andReturn(true); 77 | $foo->shouldReceive('get')->times(2)->with('..')->andReturn($root); 78 | 79 | $this->assertSame($baz, $this->walker->findNode($root, '/foo/../foo/./bar//../../foo/bar/./baz')); 80 | } 81 | 82 | public function testFindInvalidPath() 83 | { 84 | $foo = Mockery::mock('Vfs\Node\NodeContainerInterface'); 85 | $foo->shouldReceive('has')->once()->with('bar')->andReturn(false); 86 | 87 | $root = Mockery::mock('Vfs\Node\NodeContainerInterface'); 88 | $root->shouldReceive('has')->once()->with('foo')->andReturn(true); 89 | $root->shouldReceive('get')->once()->with('foo')->andReturn($foo); 90 | 91 | $this->assertNull($this->walker->findNode($root, '/foo/bar')); 92 | } 93 | 94 | public function testWalkPath() 95 | { 96 | $bar = Mockery::mock('Vfs\Node\NodeInterface'); 97 | $foo = Mockery::mock('Vfs\Node\NodeContainerInterface'); 98 | $root = Mockery::mock('Vfs\Node\NodeContainerInterface'); 99 | 100 | $fn = function ($node, $name) use ($root, $foo, $bar) { 101 | if ('foo' === $name && $node === $root) { 102 | return $foo; 103 | } elseif ('bar' === $name && $node === $foo) { 104 | return $bar; 105 | } 106 | 107 | $this->fail('None of the walk conditions were met'); 108 | }; 109 | 110 | $this->assertSame($bar, $this->walker->walkPath($root, '/foo/bar', $fn)); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /test/unit/Stream/DirectoryHandleTest.php: -------------------------------------------------------------------------------- 1 | fs = Mockery::mock('Vfs\FileSystem'); 12 | } 13 | 14 | public function testInterface() 15 | { 16 | $handle = new DirectoryHandle($this->fs, ''); 17 | 18 | $this->assertInstanceOf('Vfs\Stream\HandleInterface', $handle); 19 | } 20 | 21 | public function testRename() 22 | { 23 | $handle = new DirectoryHandle($this->fs, 'foo://foo/bar'); 24 | $foo = Mockery::mock('Vfs\Node\NodeContainerInterface'); 25 | $bar = Mockery::mock('Vfs\Node\NodeContainerInterface'); 26 | 27 | $this->fs->shouldReceive('get')->once()->with('/foo/bar')->andReturn($bar); 28 | $this->fs->shouldReceive('get')->times(2)->with('/foo')->andReturn($foo); 29 | 30 | $foo->shouldReceive('remove')->once()->with('bar'); 31 | $foo->shouldReceive('add')->once()->with('baz', $bar); 32 | 33 | $handle->rename('foo://foo/baz'); 34 | } 35 | 36 | public function testRenameMissingSource() 37 | { 38 | $handle = new DirectoryHandle($this->fs, 'foo://foo'); 39 | $foo = Mockery::mock('Vfs\Node\NodeContainerInterface'); 40 | 41 | $logger = Mockery::mock('Psr\Log\LoggerInterface'); 42 | $logger->shouldReceive('warning')->once()->with(Mockery::type('string'), [ 43 | 'origin' => 'foo://foo', 44 | 'target' => 'foo://bar', 45 | ]); 46 | 47 | $this->fs->shouldReceive('get')->once()->with('/foo'); 48 | $this->fs->shouldReceive('get')->times(2)->with(DIRECTORY_SEPARATOR); 49 | $this->fs->shouldReceive('getLogger')->once()->withNoArgs()->andReturn($logger); 50 | 51 | $handle->rename('foo://bar'); 52 | } 53 | 54 | public function testCreateRecursively() 55 | { 56 | $handle = new DirectoryHandle($this->fs, 'foo://foo/bar'); 57 | 58 | $rootContainer = Mockery::mock('Vfs\Node\NodeContainerInterface'); 59 | $builtDir = Mockery::Mock('Vfs\Node\Directory'); 60 | $builtContainer = Mockery::mock('Vfs\Node\NodeContainerInterface'); 61 | $nodeFactory = Mockery::mock('Vfs\Node\NodeFactoryInterface'); 62 | $nodeWalker = Mockery::mock('Vfs\Node\NodeWalkerInterface'); 63 | 64 | $builtContainer->shouldReceive('add')->with(basename('/foo/bar'), $builtDir); 65 | $nodeFactory->shouldReceive('buildDirectory')->andReturn($builtDir); 66 | $nodeWalker->shouldReceive('walkPath')->once()->andReturn($builtContainer); 67 | 68 | $this->fs->shouldReceive('get')->once()->with('/foo/bar'); 69 | $this->fs->shouldReceive('get')->once()->with('/foo'); 70 | 71 | 72 | $this->fs->shouldReceive('get')->once()->with('/')->andReturn($rootContainer); 73 | $this->fs->shouldReceive('getNodeFactory')->times(2)->andReturn($nodeFactory); 74 | $this->fs->shouldReceive('getNodeWalker')->once()->andReturn($nodeWalker); 75 | 76 | 77 | $handle->create(0777, true); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/unit/Stream/FileHandleTest.php: -------------------------------------------------------------------------------- 1 | fs = Mockery::mock('Vfs\FileSystem'); 12 | } 13 | 14 | public function dataCanRead() 15 | { 16 | return [ 17 | ['', false], [null, false], 18 | ['a', false], ['ab', false], ['a+', true], ['at', false], 19 | ['r', true ], ['rb', true ], ['r+', true], ['rt', true ], 20 | ['w', false], ['wb', false], ['w+', true], ['wt', false], 21 | ['c', false], ['cb', false], ['c+', true], ['ct', false], 22 | ['x', false], ['xb', false], ['x+', true], ['xt', false] 23 | ]; 24 | } 25 | 26 | public function dataOpenMissingFileIsCreated() 27 | { 28 | return [ 29 | ['a'], ['ab'], ['a+'], ['at'], 30 | ['w'], ['wb'], ['w+'], ['wt'], 31 | ['c'], ['cb'], ['c+'], ['ct'], 32 | ['x'], ['xb'], ['x+'], ['xt'] 33 | ]; 34 | } 35 | 36 | public function dataRead() 37 | { 38 | return [ 39 | [0, null, 'bar'], [1, null, 'ar'], [2, null, 'r'], [3, null, ''], 40 | [0, 0, ''], [1, 0, ''], [2, 0, ''], [3, 0, ''], 41 | [0, 1, 'b'], [1, 1, 'a'], [2, 1, 'r'], [3, 1, ''], 42 | [0, 2, 'ba'], [1, 2, 'ar'], [2, 2, 'r'], [3, 2, ''], 43 | [0, 3, 'bar'], [1, 3, 'ar'], [2, 3, 'r'], [3, 3, ''], 44 | ]; 45 | } 46 | 47 | public function dataTouch() 48 | { 49 | return [ 50 | ['a'], ['ab'], ['a+'], ['at'], 51 | ['w'], ['wb'], ['w+'], ['wt'], 52 | ['c'], ['cb'], ['c+'], ['ct'] 53 | ]; 54 | } 55 | 56 | public function testInterface() 57 | { 58 | $handle = new FileHandle($this->fs, ''); 59 | 60 | $this->assertInstanceOf('Vfs\Stream\HandleInterface', $handle); 61 | } 62 | 63 | public function testRename() 64 | { 65 | $handle = new FileHandle($this->fs, 'foo://foo/bar'); 66 | $foo = Mockery::mock('Vfs\Node\NodeContainerInterface'); 67 | $bar = Mockery::mock('Vfs\Node\NodeInterface'); 68 | 69 | $this->fs->shouldReceive('get')->once()->with('/foo/bar')->andReturn($bar); 70 | $this->fs->shouldReceive('get')->times(2)->with('/foo')->andReturn($foo); 71 | 72 | $foo->shouldReceive('remove')->once()->with('bar'); 73 | $foo->shouldReceive('add')->once()->with('baz', $bar); 74 | 75 | $handle->rename('foo://foo/baz'); 76 | } 77 | 78 | public function testRenameMissingSource() 79 | { 80 | $handle = new FileHandle($this->fs, 'foo://foo'); 81 | $foo = Mockery::mock('Vfs\Node\NodeInterface'); 82 | 83 | $logger = Mockery::mock('Psr\Log\LoggerInterface'); 84 | $logger->shouldReceive('warning')->once()->with(Mockery::type('string'), [ 85 | 'origin' => 'foo://foo', 86 | 'target' => 'foo://bar', 87 | ]); 88 | 89 | $this->fs->shouldReceive('get')->once()->with('/foo'); 90 | $this->fs->shouldReceive('get')->times(2)->with(DIRECTORY_SEPARATOR); 91 | $this->fs->shouldReceive('getLogger')->once()->withNoArgs()->andReturn($logger); 92 | 93 | $handle->rename('foo://bar'); 94 | } 95 | 96 | /** 97 | * @dataProvider dataCanRead 98 | */ 99 | public function testCanRead($mode, $expected) 100 | { 101 | $handle = new FileHandle($this->fs, '', $mode); 102 | 103 | $this->assertEquals($expected, $handle->canRead()); 104 | } 105 | 106 | public function testCreate() 107 | { 108 | $handle = new FileHandle($this->fs, 'foo://foo/bar/baz', 'w+'); 109 | 110 | $file = Mockery::mock('Vfs\Node\NodeInterface'); 111 | $dir = Mockery::mock('Vfs\Node\NodeContainerInterface'); 112 | $dir->shouldReceive('set')->once()->with('baz', $file); 113 | $factory = Mockery::mock('Vfs\Node\Factory\NodeFactoryInterface'); 114 | $factory->shouldReceive('buildFile')->once()->withNoArgs()->andReturn($file); 115 | 116 | $this->fs->shouldReceive('get')->once()->with('/foo/bar/baz'); 117 | $this->fs->shouldReceive('get')->once()->with('/foo/bar')->andReturn($dir); 118 | $this->fs->shouldReceive('getNodeFactory')->once()->withNoArgs()->andReturn($factory); 119 | 120 | $this->assertSame($file, $handle->create(0777)); 121 | } 122 | 123 | public function testCreateWithoutWriteMode() 124 | { 125 | $handle = new FileHandle($this->fs, 'foo://foo/bar/baz'); 126 | 127 | $this->fs->shouldReceive('get')->once()->with('/foo/bar/baz'); 128 | 129 | $this->assertNull($handle->create(0777)); 130 | } 131 | 132 | public function testCreateWithMissingDir() 133 | { 134 | $handle = new FileHandle($this->fs, 'foo://foo/bar/baz', 'w+'); 135 | 136 | $this->fs->shouldReceive('get')->once()->with('/foo/bar/baz'); 137 | $this->fs->shouldReceive('get')->once()->with('/foo/bar'); 138 | 139 | $this->assertNull($handle->create(0777)); 140 | } 141 | 142 | public function testDestroy() 143 | { 144 | $handle = new FileHandle($this->fs, 'foo://foo/bar/baz'); 145 | 146 | $file = Mockery::mock('Vfs\Node\NodeInterface'); 147 | $dir = Mockery::mock('Vfs\Node\NodeContainerInterface'); 148 | $dir->shouldReceive('remove')->once()->with('baz'); 149 | 150 | $this->fs->shouldReceive('get')->once()->with('/foo/bar/baz')->andReturn($file); 151 | $this->fs->shouldReceive('get')->once()->with('/foo/bar')->andReturn($dir); 152 | 153 | $this->assertTrue($handle->destroy()); 154 | } 155 | 156 | public function testDestroyMissingFile() 157 | { 158 | $handle = new FileHandle($this->fs, 'foo://foo/bar/baz'); 159 | 160 | $file = Mockery::mock('Vfs\Node\NodeInterface'); 161 | $logger = Mockery::mock('Pser\Log\LoggerInterface'); 162 | $logger->shouldReceive('warning')->once()->with(Mockery::type('string'), [ 163 | 'url' => 'foo://foo/bar/baz' 164 | ]); 165 | 166 | $this->fs->shouldReceive('get')->once()->with('/foo/bar/baz'); 167 | $this->fs->shouldReceive('getLogger')->once()->withNoArgs()->andReturn($logger); 168 | 169 | $this->assertFalse($handle->destroy()); 170 | } 171 | 172 | public function testOpen() 173 | { 174 | $handle = new FileHandle($this->fs, 'foo://foo/bar/baz'); 175 | $file = Mockery::mock('Vfs\Node\NodeInterface'); 176 | 177 | $this->fs->shouldReceive('get')->once()->with('/foo/bar/baz')->andReturn($file); 178 | 179 | $this->assertSame($file, $handle->open()); 180 | } 181 | 182 | public function testOpenMissingFileIsNotCreated() 183 | { 184 | $handle = new FileHandle($this->fs, 'foo://foo/bar/baz'); 185 | 186 | $this->fs->shouldReceive('get')->once()->with('/foo/bar/baz'); 187 | 188 | $this->assertNull($handle->open()); 189 | } 190 | 191 | /** 192 | * @dataProvider dataOpenMissingFileIsCreated 193 | */ 194 | public function testOpenMissingFileIsCreated($mode) 195 | { 196 | $handle = new FileHandle($this->fs, 'foo://foo/bar/baz', $mode); 197 | $factory = Mockery::mock('Vfs\Node\Factory\NodeFactoryInterface'); 198 | $dir = Mockery::mock('Vfs\Node\NodeContainerInterface'); 199 | $file = Mockery::mock('Vfs\Node\FileInterface'); 200 | 201 | $this->fs->shouldReceive('get')->once()->with('/foo/bar/baz'); 202 | $this->fs->shouldReceive('get')->once()->with('/foo/bar')->andReturn($dir); 203 | $this->fs->shouldReceive('getNodeFactory')->once()->withNoArgs()->andReturn($factory); 204 | 205 | $dir->shouldReceive('set')->once()->with('baz', $file); 206 | $factory->shouldReceive('buildFile')->once()->withNoArgs()->andReturn($file); 207 | 208 | if (in_array($mode, ['w', 'wb', 'w+', 'wt'])) { 209 | $file->shouldReceive('setContent')->once()->with(''); 210 | } 211 | 212 | $this->assertSame($file, $handle->open()); 213 | } 214 | 215 | /** 216 | * @dataProvider dataRead 217 | */ 218 | public function testRead($offset, $length, $expected) 219 | { 220 | $handle = new FileHandle($this->fs, 'foo://bar'); 221 | 222 | $file = Mockery::mock('Vfs\Node\FileInterface'); 223 | $file->shouldReceive('getContent')->once()->withNoArgs()->andReturn('bar'); 224 | 225 | $this->fs->shouldReceive('get')->once()->with('/bar')->andReturn($file); 226 | 227 | $handle->open(); 228 | $this->assertEquals($expected, $handle->read($offset, $length)); 229 | } 230 | 231 | public function testReadNonFile() 232 | { 233 | $handle = new FileHandle($this->fs, 'foo://bar'); 234 | 235 | $file = Mockery::mock('Vfs\Node\NodeInterface'); 236 | 237 | $this->fs->shouldReceive('get')->once()->with('/bar')->andReturn($file); 238 | 239 | $handle->open(); 240 | $this->assertEquals('', $handle->read(0, PHP_INT_MAX)); 241 | } 242 | 243 | public function testReadThrowsWithoutOpening() 244 | { 245 | $handle = new FileHandle($this->fs, 'foo://bar'); 246 | 247 | $this->setExpectedException('Vfs\Exception\UnopenedHandleException'); 248 | 249 | $handle->read(); 250 | } 251 | 252 | public function testWrite() 253 | { 254 | $handle = new FileHandle($this->fs, 'foo://bar'); 255 | 256 | $file = Mockery::mock('Vfs\Node\FileInterface'); 257 | $file->shouldReceive('setContent')->once()->with('foo'); 258 | 259 | $this->fs->shouldReceive('get')->once()->with('/bar')->andReturn($file); 260 | 261 | $handle->open(); 262 | $handle->write('foo'); 263 | } 264 | 265 | public function testWriteThrowsWithoutOpening() 266 | { 267 | $handle = new FileHandle($this->fs, 'foo://bar'); 268 | 269 | $this->setExpectedException('Vfs\Exception\UnopenedHandleException'); 270 | 271 | $handle->write(''); 272 | } 273 | 274 | /** 275 | * @dataProvider dataTouch 276 | */ 277 | public function testTouch($mode) 278 | { 279 | $handle = new FileHandle($this->fs, 'foo://bar', $mode); 280 | $atime = Mockery::mock('DateTime'); 281 | $mtime = Mockery::mock('DateTime'); 282 | 283 | $file = Mockery::mock('Vfs\Node\FileInterface'); 284 | $file->shouldReceive('setDateAccessed')->once()->with($atime); 285 | $file->shouldReceive('setDateModified')->once()->with($mtime); 286 | 287 | $factory = Mockery::mock('Vfs\Node\Factory\NodeFactoryInterface'); 288 | $factory->shouldReceive('buildFile')->once()->withNoArgs()->andReturn($file); 289 | 290 | $root = Mockery::mock('Vfs\Node\NodeContainerInterface'); 291 | $root->shouldReceive('set')->once()->with('bar', $file); 292 | 293 | $this->fs->shouldReceive('get')->once()->with(DIRECTORY_SEPARATOR)->andReturn($root); 294 | $this->fs->shouldReceive('get')->once()->with('/bar'); 295 | $this->fs->shouldReceive('getNodeFactory')->once()->withNoArgs()->andReturn($factory); 296 | 297 | $node = $handle->touch($mtime, $atime); 298 | 299 | $this->assertSame($file, $node); 300 | } 301 | 302 | /** 303 | * @dataProvider dataTouch 304 | */ 305 | public function testTouchExistingFile($mode) 306 | { 307 | $handle = new FileHandle($this->fs, 'foo://bar', $mode); 308 | $atime = Mockery::mock('DateTime'); 309 | $mtime = Mockery::mock('DateTime'); 310 | 311 | $file = Mockery::mock('Vfs\Node\FileInterface'); 312 | $file->shouldReceive('setDateAccessed')->once()->with($atime); 313 | $file->shouldReceive('setDateModified')->once()->with($mtime); 314 | 315 | $this->fs->shouldReceive('get')->once()->with('/bar')->andReturn($file); 316 | 317 | $node = $handle->touch($mtime, $atime); 318 | 319 | $this->assertSame($file, $node); 320 | } 321 | } 322 | --------------------------------------------------------------------------------