842 lines
26 KiB
PHP
842 lines
26 KiB
PHP
<?php
|
|
/**
|
|
* VFS implementation for Horde's database abstraction layer.
|
|
*
|
|
* Required values for $params:
|
|
* - db: A Horde_Db_Adapter object.
|
|
*
|
|
* Optional values:
|
|
* - table: (string) The name of the vfs table in 'database'. Defaults to
|
|
* 'horde_vfs'.
|
|
*
|
|
* The table structure for the VFS can be created with the horde-db-migrate
|
|
* script from the Horde_Db package.
|
|
*
|
|
* Copyright 2002-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file COPYING for license information (LGPL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
|
|
*
|
|
* @author Chuck Hagenbuch <chuck@horde.org>
|
|
* @author Jan Schneider <jan@horde.org>
|
|
* @category Horde
|
|
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
|
|
* @package VFS
|
|
*/
|
|
class Horde_Vfs_Sql extends Horde_Vfs_Base
|
|
{
|
|
/* File value for vfs_type column. */
|
|
const FILE = 1;
|
|
|
|
/* Folder value for vfs_type column. */
|
|
const FOLDER = 2;
|
|
|
|
/**
|
|
* Handle for the current database connection.
|
|
*
|
|
* @var Horde_Db
|
|
*/
|
|
protected $_db = false;
|
|
|
|
/**
|
|
* List of permissions and if they can be changed in this VFS backend.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_permissions = array();
|
|
|
|
/**
|
|
* List of features that the VFS driver supports.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_features = array(
|
|
'readByteRange' => true,
|
|
);
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param array $params A hash containing connection parameters.
|
|
*/
|
|
public function __construct($params = array())
|
|
{
|
|
$params = array_merge(array('table' => 'horde_vfs'), $params);
|
|
parent::__construct($params);
|
|
$this->_db = $this->_params['db'];
|
|
unset($this->_params['db']);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the filesize from the VFS.
|
|
*
|
|
* @param string $path The pathname to the file.
|
|
* @param string $name The filename to retrieve.
|
|
*
|
|
* @return integer The file size.
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
public function size($path, $name)
|
|
{
|
|
$length_op = $this->_getFileSizeOp();
|
|
$path = $this->_convertPath($path);
|
|
list($op, $values) = $this->_nullString($path);
|
|
$sql = sprintf(
|
|
'SELECT %s(vfs_data) FROM %s WHERE vfs_path %s AND vfs_name = ?',
|
|
$length_op,
|
|
$this->_params['table'],
|
|
$op
|
|
);
|
|
$values[] = $name;
|
|
try {
|
|
$size = $this->_db->selectValue($sql, $values);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
|
|
if ($size === false) {
|
|
throw new Horde_Vfs_Exception(sprintf('Unable to check file size of "%s/%s".', $path, $name));
|
|
}
|
|
|
|
return $size;
|
|
}
|
|
|
|
/**
|
|
* Returns the size of a folder.
|
|
*
|
|
* @param string $path The path of the folder.
|
|
*
|
|
* @return integer The size of the folder in bytes.
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
public function getFolderSize($path = null)
|
|
{
|
|
try {
|
|
$where = null;
|
|
$params = array();
|
|
if (strlen($path)) {
|
|
$where = 'WHERE vfs_path = ? OR vfs_path LIKE ?';
|
|
$path = $this->_convertPath($path);
|
|
$params = array($path, $path . '/%');
|
|
}
|
|
$sql = sprintf('SELECT SUM(%s(vfs_data)) FROM %s %s',
|
|
$this->_getFileSizeOp(),
|
|
$this->_params['table'],
|
|
$where);
|
|
$size = $this->_db->selectValue($sql, $params);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
|
|
return (int)$size;
|
|
}
|
|
|
|
/**
|
|
* Retrieves a file from the VFS.
|
|
*
|
|
* @param string $path The pathname to the file.
|
|
* @param string $name The filename to retrieve.
|
|
*
|
|
* @return string The file data.
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
public function read($path, $name)
|
|
{
|
|
return $this->_readBlob($this->_params['table'], 'vfs_data', array(
|
|
'vfs_path' => $this->_convertPath($path),
|
|
'vfs_name' => $name
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Retrieves a part of a file from the VFS. Particularly useful
|
|
* when reading large files which would exceed the PHP memory
|
|
* limits if they were stored in a string.
|
|
*
|
|
* @param string $path The pathname to the file.
|
|
* @param string $name The filename to retrieve.
|
|
* @param integer $offset The offset of the part. (The new offset will
|
|
* be stored in here).
|
|
* @param integer $length The length of the part. If the length = -1,
|
|
* the whole part after the offset is
|
|
* retrieved. If more bytes are given as exists
|
|
* after the given offset. Only the available
|
|
* bytes are read.
|
|
* @param integer $remaining The bytes that are left, after the part that
|
|
* is retrieved.
|
|
*
|
|
* @return string The file data.
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
public function readByteRange($path, $name, &$offset, $length, &$remaining)
|
|
{
|
|
$data = $this->_readBlob($this->_params['table'], 'vfs_data', array(
|
|
'vfs_path' => $this->_convertPath($path),
|
|
'vfs_name' => $name
|
|
));
|
|
|
|
// Calculate how many bytes MUST be read, so the remainging
|
|
// bytes and the new offset can be calculated correctly.
|
|
$size = strlen ($data);
|
|
if ($length == -1 || (($length + $offset) > $size)) {
|
|
$length = $size - $offset;
|
|
}
|
|
if ($remaining < 0) {
|
|
$remaining = 0;
|
|
}
|
|
|
|
$data = substr($data, $offset, $length);
|
|
$offset = $offset + $length;
|
|
$remaining = $size - $offset;
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Stores a file in the VFS.
|
|
*
|
|
* @param string $path The path to store the file in.
|
|
* @param string $name The filename to use.
|
|
* @param string $tmpFile The temporary file containing the data to
|
|
* be stored.
|
|
* @param boolean $autocreate Automatically create directories?
|
|
*
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
public function write($path, $name, $tmpFile, $autocreate = false)
|
|
{
|
|
/* Don't need to check quota here since it will be checked when
|
|
* writeData() is called. */
|
|
if (!$stream = @fopen($tmpFile, 'rb')) {
|
|
throw new Horde_Vfs_Exception('Unable to open ' . $tmpFile);
|
|
}
|
|
|
|
$result = $this->writeData(
|
|
$path, $name, $stream, $autocreate);
|
|
fclose($stream);
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Store a file in the VFS from raw data.
|
|
*
|
|
* @param string $path The path to store the file in.
|
|
* @param string $name The filename to use.
|
|
* @param string|resource $data The data as a string or stream resource.
|
|
* Resources allowed @since 2.4.0
|
|
* @param boolean $autocreate Automatically create directories?
|
|
*
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
public function writeData($path, $name, $data, $autocreate = false)
|
|
{
|
|
$data = $this->_ensureSeekable($data);
|
|
$this->_checkQuotaWrite('string', $data, $path, $name);
|
|
|
|
$path = $this->_convertPath($path);
|
|
list($op, $values) = $this->_nullString($path);
|
|
|
|
/* Check to see if the data already exists. */
|
|
try {
|
|
$sql = sprintf(
|
|
'SELECT vfs_id FROM %s WHERE vfs_path %s AND vfs_name = ?',
|
|
$this->_params['table'],
|
|
$op
|
|
);
|
|
$values[] = $name;
|
|
$id = $this->_db->selectValue($sql, $values);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
|
|
if ($id) {
|
|
$this->_updateBlob($this->_params['table'], 'vfs_data', $data,
|
|
array('vfs_id' => $id),
|
|
array('vfs_modified' => time()));
|
|
return;
|
|
}
|
|
|
|
/* Check to see if the folder already exists. */
|
|
$dirs = explode('/', $path);
|
|
$path_name = array_pop($dirs);
|
|
$parent = implode('/', $dirs);
|
|
if (!$this->isFolder($parent, $path_name)) {
|
|
if (!$autocreate) {
|
|
throw new Horde_Vfs_Exception(sprintf('Folder "%s" does not exist', $path));
|
|
}
|
|
|
|
$this->autocreatePath($path);
|
|
}
|
|
|
|
return $this->_insertBlob($this->_params['table'], 'vfs_data', $data, array(
|
|
'vfs_type' => self::FILE,
|
|
'vfs_path' => strlen($path) ? $path : null,
|
|
'vfs_name' => $name,
|
|
'vfs_modified' => time(),
|
|
'vfs_owner' => $this->_params['user']
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Delete a file from the VFS.
|
|
*
|
|
* @param string $path The path to store the file in.
|
|
* @param string $name The filename to use.
|
|
*
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
public function deleteFile($path, $name)
|
|
{
|
|
$this->_checkQuotaDelete($path, $name);
|
|
|
|
$path = $this->_convertPath($path);
|
|
list($op, $values) = $this->_nullString($path);
|
|
|
|
try {
|
|
$sql = sprintf(
|
|
'DELETE FROM %s WHERE vfs_type = ? AND vfs_path %s AND vfs_name = ?',
|
|
$this->_params['table'],
|
|
$op
|
|
);
|
|
array_unshift($values, self::FILE);
|
|
array_push($values, $name);
|
|
$result = $this->_db->delete($sql, $values);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
|
|
if ($result == 0) {
|
|
throw new Horde_Vfs_Exception('Unable to delete VFS file.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Rename a file or folder in the VFS.
|
|
*
|
|
* @param string $oldpath The old path to the file.
|
|
* @param string $oldname The old filename.
|
|
* @param string $newpath The new path of the file.
|
|
* @param string $newname The new filename.
|
|
*
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
public function rename($oldpath, $oldname, $newpath, $newname)
|
|
{
|
|
if (strpos($newpath, '/') === false) {
|
|
$parent = '';
|
|
$path = $newpath;
|
|
} else {
|
|
list($parent, $path) = explode('/', $newpath, 2);
|
|
}
|
|
|
|
if (!$this->isFolder($parent, $path)) {
|
|
$this->autocreatePath($newpath);
|
|
}
|
|
|
|
$oldpath = $this->_convertPath($oldpath);
|
|
$newpath = $this->_convertPath($newpath);
|
|
if (!strlen($newpath)) {
|
|
$newpath = null;
|
|
}
|
|
list($op, $values) = $this->_nullString($oldpath);
|
|
|
|
$sql = sprintf(
|
|
'UPDATE %s SET vfs_path = ?, vfs_name = ?, vfs_modified = ? WHERE vfs_path %s AND vfs_name = ?',
|
|
$this->_params['table'],
|
|
$op
|
|
);
|
|
$values = array_merge(
|
|
array($newpath, $newname, time()),
|
|
$values,
|
|
array($oldname)
|
|
);
|
|
|
|
try {
|
|
$result = $this->_db->update($sql, $values);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
|
|
if ($result == 0) {
|
|
throw new Horde_Vfs_Exception('Unable to rename VFS file.');
|
|
}
|
|
|
|
$this->_recursiveRename($oldpath, $oldname, $newpath, $newname);
|
|
}
|
|
|
|
/**
|
|
* Creates a folder on the VFS.
|
|
*
|
|
* @param string $path Holds the path of directory to create folder.
|
|
* @param string $name Holds the name of the new folder.
|
|
*
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
public function createFolder($path, $name)
|
|
{
|
|
$path = $this->_convertPath($path);
|
|
if (!strlen($path)) {
|
|
$path = null;
|
|
}
|
|
$sql = sprintf(
|
|
'INSERT INTO %s (vfs_type, vfs_path, vfs_name, vfs_modified, vfs_owner) VALUES (?, ?, ?, ?, ?)',
|
|
$this->_params['table']
|
|
);
|
|
$values = array(self::FOLDER, $path, $name, time(), $this->_params['user'] ?: null);
|
|
|
|
try {
|
|
$this->_db->insert($sql, $values);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Horde_Vfs_Sql override of isFolder() to check for root folder.
|
|
*
|
|
* @param string $path Path to possible folder
|
|
* @param string $name Name of possible folder
|
|
*
|
|
* @return boolean True if $path/$name is a folder
|
|
*/
|
|
public function isFolder($path, $name)
|
|
{
|
|
$path = $this->_convertPath($path);
|
|
if ($path == '' && $name == '') {
|
|
// The root of VFS is always a folder.
|
|
return true;
|
|
}
|
|
$path = $this->_getNativePath($path, $name);
|
|
$name = basename($path);
|
|
$path = dirname($path);
|
|
if ($path == '.') {
|
|
$path = '';
|
|
}
|
|
list($op, $values) = $this->_nullString($path);
|
|
try {
|
|
return (bool)$this->_db->selectValue(
|
|
sprintf(
|
|
'SELECT 1 FROM %s WHERE vfs_type = ? AND vfs_path %s AND vfs_name = ?',
|
|
$this->_params['table'],
|
|
$op
|
|
),
|
|
array_merge(array(self::FOLDER), $values, array($name))
|
|
);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a folder from the VFS.
|
|
*
|
|
* @param string $path The path of the folder.
|
|
* @param string $name The folder name to use.
|
|
* @param boolean $recursive Force a recursive delete?
|
|
*
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
public function deleteFolder($path, $name, $recursive = false)
|
|
{
|
|
$path = $this->_convertPath($path);
|
|
$folderPath = $this->_getNativePath($path, $name);
|
|
|
|
/* Check if not recursive and fail if directory not empty */
|
|
if (!$recursive) {
|
|
$folderList = $this->listFolder($folderPath, null, true);
|
|
if (!empty($folderList)) {
|
|
throw new Horde_Vfs_Exception(sprintf('Unable to delete %s, the directory is not empty', $path . '/' . $name));
|
|
}
|
|
}
|
|
|
|
/* Remember the size of the folder. */
|
|
if (!is_null($this->_vfsSize)) {
|
|
$size = $this->getFolderSize($folderPath);
|
|
}
|
|
|
|
/* First delete everything below the folder, so if error we get no
|
|
* orphans. */
|
|
try {
|
|
$sql = sprintf('DELETE FROM %s WHERE vfs_path %s',
|
|
$this->_params['table'],
|
|
' LIKE ' . $this->_db->quote($this->_getNativePath($folderPath, '%')));
|
|
$this->_db->delete($sql);
|
|
} catch (Horde_Db_Exception $e) {
|
|
$this->_vfsSize = null;
|
|
throw new Horde_Vfs_Exception('Unable to delete VFS recursively: ' . $e->getMessage());
|
|
}
|
|
|
|
/* Now delete everything inside the folder. */
|
|
list ($op, $values) = $this->_nullString($folderPath);
|
|
try {
|
|
$sql = sprintf(
|
|
'DELETE FROM %s WHERE vfs_path %s',
|
|
$this->_params['table'],
|
|
$op
|
|
);
|
|
$this->_db->delete($sql, $values);
|
|
} catch (Horde_Db_Exception $e) {
|
|
$this->_vfsSize = null;
|
|
throw new Horde_Vfs_Exception('Unable to delete VFS directory: ' . $e->getMessage());
|
|
}
|
|
|
|
/* All ok now delete the actual folder */
|
|
list($op, $values) = $this->_nullString($path);
|
|
try {
|
|
$sql = sprintf(
|
|
'DELETE FROM %s WHERE vfs_path %s AND vfs_name = ?',
|
|
$this->_params['table'],
|
|
$op
|
|
);
|
|
$values[] = $name;
|
|
$this->_db->delete($sql, $values);
|
|
} catch (Horde_Db_Exception $e) {
|
|
$this->_vfsSize = null;
|
|
throw new Horde_Vfs_Exception('Unable to delete VFS directory: ' . $e->getMessage());
|
|
}
|
|
|
|
/* Update VFS size. */
|
|
if (!is_null($this->_vfsSize)) {
|
|
$this->_vfsSize -= $size;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns an an unsorted file list of the specified directory.
|
|
*
|
|
* @param string $path The path of the directory.
|
|
* @param string|array $filter Regular expression(s) to filter
|
|
* file/directory name on.
|
|
* @param boolean $dotfiles Show dotfiles?
|
|
* @param boolean $dironly Show only directories?
|
|
*
|
|
* @return array File list.
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
protected function _listFolder($path, $filter = null, $dotfiles = true,
|
|
$dironly = false)
|
|
{
|
|
if (!$this->isFolder(dirname($path), basename($path))) {
|
|
throw new Horde_Vfs_Exception(sprintf('"%s" is not a folder.', $path));
|
|
}
|
|
|
|
$path = $this->_convertPath($path);
|
|
list($op, $values) = $this->_nullString($path);
|
|
|
|
try {
|
|
$length_op = $this->_getFileSizeOp();
|
|
$sql = sprintf(
|
|
'SELECT vfs_name, vfs_type, %s(vfs_data) length, vfs_modified, vfs_owner FROM %s WHERE vfs_path %s',
|
|
$length_op,
|
|
$this->_params['table'],
|
|
$op
|
|
);
|
|
$fileList = $this->_db->select($sql, $values);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
|
|
$files = array();
|
|
foreach ($fileList as $line) {
|
|
// Filter out dotfiles if they aren't wanted.
|
|
if (!$dotfiles && substr($line['vfs_name'], 0, 1) == '.') {
|
|
continue;
|
|
}
|
|
|
|
$file['name'] = $line['vfs_name'];
|
|
|
|
if ($line['vfs_type'] == self::FILE) {
|
|
$name = explode('.', $line['vfs_name']);
|
|
|
|
if (count($name) == 1) {
|
|
$file['type'] = '**none';
|
|
} else {
|
|
$file['type'] = Horde_String::lower($name[count($name) - 1]);
|
|
}
|
|
|
|
$file['size'] = $line['length'];
|
|
} elseif ($line['vfs_type'] == self::FOLDER) {
|
|
$file['type'] = '**dir';
|
|
$file['size'] = -1;
|
|
}
|
|
|
|
$file['date'] = $line['vfs_modified'];
|
|
$file['owner'] = isset($line['vfs_owner']) ? $line['vfs_owner'] : '';
|
|
$file['perms'] = '';
|
|
$file['group'] = '';
|
|
|
|
// filtering
|
|
if ($this->_filterMatch($filter, $file['name'])) {
|
|
unset($file);
|
|
continue;
|
|
}
|
|
if ($dironly && $file['type'] !== '**dir') {
|
|
unset($file);
|
|
continue;
|
|
}
|
|
|
|
$files[$file['name']] = $file;
|
|
unset($file);
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
/**
|
|
* Garbage collect files in the VFS storage system.
|
|
*
|
|
* @param string $path The VFS path to clean.
|
|
* @param integer $secs The minimum amount of time (in seconds) required
|
|
* before a file is removed.
|
|
*
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
public function gc($path, $secs = 345600)
|
|
{
|
|
$path = $this->_convertPath($path);
|
|
list($op, $values) = $this->_nullString($path);
|
|
$sql = sprintf(
|
|
'DELETE FROM %s WHERE vfs_type = ? AND vfs_modified < ? AND (vfs_path %s OR vfs_path LIKE ?)',
|
|
$this->_params['table'],
|
|
$op
|
|
);
|
|
$values = array_merge(
|
|
array(self::FILE, time() - $secs),
|
|
$values,
|
|
array($path . '/%')
|
|
);
|
|
|
|
try {
|
|
$this->_db->delete($sql, $values);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Renames all child paths.
|
|
*
|
|
* @param string $path The path of the folder to rename.
|
|
* @param string $name The foldername to use.
|
|
*
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
protected function _recursiveRename($oldpath, $oldname, $newpath, $newname)
|
|
{
|
|
$oldpath = $this->_convertPath($oldpath);
|
|
$newpath = $this->_convertPath($newpath);
|
|
|
|
$sql = sprintf(
|
|
'SELECT vfs_name FROM %s WHERE vfs_type = ? AND vfs_path = ?',
|
|
$this->_params['table']
|
|
);
|
|
$values = array(self::FOLDER, $this->_getNativePath($oldpath, $oldname));
|
|
|
|
try {
|
|
$folderList = $this->_db->selectValues($sql, $values);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
|
|
foreach ($folderList as $folder) {
|
|
$this->_recursiveRename($this->_getNativePath($oldpath, $oldname), $folder, $this->_getNativePath($newpath, $newname), $folder);
|
|
}
|
|
|
|
$sql = sprintf(
|
|
'UPDATE %s SET vfs_path = ? WHERE vfs_path = ?',
|
|
$this->_params['table']
|
|
);
|
|
$values = array(
|
|
$this->_getNativePath($newpath, $newname),
|
|
$this->_getNativePath($oldpath, $oldname)
|
|
);
|
|
|
|
try {
|
|
$this->_db->update($sql, $values);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return a full filename on the native filesystem, from a VFS
|
|
* path and name.
|
|
*
|
|
* @param string $path The VFS file path.
|
|
* @param string $name The VFS filename.
|
|
*
|
|
* @return string The full native filename.
|
|
*/
|
|
protected function _getNativePath($path, $name)
|
|
{
|
|
if (!strlen($path)) {
|
|
return $name;
|
|
}
|
|
|
|
if (isset($this->_params['home']) &&
|
|
preg_match('|^~/?(.*)$|', $path, $matches)) {
|
|
$path = $this->_params['home'] . '/' . $matches[1];
|
|
}
|
|
|
|
return $path . '/' . $name;
|
|
}
|
|
|
|
/**
|
|
* Read file data from the SQL VFS backend.
|
|
*
|
|
* @param string $table The VFS table name.
|
|
* @param string $field TODO
|
|
* @param array $criteria TODO
|
|
*
|
|
* @return mixed TODO
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
protected function _readBlob($table, $field, $criteria)
|
|
{
|
|
if (!count($criteria)) {
|
|
throw new Horde_Vfs_Exception('You must specify the fetch criteria');
|
|
}
|
|
|
|
$where = '';
|
|
$values = array();
|
|
foreach ($criteria as $key => $value) {
|
|
if (!empty($where)) {
|
|
$where .= ' AND ';
|
|
}
|
|
list($op, $val) = $this->_nullString($value);
|
|
$where .= $key . ' ' . $op;
|
|
$values = array_merge($values, $val);
|
|
}
|
|
|
|
$sql = sprintf(
|
|
'SELECT %s FROM %s WHERE %s',
|
|
$field, $table, $where
|
|
);
|
|
|
|
try {
|
|
$result = $this->_db->selectValue($sql, $values);
|
|
$columns = $this->_db->columns($table);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
|
|
if ($result === false) {
|
|
throw new Horde_Vfs_Exception('Unable to load SQL data.');
|
|
}
|
|
|
|
return $columns[$field]->binaryToString($result);
|
|
}
|
|
|
|
/**
|
|
* TODO
|
|
*
|
|
* @param string $table TODO
|
|
* @param string $field TODO
|
|
* @param string $data TODO
|
|
* @param string $attributes TODO
|
|
*
|
|
* @return mixed TODO
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
protected function _insertBlob($table, $field, $data, $attributes)
|
|
{
|
|
$attributes[$field] = new Horde_Db_Value_Binary($data);
|
|
|
|
/* Execute the query. */
|
|
try {
|
|
$this->_db->insertBlob($table, $attributes);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TODO
|
|
*
|
|
* @param string $table TODO
|
|
* @param string $field TODO
|
|
* @param string $data TODO
|
|
* @param string $where TODO
|
|
* @param array $alsoupdate TODO
|
|
*
|
|
* @return mixed TODO
|
|
* @throws Horde_Vfs_Exception
|
|
*/
|
|
protected function _updateBlob($table, $field, $data, $where, $alsoupdate)
|
|
{
|
|
$wherestring = '';
|
|
$wherevalues = array();
|
|
foreach ($where as $key => $value) {
|
|
if (!empty($wherestring)) {
|
|
$wherestring .= ' AND ';
|
|
}
|
|
$wherestring .= $key . ' = ?';
|
|
$wherevalues[] = $value;
|
|
}
|
|
|
|
/* Execute the query. */
|
|
try {
|
|
$this->_db->updateBlob(
|
|
$table,
|
|
array_merge(
|
|
$alsoupdate,
|
|
array($field => new Horde_Db_Value_Binary($data))
|
|
),
|
|
array($wherestring, $wherevalues)
|
|
);
|
|
} catch (Horde_Db_Exception $e) {
|
|
throw new Horde_Vfs_Exception($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts the path name from regular filesystem form to the internal
|
|
* format needed to access the file in the database.
|
|
*
|
|
* Namely, we will treat '/' as a base directory as this is pretty much
|
|
* the standard way to access base directories over most filesystems.
|
|
*
|
|
* @param string $path A VFS path.
|
|
*
|
|
* @return string The path with any surrouding slashes stripped off.
|
|
*/
|
|
protected function _convertPath($path)
|
|
{
|
|
return trim($path, '/');
|
|
}
|
|
|
|
/**
|
|
* TODO
|
|
*
|
|
* @return string TODO
|
|
*/
|
|
protected function _getFileSizeOp()
|
|
{
|
|
switch ($this->_db->adapterName()) {
|
|
case 'PostgreSQL':
|
|
case 'PDO_PostgreSQL':
|
|
return 'OCTET_LENGTH';
|
|
|
|
default:
|
|
return 'LENGTH';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a comparison for a possibly empty string.
|
|
*
|
|
* Returns IS NULL instead of an equals operator if the string is empty.
|
|
*
|
|
* @param string $value A string.
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function _nullString($value)
|
|
{
|
|
return strlen($value)
|
|
? array('= ?', array($value))
|
|
: array('IS NULL', array());
|
|
}
|
|
}
|