mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2024-11-16 13:07:14 +01:00
447 lines
10 KiB
PHP
447 lines
10 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Xentral\Components\Filesystem;
|
||
|
|
||
|
use Xentral\Components\Database\Database;
|
||
|
use Xentral\Components\Filesystem\Exception\FileNotFoundException;
|
||
|
use Xentral\Components\Filesystem\Exception\FilesystemException;
|
||
|
use Xentral\Components\Filesystem\Exception\InvalidArgumentException;
|
||
|
|
||
|
/**
|
||
|
* @todo Datenbank anlegen
|
||
|
*/
|
||
|
final class FilesystemSyncCache implements FilesystemInterface
|
||
|
{
|
||
|
/** @var Database $db */
|
||
|
private $db;
|
||
|
|
||
|
/** @var FilesystemInterface $fs */
|
||
|
private $fs;
|
||
|
|
||
|
/** @var int $syncId */
|
||
|
private $syncId;
|
||
|
|
||
|
/**
|
||
|
* @param Database $database
|
||
|
* @param FilesystemInterface $filesystem
|
||
|
* @param int $syncId
|
||
|
*/
|
||
|
public function __construct(Database $database, FilesystemInterface $filesystem, $syncId)
|
||
|
{
|
||
|
if ((int)$syncId <= 0) {
|
||
|
throw new InvalidArgumentException(sprintf('Sync-ID "%s" is invalid.', $syncId));
|
||
|
}
|
||
|
|
||
|
$this->db = $database;
|
||
|
$this->fs = $filesystem;
|
||
|
$this->syncId = (int)$syncId;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $directory
|
||
|
* @param bool $recursive
|
||
|
*
|
||
|
* @return array|PathInfo[]
|
||
|
*/
|
||
|
public function listChanges($directory = '', $recursive = false)
|
||
|
{
|
||
|
$result = $this->fs->listContents($directory, $recursive);
|
||
|
|
||
|
$location = PathUtil::normalizePath($directory);
|
||
|
$cache = $this->readCache($location, $recursive);
|
||
|
|
||
|
foreach ($result as $item) {
|
||
|
$path = $item->getPath();
|
||
|
|
||
|
// Ignore directories; only files are nessesary for syncing
|
||
|
if ($item->isDir()) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Defaults
|
||
|
$item->set('missing', false);
|
||
|
$item->set('modified', null);
|
||
|
|
||
|
if (!array_key_exists($path, $cache)) {
|
||
|
$item->set('missing', true);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((int)$cache[$path]['size'] !== (int)$item->getSize()) {
|
||
|
$item->set('modified', true);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ((int)$cache[$path]['timestamp'] !== (int)$item->getTimestamp()) {
|
||
|
$item->set('modified', true);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$item->set('modified', false);
|
||
|
}
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Lists deleted files
|
||
|
*
|
||
|
* Lists files that are present in cache but does not exist on the filesystem any more.
|
||
|
*
|
||
|
* @param string $directory
|
||
|
* @param bool $recursive
|
||
|
*
|
||
|
* @return array|PathInfo[]
|
||
|
*/
|
||
|
public function listDeleted($directory = '', $recursive = false)
|
||
|
{
|
||
|
$paths = $this->fs->listPaths($directory, $recursive);
|
||
|
|
||
|
$location = PathUtil::normalizePath($directory);
|
||
|
$cache = $this->readCache($location, $recursive);
|
||
|
|
||
|
foreach ($cache as $path => $cacheItem) {
|
||
|
if (in_array($path, $paths, true)) {
|
||
|
unset($cache[$path]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$result = [];
|
||
|
foreach ($cache as $cacheItem) {
|
||
|
$pathinfo = PathUtil::pathinfo($cacheItem['path']);
|
||
|
$result[] = new PathInfo(array_merge($cacheItem, $pathinfo));
|
||
|
}
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function has($path)
|
||
|
{
|
||
|
return $this->fs->has($path);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function getInfo($path)
|
||
|
{
|
||
|
return $this->fs->getInfo($path);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function getType($path)
|
||
|
{
|
||
|
return $this->fs->getType($path);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function getSize($path)
|
||
|
{
|
||
|
return $this->fs->getSize($path);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function getTimestamp($path)
|
||
|
{
|
||
|
return $this->fs->getTimestamp($path);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function getMimetype($path)
|
||
|
{
|
||
|
return $this->fs->getMimetype($path);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function listContents($directory = '', $recursive = false)
|
||
|
{
|
||
|
return $this->fs->listContents($directory, $recursive);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function listDirs($directory = '', $recursive = false)
|
||
|
{
|
||
|
return $this->fs->listDirs($directory, $recursive);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function listFiles($directory = '', $recursive = false)
|
||
|
{
|
||
|
return $this->fs->listFiles($directory, $recursive);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function listPaths($directory = '', $recursive = false)
|
||
|
{
|
||
|
return $this->fs->listPaths($directory, $recursive);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function read($path)
|
||
|
{
|
||
|
$result = $this->fs->read($path);
|
||
|
$this->updateCachePath($path);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function readStream($path)
|
||
|
{
|
||
|
$result = $this->fs->readStream($path);
|
||
|
$this->updateCachePath($path);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function write($path, $contents, array $config = [])
|
||
|
{
|
||
|
$result = $this->fs->write($path, $contents, $config);
|
||
|
$this->updateCachePath($path);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function writeStream($path, $resource, array $config = [])
|
||
|
{
|
||
|
$result = $this->fs->writeStream($path, $resource, $config);
|
||
|
$this->updateCachePath($path);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function put($path, $contents, array $config = [])
|
||
|
{
|
||
|
$result = $this->fs->put($path, $contents, $config);
|
||
|
$this->updateCachePath($path);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function putStream($path, $resource, array $config = [])
|
||
|
{
|
||
|
$result = $this->fs->putStream($path, $resource, $config);
|
||
|
$this->updateCachePath($path);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function delete($path)
|
||
|
{
|
||
|
$result = $this->fs->delete($path);
|
||
|
$this->deleteCachePath($path);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Deletes a single file, but without Exception if path does not exist.
|
||
|
*
|
||
|
* @param string $path
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function softDelete($path)
|
||
|
{
|
||
|
$result = false;
|
||
|
try {
|
||
|
$this->deleteCachePath($path);
|
||
|
$result = $this->fs->delete($path);
|
||
|
} catch (FileNotFoundException $e) {
|
||
|
// nope - its soft
|
||
|
}
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function deleteDir($dirname)
|
||
|
{
|
||
|
$result = $this->fs->deleteDir($dirname);
|
||
|
$this->deleteCacheDir($dirname);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function createDir($dirname, array $config = [])
|
||
|
{
|
||
|
return $this->fs->createDir($dirname, $config);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function rename($path, $newpath)
|
||
|
{
|
||
|
$result = $this->fs->rename($path, $newpath);
|
||
|
$this->deleteCachePath($path);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function copy($path, $newpath)
|
||
|
{
|
||
|
$result = $this->fs->copy($path, $newpath);
|
||
|
$this->updateCachePath($newpath);
|
||
|
|
||
|
return $result;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @inheritdoc
|
||
|
*/
|
||
|
public function getAdapter()
|
||
|
{
|
||
|
return $this->fs->getAdapter();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $path
|
||
|
* @param bool $recursive
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private function readCache($path, $recursive = false)
|
||
|
{
|
||
|
$this->ensureDependencies();
|
||
|
$path = $this->normalizePath($path);
|
||
|
|
||
|
return $this->db->fetchAssoc(
|
||
|
'SELECT f.path, f.dirname, f.type, f.size, f.updated_at AS timestamp ' .
|
||
|
'FROM sync_files AS f ' .
|
||
|
'WHERE f.sync_id = :sync_id AND f.dirname LIKE :path_prefix',
|
||
|
[
|
||
|
'sync_id' => $this->syncId,
|
||
|
'path_prefix' => $recursive === true ? $path . '%' : $path,
|
||
|
]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $path
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
private function updateCachePath($path)
|
||
|
{
|
||
|
$this->ensureDependencies();
|
||
|
$info = $this->fs->getInfo($path);
|
||
|
|
||
|
$this->db->perform(
|
||
|
'REPLACE INTO sync_files (sync_id, `path`, dirname, type, size, updated_at) ' .
|
||
|
'VALUES (:sync_id, :path, :dirname, :type, :size, :updated_at)',
|
||
|
[
|
||
|
'sync_id' => $this->syncId,
|
||
|
'path' => $info->getPath(),
|
||
|
'dirname' => $info->getDir(),
|
||
|
'type' => $info->getType(),
|
||
|
'size' => (int)$info->getSize(),
|
||
|
'updated_at' => (int)$info->getTimestamp(),
|
||
|
]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $path
|
||
|
*
|
||
|
* @return int Deleted row count
|
||
|
*/
|
||
|
private function deleteCachePath($path)
|
||
|
{
|
||
|
$this->ensureDependencies();
|
||
|
$path = $this->normalizePath($path);
|
||
|
|
||
|
return $this->db->fetchAffected(
|
||
|
'DELETE FROM sync_files WHERE sync_id = :sync_id AND `path` = :path',
|
||
|
['sync_id' => $this->syncId, 'path' => $path]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $dirname
|
||
|
*
|
||
|
* @return int Deleted row count
|
||
|
*/
|
||
|
private function deleteCacheDir($dirname)
|
||
|
{
|
||
|
$this->ensureDependencies();
|
||
|
$dirname = $this->normalizePath($dirname);
|
||
|
|
||
|
return $this->db->fetchAffected(
|
||
|
'DELETE FROM sync_files WHERE sync_id = :sync_id AND `dirname` LIKE :dirname',
|
||
|
['sync_id' => $this->syncId, 'dirname' => $dirname . '%']
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $path
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
private function normalizePath($path)
|
||
|
{
|
||
|
return PathUtil::normalizePath($path);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @throws FilesystemException
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
private function ensureDependencies()
|
||
|
{
|
||
|
if ($this->db === null || $this->syncId === null) {
|
||
|
throw new FilesystemException('Can not continue. Required dependencies are missing.');
|
||
|
}
|
||
|
}
|
||
|
}
|