Skip to content

Instantly share code, notes, and snippets.

@chx
Last active August 29, 2015 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chx/0db97d97661b50381907 to your computer and use it in GitHub Desktop.
Save chx/0db97d97661b50381907 to your computer and use it in GitHub Desktop.
<?php
/**
* @file
* Contains \Drupal\Core\StreamWrapper\FileDatabaseStream.
*/
namespace Drupal\Core\StreamWrapper;
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
class PharDatabaseStream {
// This file doesn't exist. When you try reading
// phar://./drupal-phpstorage.phar/something.php the value will be read from
// a K-V store. The key will be a hash of a secret and "something.php".
const MAGIC_NAME = 'phar://./drupal-phpstorage.phar/';
/**
* Stream context resource.
*
* @var resource
*/
public $context = NULL;
/**
* A generic resource handle.
*
* @var resource
*/
public $handle = NULL;
/**
* @var KeyValueStoreInterface
*/
protected $storage;
/**
* @var string
*/
protected $key;
public function __construct(KeyValueStoreInterface $storage, $secret) {
$this->storage = $storage;
$this->secret = $secret;
}
public function dir_closedir() {
closedir($this->handle);
return TRUE;
}
public function dir_opendir($path, $options) {
$this->usePhpWrapper();
$this->key = '';
$this->handle = opendir($path, $this->context);
$this->useOurWrapper();
return (bool) $this->handle;
}
public function dir_readdir() {
return readdir($this->handle);
}
public function dir_rewinddir() {
rewinddir($this->handle);
}
public function mkdir($path, $mode, $options) {
$this->usePhpWrapper();
$return = mkdir($path, $mode, $options);
$this->useOurWrapper();
return $return;
}
public function rename($path_from, $path_to) {
$this->usePhpWrapper();
$return = rename($path_from, $path_to);
$this->useOurWrapper();
return $return;
}
public function rmdir($path, $options) {
$this->usePhpWrapper();
$return = rmdir($path, $options);
$this->useOurWrapper();
return $return;
}
public function stream_cast($cast_as) {
return $this->handle ? $this->handle : FALSE;
}
public function stream_close () {
if ($this->key) {
fseek($this->handle, 0);
$buf = '';
while (!feof($this->handle)) {
$buf .= fread($this->handle, 1048576);
}
$this->storage->setMultiple([
$this->key => $buf,
"$this->key:mtime" => time(),
"$this->key:size" => strlen($buf),
]);
}
return fclose($this->handle);
}
public function stream_eof() {
return feof($this->handle);
}
public function stream_flush() {
return fflush($this->handle);
}
public function stream_lock($operation) {
return flock($this->handle, $operation);
}
public function stream_metadata($path, $option, $value) {
$this->usePhpWrapper();
$return = LocalStream::metadata($path, $option, $value);
$this->useOurWrapper();
return $return;
}
public function stream_open($path, $mode, $options, &$opened_path) {
$this->key = '';
if ($filename = $this->getFilename($path)) {
$this->handle = $this->openFromStorage($filename, $mode);
}
else {
$this->usePhpWrapper();
$this->handle = fopen($path, $mode, $options & STREAM_USE_PATH, $this->context);
$this->useOurWrapper();
}
// @TODO is this necessary?
if ($this->handle) {
$opened_path = $path;
return TRUE;
}
return FALSE;
}
public function stream_read($count) {
return fread($this->handle, $count);
}
public function stream_seek($offset, $whence = SEEK_SET) {
return fseek($this->handle, $offset, $whence);
}
public function stream_set_option($option, $arg1, $arg2) {
switch ($option) {
case STREAM_OPTION_BLOCKING:
return stream_set_blocking($this->handle, $arg1);
case STREAM_OPTION_READ_TIMEOUT:
return stream_set_timeout($this->handle, $arg1, $arg2);
case STREAM_OPTION_WRITE_BUFFER:
return stream_set_write_buffer($this->handle, $arg1);
case STREAM_OPTION_READ_BUFFER:
return stream_set_read_buffer($this->handle, $arg1);
}
}
public function stream_stat() {
return fstat($this->handle);
}
public function stream_tell() {
return ftell($this->handle);
}
public function stream_truncate($new_size) {
return ftruncate($this->handle, $new_size);
}
public function stream_write ($data) {
return fwrite($this->handle, $data);
}
public function unlink($path) {
$this->usePhpWrapper();
$return = unlink($path, $this->context);
$this->useOurWrapper();
return $return;
}
public function url_stat($path, $flags) {
if ($flags & STREAM_URL_STAT_QUIET) {
set_error_handler(function() {});
}
$this->usePhpWrapper();
if ($filename = $this->getFilename($path)) {
$return = $this->statFromStorage($filename);
}
else {
$return = stat($path);
}
$this->useOurWrapper();
if ($flags & STREAM_URL_STAT_QUIET) {
restore_error_handler();
}
return $return;
}
protected function usePhpWrapper() {
stream_wrapper_restore('phar');
}
protected function useOurWrapper() {
stream_wrapper_register('phar', get_called_class());
}
protected function getFilename($path) {
$n = strlen(static::MAGIC_NAME);
if (substr($path, 0, $n) === static::MAGIC_NAME) {
return substr($path, $n);
}
return FALSE;
}
/**
* @param $filename
* @param $mode
*
* @return resource|FALSE
*/
protected function openFromStorage($filename, $mode) {
$handle = FALSE;
$key = $this->getKeyFromFilename($filename);
$data = $this->storage->getMultiple($key);
if (isset($data[$key])) {
if ($mode !== 'r') {
$this->key = $key;
}
$handle = fopen('php://memory', 'rw');
fwrite($this->handle, $data[$key]);
fseek($this->handle, 0);
}
return $handle;
}
/**
* @return array
*/
protected function statFromStorage($filename) {
$key = $this->getKeyFromFilename($filename);
$return = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1];
$data = $this->storage->getMultiple(["$key:size", "$key:mtime"]);
if (count($data) == 2) {
$return[7] = $data["$key:size"];
$return[9] = $data["$key:mtime"];
}
return $return;
}
/**
* @param $filename
* @return string
*/
protected function getKeyFromFilename($filename) {
return hash_hmac('sha256', $filename, $this->secret);
}
}
diff --git a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
index 7720655..5bf4bcb 100644
--- a/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
+++ b/core/lib/Drupal/Core/StreamWrapper/LocalStream.php
@@ -50,6 +50,46 @@ public static function getType() {
}
/**
+ * @param $target
+ * @param $option
+ * @param $value
+ * @return bool
+ */
+ public static function metadata($target, $option, $value) {
+ $return = FALSE;
+ switch ($option) {
+ case STREAM_META_TOUCH:
+ if (!empty($value)) {
+ $return = touch($target, $value[0], $value[1]);
+ }
+ else {
+ $return = touch($target);
+ }
+ break;
+
+ case STREAM_META_OWNER_NAME:
+ case STREAM_META_OWNER:
+ $return = chown($target, $value);
+ break;
+
+ case STREAM_META_GROUP_NAME:
+ case STREAM_META_GROUP:
+ $return = chgrp($target, $value);
+ break;
+
+ case STREAM_META_ACCESS:
+ $return = chmod($target, $value);
+ break;
+ }
+ if ($return) {
+ // For convenience clear the file status cache of the underlying file,
+ // since metadata operations are often followed by file status checks.
+ clearstatcache(TRUE, $target);
+ }
+ return $return;
+ }
+
+ /**
* Gets the path that the wrapper is responsible for.
*
* @todo Review this method name in D8 per https://www.drupal.org/node/701358.
@@ -304,37 +344,7 @@ public function stream_cast($cast_as) {
*/
public function stream_metadata($uri, $option, $value) {
$target = $this->getLocalPath($uri);
- $return = FALSE;
- switch ($option) {
- case STREAM_META_TOUCH:
- if (!empty($value)) {
- $return = touch($target, $value[0], $value[1]);
- }
- else {
- $return = touch($target);
- }
- break;
-
- case STREAM_META_OWNER_NAME:
- case STREAM_META_OWNER:
- $return = chown($target, $value);
- break;
-
- case STREAM_META_GROUP_NAME:
- case STREAM_META_GROUP:
- $return = chgrp($target, $value);
- break;
-
- case STREAM_META_ACCESS:
- $return = chmod($target, $value);
- break;
- }
- if ($return) {
- // For convenience clear the file status cache of the underlying file,
- // since metadata operations are often followed by file status checks.
- clearstatcache(TRUE, $target);
- }
- return $return;
+ return static::metadata($target, $option, $value);
}
/**
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment