Test project for media files management.
<?php
/**
* Hoa
*
*
* @license
*
* New BSD License
*
* Copyright © 2007-2017, Hoa community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Hoa nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace Psy\Readline\Hoa;
/**
* Stream wrapper for the `hoa://` protocol.
*/
class ProtocolWrapper
{
/**
* Opened stream as a resource.
*/
private $_stream = null;
/**
* Stream name (filename).
*/
private $_streamName = null;
/**
* Stream context (given by the streamWrapper class) as a resource.
*/
public $context = null;
/**
* Get the real path of the given URL.
* Could return false if the path cannot be reached.
*/
public static function realPath(string $path, bool $exists = true)
{
return ProtocolNode::getRoot()->resolve($path, $exists);
}
/**
* Retrieve the underlying resource.
*
* `$castAs` can be `STREAM_CAST_FOR_SELECT` when `stream_select` is
* calling `stream_cast` or `STREAM_CAST_AS_STREAM` when `stream_cast` is
* called for other uses.
*/
public function stream_cast(int $castAs)
{
return null;
}
/**
* Closes a resource.
* This method is called in response to `fclose`.
* All resources that were locked, or allocated, by the wrapper should be
* released.
*/
public function stream_close()
{
if (true === @\fclose($this->getStream())) {
$this->_stream = null;
$this->_streamName = null;
}
}
/**
* Tests for end-of-file on a file pointer.
* This method is called in response to feof().
*/
public function stream_eof(): bool
{
return \feof($this->getStream());
}
/**
* Flush the output.
* This method is called in respond to fflush().
* If we have cached data in our stream but not yet stored it into the
* underlying storage, we should do so now.
*/
public function stream_flush(): bool
{
return \fflush($this->getStream());
}
/**
* Advisory file locking.
* This method is called in response to flock(), when file_put_contents()
* (when flags contains LOCK_EX), stream_set_blocking() and when closing the
* stream (LOCK_UN).
*
* Operation is one the following:
* * LOCK_SH to acquire a shared lock (reader) ;
* * LOCK_EX to acquire an exclusive lock (writer) ;
* * LOCK_UN to release a lock (shared or exclusive) ;
* * LOCK_NB if we don't want flock() to
* block while locking (not supported on
* Windows).
*/
public function stream_lock(int $operation): bool
{
return \flock($this->getStream(), $operation);
}
/**
* Change stream options.
* This method is called to set metadata on the stream. It is called when
* one of the following functions is called on a stream URL: touch, chmod,
* chown or chgrp.
*
* Option must be one of the following constant:
* * STREAM_META_TOUCH,
* * STREAM_META_OWNER_NAME,
* * STREAM_META_OWNER,
* * STREAM_META_GROUP_NAME,
* * STREAM_META_GROUP,
* * STREAM_META_ACCESS.
*
* Values are arguments of `touch`, `chmod`, `chown`, and `chgrp`.
*/
public function stream_metadata(string $path, int $option, $values): bool
{
$path = static::realPath($path, false);
switch ($option) {
case \STREAM_META_TOUCH:
$arity = \count($values);
if (0 === $arity) {
$out = \touch($path);
} elseif (1 === $arity) {
$out = \touch($path, $values[0]);
} else {
$out = \touch($path, $values[0], $values[1]);
}
break;
case \STREAM_META_OWNER_NAME:
case \STREAM_META_OWNER:
$out = \chown($path, $values);
break;
case \STREAM_META_GROUP_NAME:
case \STREAM_META_GROUP:
$out = \chgrp($path, $values);
break;
case \STREAM_META_ACCESS:
$out = \chmod($path, $values);
break;
default:
$out = false;
}
return $out;
}
/**
* Open file or URL.
* This method is called immediately after the wrapper is initialized (f.e.
* by fopen() and file_get_contents()).
*/
public function stream_open(string $path, string $mode, int $options, &$openedPath): bool
{
$path = static::realPath($path, 'r' === $mode[0]);
if (Protocol::NO_RESOLUTION === $path) {
return false;
}
if (null === $this->context) {
$openedPath = \fopen($path, $mode, $options & \STREAM_USE_PATH);
} else {
$openedPath = \fopen(
$path,
$mode,
(bool) ($options & \STREAM_USE_PATH),
$this->context
);
}
if (false === \is_resource($openedPath)) {
return false;
}
$this->_stream = $openedPath;
$this->_streamName = $path;
return true;
}
/**
* Read from stream.
* This method is called in response to fread() and fgets().
*/
public function stream_read(int $size): string
{
return \fread($this->getStream(), $size);
}
/**
* Seek to specific location in a stream.
* This method is called in response to fseek().
* The read/write position of the stream should be updated according to the
* $offset and $whence.
*
* The possible values for `$whence` are:
* * SEEK_SET to set position equal to $offset bytes,
* * SEEK_CUR to set position to current location plus `$offset`,
* * SEEK_END to set position to end-of-file plus `$offset`.
*/
public function stream_seek(int $offset, int $whence = \SEEK_SET): bool
{
return 0 === \fseek($this->getStream(), $offset, $whence);
}
/**
* Retrieve information about a file resource.
* This method is called in response to fstat().
*/
public function stream_stat(): array
{
return \fstat($this->getStream());
}
/**
* Retrieve the current position of a stream.
* This method is called in response to ftell().
*/
public function stream_tell(): int
{
return \ftell($this->getStream());
}
/**
* Truncate a stream to a given length.
*/
public function stream_truncate(int $size): bool
{
return \ftruncate($this->getStream(), $size);
}
/**
* Write to stream.
* This method is called in response to fwrite().
*/
public function stream_write(string $data): int
{
return \fwrite($this->getStream(), $data);
}
/**
* Close directory handle.
* This method is called in to closedir().
* Any resources which were locked, or allocated, during opening and use of
* the directory stream should be released.
*/
public function dir_closedir()
{
\closedir($this->getStream());
$this->_stream = null;
$this->_streamName = null;
}
/**
* Open directory handle.
* This method is called in response to opendir().
*
* The `$options` input represents whether or not to enforce safe_mode
* (0x04). It is not used here.
*/
public function dir_opendir(string $path, int $options): bool
{
$path = static::realPath($path);
$handle = null;
if (null === $this->context) {
$handle = @\opendir($path);
} else {
$handle = @\opendir($path, $this->context);
}
if (false === $handle) {
return false;
}
$this->_stream = $handle;
$this->_streamName = $path;
return true;
}
/**
* Read entry from directory handle.
* This method is called in response to readdir().
*
* @return mixed
*/
public function dir_readdir()
{
return \readdir($this->getStream());
}
/**
* Rewind directory handle.
* This method is called in response to rewinddir().
* Should reset the output generated by self::dir_readdir, i.e. the next
* call to self::dir_readdir should return the first entry in the location
* returned by self::dir_opendir.
*/
public function dir_rewinddir()
{
\rewinddir($this->getStream());
}
/**
* Create a directory.
* This method is called in response to mkdir().
*/
public function mkdir(string $path, int $mode, int $options): bool
{
if (null === $this->context) {
return \mkdir(
static::realPath($path, false),
$mode,
$options | \STREAM_MKDIR_RECURSIVE
);
}
return \mkdir(
static::realPath($path, false),
$mode,
(bool) ($options | \STREAM_MKDIR_RECURSIVE),
$this->context
);
}
/**
* Rename a file or directory.
* This method is called in response to rename().
* Should attempt to rename $from to $to.
*/
public function rename(string $from, string $to): bool
{
if (null === $this->context) {
return \rename(static::realPath($from), static::realPath($to, false));
}
return \rename(
static::realPath($from),
static::realPath($to, false),
$this->context
);
}
/**
* Remove a directory.
* This method is called in response to rmdir().
* The `$options` input is a bitwise mask of values. It is not used here.
*/
public function rmdir(string $path, int $options): bool
{
if (null === $this->context) {
return \rmdir(static::realPath($path));
}
return \rmdir(static::realPath($path), $this->context);
}
/**
* Delete a file.
* This method is called in response to unlink().
*/
public function unlink(string $path): bool
{
if (null === $this->context) {
return \unlink(static::realPath($path));
}
return \unlink(static::realPath($path), $this->context);
}
/**
* Retrieve information about a file.
* This method is called in response to all stat() related functions.
* The `$flags` input holds additional flags set by the streams API. It
* can hold one or more of the following values OR'd together.
* STREAM_URL_STAT_LINK: for resource with the ability to link to other
* resource (such as an HTTP location: forward, or a filesystem
* symlink). This flag specified that only information about the link
* itself should be returned, not the resource pointed to by the
* link. This flag is set in response to calls to lstat(), is_link(), or
* filetype(). STREAM_URL_STAT_QUIET: if this flag is set, our wrapper
* should not raise any errors. If this flag is not set, we are
* responsible for reporting errors using the trigger_error() function
* during stating of the path.
*/
public function url_stat(string $path, int $flags)
{
$path = static::realPath($path);
if (Protocol::NO_RESOLUTION === $path) {
if ($flags & \STREAM_URL_STAT_QUIET) {
return 0;
} else {
return \trigger_error(
'Path '.$path.' cannot be resolved.',
\E_WARNING
);
}
}
if ($flags & \STREAM_URL_STAT_LINK) {
return @\lstat($path);
}
return @\stat($path);
}
/**
* Get stream resource.
*/
public function getStream()
{
return $this->_stream;
}
/**
* Get stream name.
*/
public function getStreamName()
{
return $this->_streamName;
}
}
/*
* Register the `hoa://` protocol.
*/
\stream_wrapper_register('hoa', ProtocolWrapper::class);