====== Request for Comments: Stream Metadata ====== * Version: 1.0 * Date: 2011-03-13 * Author: Stas Malyshev * Status: Implemented in 5.4 * First Published at: http://wiki.php.net/rfc/streammetadata * Patch: http://random-bits-of.info/stream_meta.diff ====== Purpose ====== PHP file streams provide very powerful and useful abstraction layer over the I/O-related functions. However, there's a group of functions which are excluded from this support - namely, touch(), chmod(), chown() and chgrp() - i.e., functions dealing with file metadata. This lead to libraries implementing FS virtualization developing various hacks, such as here: https://code.google.com/p/bovigo/wiki/vfsStreamDocsFilemode The purpose of this RFC is to plug this hole by creating stream metadata API allowing to override these functions. ====== Engine part ====== The stream wrapper gets an additional optional handler, like this: int (*stream_metadata)(php_stream_wrapper *wrapper, char *url, int options, void *value, php_stream_context *context TSRMLS_DC); The options supported currently are: #define PHP_STREAM_META_TOUCH 1 #define PHP_STREAM_META_OWNER_NAME 2 #define PHP_STREAM_META_OWNER 3 #define PHP_STREAM_META_OWNER_NAME 4 #define PHP_STREAM_META_GROUP 5 #define PHP_STREAM_META_ACCESS 6 implementing various aspects of touch(), chmod(), etc. Values are defined by option and currently are: * For PHP_STREAM_META_TOUCH - struct utime * * For PHP_STREAM_META_OWNER_NAME, PHP_STREAM_META_OWNER_NAME - char * * For all the rest - long * The return value is 0 on failure, non-0 on success. ====== Userspace part ====== Userspace stream handler implements this wrapper by using userspace method: public function stream_metadata($path, $option, $value) See example below as for suggested implementation for virtualized filesystem. The option values match options above, and have constants defined without the PHP prefix (i.e., STREAM_META_TOUCH, etc.). The value is: * For STREAM_META_TOUCH - array specifying modification and access time (both optional, can be empty array) * For STREAM_META_OWNER_NAME, STREAM_META_OWNER_NAME - string specifying the name * For all the rest - integer The return value is true on success, false on failure. The stream implementor has to decide what to do with unknown/unsupported options. ====== Examples ====== Virtual stream usage (mostly as before): Virtual stream definition: dirp); } public function dir_opendir ($path, $options ) { $this->dirp = opendir($this->path($path)); return !empty($this->dirp); } public function dir_readdir() { return readdir($this->dirp); } public function dir_rewinddir() { return rewinddir($this->dirp); } public function mkdir($path, $mode, $options) { return mkdir($this->path($path), $mode, ($options&STREAM_MKDIR_RECURSIVE) != 0); } public function rename($path_from, $path_to) { return rename($this->path($path_from), $this->path($path_to)); } public function rmdir($path, $options) { return rmdir($this->path($path)); } public function stream_cast ($cast_as) { return $this->fp; } public function stream_close () { fclose($this->fp); return true; } public function stream_eof () { return feof($this->fp); } public function stream_flush () { return fflush($this->fp); } public function stream_lock($operation) { return flock($this->fp, $operation); } public function stream_open($path, $mode) { $fullpath = $this->path($path); if($mode == 'r') { $this->fp = fopen($fullpath, $mode); } else { // if we will be writing, try to transparently create the directory $this->fp = @fopen($fullpath, $mode); if(!$this->fp && !file_exists(dirname($fullpath))) { mkdir(dirname($fullpath), 0755, true); $this->fp = fopen($fullpath, $mode); } } return !empty($this->fp); } public function stream_read($count) { return fread($this->fp, $count); } public function stream_seek($offset, $whence = SEEK_SET) { return fseek($this->fp, $offset, $whence); } public function stream_set_option($option, $arg1, $arg2) { return true; } public function stream_stat() { return fstat($this->fp); } public function stream_tell() { return ftell($this->fp); } public function stream_write($data) { return fwrite($this->fp, $data); } public function unlink($path) { unlink($this->path($path)); return true; } public function url_stat($path, $flags) { return @stat($this->path($path)); } public function stream_metadata($path, $option, $var) { $path = $this->path($path); switch($option) { case STREAM_META_TOUCH: array_unshift($var, $path); return call_user_func_array("touch", $var); case STREAM_META_OWNER: case STREAM_META_OWNER_NAME: return chown($path, $var); case STREAM_META_GROUP: case STREAM_META_GROUP_NAME: return chgrp($path, $var); case STREAM_META_ACCESS: return chmod($path, $var); } } } ====== TODO ====== ch{own|mod}() functions are not defined on Netware and defined, but not implemented on Windows. While this API does not limit its implementation to any particular set of features, currently it follows the existing API implementation, not improving on it. We could add additional capabilities allowing to support these operations on these systems in one way or another.