Класс File
Объект $File:
Объект для работы с файлами
class File extends \Cms\Site\Base { … }
Свойства
$types
$File->types = array(
'html' => array( 'type' => 'text/html' ),
'js' => array( 'type' => 'application/javascript' ),
'css' => array( 'type' => 'text/css' ),
'png' => array( 'type' => 'image/png' ),
'jpg' => array( 'type' => 'image/jpeg' ),
'jpeg' => array( 'type' => 'image/jpeg' ),
'gif' => array( 'type' => 'image/gif' ),
'svg' => array( 'type' => 'image/svg+xml' ),
'woff' => array( 'type' => 'application/font-woff' ),
'woff2' => array( 'type' => 'font/woff2' ),
'eot' => array( 'type' => 'application/vnd.ms-fontobject' ),
'ttf' => array( 'type' => 'application/font-ttf' ),
);
var $types = array( … );
$routePrefixes
$File->routePrefixes = array(
'cms/jquery',
'cms/modules',
'cms/manual',
'cms/images',
);
var $routePrefixes = array( … );
$fileFolders
$File->fileFolders = array(
'image' => array(
'title' => 'Изображения',
),
'file' => array(
'title' => 'Документы',
),
);
var $fileFolders = array( … );
Методы
route()
$File->route($Request);
Обработка HTTP-запроса
Параметры:
Имя | Описание |
---|---|
$Request |
Возвращает: true
– запрос обработан;
null
– запрос не обработан
function route($Request) {
if (substr($self = $Request->getSelf(), 0, 4) === 'cms/') {
$prefix = false;
foreach ($this->routePrefixes as $_prefix) if (substr($self, 0, strlen($_prefix)) === $_prefix) {
$prefix = $_prefix;
break;
}
if ($prefix) {
$ext = ($dot = strrpos($name = basename($self), '.')) !== false ? substr($name, $dot + 1) : false;
if (!$ext) {
if ($path = $this->App()->resolvePath($self . '/index.php')) {
print $this->Main()->Display()->includeFile($path);
} else {
$Request->setStatus(403);
}
return true;
} elseif ($ext === 'php') {
if ($path = $this->App()->resolvePath($self)) {
print $this->Main()->Display()->includeFile($path);
} else {
$Request->setStatus(403);
}
return true;
} elseif (isset($this->types[$ext])) {
if ($path = $this->App()->resolvePath($self)) {
$Request->addHeader('Content-Type: ' . $this->types[$ext]['type']);
$Request->sendFile($path);
} else {
$Request->setStatus(404);
}
return true;
}
}
}
$_self = array_shift(explode('?', $_SERVER['REQUEST_URI'], 2));
if (strpos($_self, 'index.php') !== false)
if (($self = str_replace('/index.php', '/', $_self)) !== $_self) {
$self = preg_replace('~[\\./]+/~', '/', $self);
$Request->redirect($self . (strlen($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''), 301);
return true;
}
if (($self = preg_replace('~[\\./]+/~', '/', $_self)) != $_self) {
$Request->redirect($self . (strlen($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''), 301);
return true;
}
if ($Request->default_extension !== '/' && strlen($self) > 1 && substr($self, 0, 4) !== '/cms' && substr($self, -1) === '/' && $Request->default_extension && $Request->default_extension !== '/') {
$Request->redirect(substr($self, 0, -1) . $Request->default_extension . (strlen($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''), 301);
return true;
}
if ($self === '/.html') {
$Request->redirect('/' . (strlen($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''), 301);
return true;
}
if ($self === '/content.css') {
return $this->displayContentCss($Request);
}
}
apiCallFileInfo()
$File->apiCallFileInfo($params);
function apiCallFileInfo($params) {
if (!isset($params['name'])) return;
$filename = strval($params['name']);
if (!$r = $this->getFileInfo($filename, false)) {
return;
}
$r['data'] = $this->loadFileInfoData($filename);
if ($list = $this->getFileItemList($filename)) {
$r['items'] = $list;
}
if ($list = $this->getFileOtherList($filename)) {
$r['other'] = $list;
}
if ($list = $this->getFileCloneList($filename)) {
$r['clone'] = $list;
}
return $r;
}
apiCallFileText()
$File->apiCallFileText($params);
function apiCallFileText($params) {
if (!isset($params['name'])) return;
$filename = strval($params['name']);
$data = $this->loadFileInfoData($filename);
if (isset($params['text'])) {
$data['text'] = $params['text'];
return $this->saveFileInfoData($filename, $data);
}
return $data['text'];
}
apiCallUpload()
$File->apiCallUpload($params);
function apiCallUpload($params) {
$root = $this->root();
$Main = $this->Main();
$tmpFolder = $root . '/upload/tmp';
if (!$this->createDirectory($tmpFolder)) {
throw new \Exception('Ошибка создания временной папки', 500);
}
foreach ($_FILES as $_FILE) if (is_array($_FILE)) {
if (!$_FILE['error']) {
foreach ($_FILE as $key => $value) {
$params[$key] = $value;
}
$ext = '';
if (($position = strrpos($_FILE['name'], '.')) !== false) {
$ext = mb_strtolower(substr($_FILE['name'], $position));
if ($ext === '.php') {
throw new \Exception('Запрещённое имя файла', 403);
}
}
$uuid = null;
if (isset($params['uuid']) && is_string($params['uuid']) && preg_match('~^[-\\w]+$~', $params['uuid'])) {
$uuid = $params['uuid'];
} else {
$uuid = $Main->getDefaultUuid();
}
$tmpBasename = $uuid . $ext;
$tmpFilename = $tmpFolder . '/' . $tmpBasename;
$chunk = false;
if (isset($params['chunkIndex']) && is_numeric($params['chunkIndex']) && $params['chunkIndex'] >= 0) {
$chunk = true;
$chunkIndex = intval($params['chunkIndex']);
if ($chunkIndex > 0) {
if (@is_file($tmpFilename)) {
$chunkBasename = $uuid . '.' . $chunkIndex . $ext;
$chunkFilename = $tmpFolder . '/' . $chunkBasename;
@unlink($chunkFilename);
if (@move_uploaded_file($_FILE['tmp_name'], $chunkFilename)) {
if (@filesize($tmpFilename) === intval($params['chunkSizeOffset'])) {
if ($I = @fopen($chunkFilename, 'r')) {
if ($O = @fopen($tmpFilename, 'a')) {
while ($length = strlen($buffer = @fread($I, 4096))) {
while ($offset = @fwrite($O, $buffer)) {
if ($offset < $length) {
$buffer = substr($buffer, $offset);
} else {
break;
}
}
if (!$offset) {
@fclose($O);
@fclose($I);
@unlink($chunkFilename);
@unlink($tmpFilename);
throw new \Exception('Ошибка записи файла «' . $tmpBasename . '»', 500);
}
}
@fclose($O);
} else {
@fclose($I);
@unlink($chunkFilename);
throw new \Exception('Ошибка записи в файл «' . $tmpBasename . '»', 500);
}
@fclose($I);
} else {
@unlink($chunkFilename);
throw new \Exception('Ошибка чтения файла «' . $chunkFilename . '»', 500);
}
@unlink($chunkFilename);
if ($chunkIndex === intval($params['totalChunkCount']) - 1) {
if (@filesize($tmpFilename) === intval($params['totalFileSize'])) {
$params['tmp_name'] = $tmpFilename;
$params['hash'] = $this->getFileHash($tmpFilename, true);
return $this->processUpload($params);
} else {
@unlink($tmpFilename);
throw new \Exception('Ошибка размера файла «' . $tmpBasename . '»', 500);
}
} else {
return true;
}
} else {
@unlink($tmpFilename);
throw new \Exception('Ошибка размера файла «' . $tmpBasename . '»', 500);
}
} else {
throw new \Exception('Ошибка создания файла «' . $chunkBasename . '»', 500);
}
} else {
throw new \Exception('Ошибка чтения файла «' . $tmpBasename . '»', 500);
}
}
}
@unlink($tmpFilename);
if (@move_uploaded_file($_FILE['tmp_name'], $tmpFilename)) {
@chmod($tmpFilename, 0666);
if ($chunk) return true;
$params['tmp_name'] = $tmpFilename;
$params['hash'] = $this->getFileHash($tmpFilename, true);
return $this->processUpload($params);
}
} elseif ($_FILE['error'] === UPLOAD_ERR_INI_SIZE) {
throw new \Exception('Размер файла превышает ' . ini_get('upload_max_filesize'), 400);
} elseif ($_FILE['error'] === UPLOAD_ERR_FORM_SIZE) {
throw new \Exception('Размер файла превышает допустимый', 400);
} elseif ($_FILE['error'] === UPLOAD_ERR_PARTIAL) {
throw new \Exception('Файл загружен частично', 400);
} elseif ($_FILE['error'] === UPLOAD_ERR_NO_TMP_DIR) {
throw new \Exception('Ошибка временной папки', 500);
} elseif ($_FILE['error'] === UPLOAD_ERR_CANT_WRITE) {
throw new \Exception('Ошибка записи на диск', 500);
}
}
}
apiCallFileScan()
$File->apiCallFileScan($params=null);
Сканирование загруженных файлов и страниц сайта
Параметры:
Имя | Описание |
---|---|
$params |
|
Информация о файлах хранится в таблице
file
Поля таблицы
file
:
name
size
date
hash
stamp
Также в этой таблице хранится:
- Информация о дате сканирования (
date
) и суммарном размере (size
) всех файлов:/
- Информация о дате сканирования (
date
) и суммарном размере (size
) каждой из папок с файлами:/file
,/image
, … - Информация о дате сканирования (
date
) каждой из таблиц:item
, …
Информация об использовании файлов на страницах хранится в таблице
item_file
Поля таблицы
item_file
:
id
name
stamp
Также в этой таблице хранится информация о дате анализа каждой страницы, эти записи имеют пустое значение
name
Сканирование производится следующим образом:
- Определяется, должно ли произойти полное сканирование, или достаточно быстрого сканирования
- При полном сканировании полностью актуализируется список файлов, при этом информация о дате анализа таблиц удаляется
- При полном сканировании удаляется информация о файлах, которых больше нет в файловой системе
- Анализируются страницы, отредактированные после их последнего анализа
Процесс сканирования может быть прерван в любой момент. Например, из-за превышения ограничений по памяти в процессе сканирования. В этом случае повторное сканирование проанализирует оставшиеся страницы.
function apiCallFileScan($params = null) {
$App = $this->App();
$Conf = $this->Conf();
$Data = $this->Data();
$Main = $this->Main();
$Storage = $Main->Storage();
# Период полного сканирования файлов
$interval = intval($Conf->get('file_scan_interval'));
# Период полного сканирования по умолчанию
if ($interval <= 0) $interval = 9999;
# Когда должно было случиться полное сканирование
$planStamp = date('Y-m-d H:i:s', time() - $interval);
# Реальная дата последнего полного сканирования
if (!$scanStamp = $Data->dLookup("SELECT `date` FROM `file` WHERE `name`='/'")) {
# Полное сканирование никогда не проводилось
if ($Data->error) {
# Возможно, таблицы не созданы
if ($Table = $Storage->getTable('file')) {
$Table->saveData();
}
if ($Table = $Storage->getTable('file_data')) {
$Table->saveData();
}
if ($Table = $Storage->getTable('item_file')) {
$Table->saveData();
}
}
}
$App->largeMemoryLimit();
if ($scanStamp < $planStamp) {
# Необходимо полное сканирование, потому что его давно не было
$this->saveFileList($this->scanFileList());
# Необходимо удалить записи о файлах на тех страницах, которые были удалены
$selectList = array();
foreach ($Storage->getItemTableList() as $table => $Table) {
$selectList[$table] = "SELECT `id` FROM `$table`";
}
if ($selectList) {
$Data->query("DELETE FROM `item_file` WHERE `id` NOT IN (" . implode(" UNION ", $selectList) . ")");
}
}
foreach ($Storage->getItemTableList() as $table => $Table) {
# Дата полного анализа таблицы хранится в списке файлов и удаляется при полном сканировании
$scanStamp = $Data->dLookup("SELECT `date` FROM `file` WHERE `name`={$Data->quote($table)}");
if ($Data->dLookup("SELECT MAX(`stamp`) FROM `$table`") < $scanStamp) {
# Дата последней записи в таблице раньше даты полного анализа страниц в этой таблице, поэтому её можно пропустить
continue;
}
if ($result = $Data->query("SELECT * FROM `$table`" . ($scanStamp ? " WHERE `stamp`>={$Data->quote($scanStamp)}" : ""))) {
while ($row = $Data->fetch($result)) {
if ($row['stamp'] <= $Data->dLookup("SELECT `stamp` FROM `item_file` WHERE `id`={$Data->quote($row['id'])} AND `name`=''")) {
# Страница не сохранялась после анализа файлов на ней
continue;
}
$this->updateFileListItem($Main->load($row));
}
$Data->finish($result);
# Сохраняем дату анализа таблицы
$this->saveFileInfo(array(
'name' => $table,
'size' => 0,
'hash' => '',
'date' => date('Y-m-d H:i:s'),
));
}
}
$this->removeOldFiles('/upload/tmp', false);
return true;
}
displayContentCss()
$File->displayContentCss($Request);
function displayContentCss($Request) {
$root = $this->root();
$css = @file_get_contents($root . '/style.css');
if (preg_match('~content {{{(.*?)}}} content~s', $css, $m)) {
$css = $m[1];
$css = preg_replace('~^\\s*\\*/\\s*~s', '', $css);
$css = preg_replace('~\\s*/\\*\\!?\\s*~s', '', $css);
print $css;
}
$Request->addHeader('Content-Type: text/css');
return true;
}
createDirectory()
$File->createDirectory($directoryPath, $isAbsolutePath=null);
function createDirectory($directoryPath, $isAbsolutePath = null) {
if ($isAbsolutePath === false) {
$root = $this->root();
if (@is_dir($root . DIRECTORY_SEPARATOR . $directoryPath)) return true;
$createPath = $root;
$remainPath = ltrim($directoryPath, '/' . DIRECTORY_SEPARATOR);
} else {
if (@is_dir($directoryPath)) return true;
$root = $this->root();
if (substr($directoryPath, 0, strlen($root)) === $root) {
$createPath = $root;
$remainPath = ltrim(substr($directoryPath, strlen($root)), '/' . DIRECTORY_SEPARATOR);
} elseif ($isAbsolutePath) {
$createPath = '/';
$remainPath = ltrim($directoryPath, '/' . DIRECTORY_SEPARATOR);
} else {
$createPath = $root;
$remainPath = ltrim($directoryPath, '/' . DIRECTORY_SEPARATOR);
}
}
if (DIRECTORY_SEPARATOR !== '/') $remainPath = strtr($remainPath, DIRECTORY_SEPARATOR, '/');
foreach (explode('/', $remainPath) as $path) {
$createPath .= '/' . $path;
if (!@is_dir($createPath)) if (@mkdir($createPath)) @chmod($createPath, 0777);
}
return @is_dir($directoryPath);
}
getAbsolutePath()
$File->getAbsolutePath($filename, $isAbsolutePath=null);
function getAbsolutePath($filename, $isAbsolutePath = null) {
if ($isAbsolutePath) {
return $filename;
} elseif ($isAbsolutePath === false) {
$root = $this->root();
return $root . '/' . ltrim($filename, '/');
} else {
$root = $this->root();
if (substr($filename, 0, strlen($root)) === $root) {
return $filename;
} else {
return $root . '/' . ltrim($filename, '/');
}
}
}
stripThumb()
$File->stripThumb($filename);
function stripThumb($filename) {
$dirname = dirname($filename);
$basename = basename($filename);
$parts = explode('.', $basename);
if ($parts[0] === 'thumb') {
array_shift($parts);
array_pop($parts);
array_pop($parts);
if (!empty($parts)) {
if (strpos($filename, '/') !== false) {
return $dirname . '/' . implode('.', $parts);
} else {
return implode('.', $parts);
}
}
}
return $filename;
}
stripRoot()
$File->stripRoot($filename);
function stripRoot($filename) {
$root = $this->root();
if (substr($filename, 0, strlen($root)) === $root) {
return substr($filename, strlen($root));
} else {
return $filename;
}
}
saveFileData()
$File->saveFileData($filename, $data, $isAbsolutePath=null);
function saveFileData($filename, $data, $isAbsolutePath = null) {
$filePath = $this->getAbsolutePath($filename, $isAbsolutePath);
if (!@file_exists($filePath)) $this->createDirectory(dirname($filePath), true);
if (@file_put_contents($filePath, '<' . '?php' . "\n" . 'return' . "\n" . var_export($data, true) . ';' . "\n")) {
@chmod($filePath, 0666);
return true;
}
return false;
}
processUpload()
$File->processUpload(&$params);
function processUpload(&$params) {
$filename = $this->getUploadFilename($params);
$filePath = $this->getAbsolutePath($filename, false);
$suffix = null;
$ext = $dirname = $basename = '';
while (@file_exists($filePath)) {
if ($this->getFileHash($filePath, true) === $params['hash']) {
return $this->getFileInfo($filePath, true);
}
if ($suffix === null) {
$ext = '';
$dirname = dirname($filename);
$basename = basename($filename);
if (($pos = strrpos($basename, '.')) !== false) {
$ext = substr($basename, $pos);
$basename = substr($basename, 0, $pos);
}
$suffix = -2;
$filename = $dirname . '/' . $basename . $suffix . $ext;
$filePath = $this->getAbsolutePath($filename, false);
} else {
$suffix --;
if ($suffix <= -100) {
$suffix = '-' . uniqid();
} else {
$filename = $dirname . '/' . $basename . $suffix . $ext;
$filePath = $this->getAbsolutePath($filename, false);
}
}
}
$this->createDirectory(dirname($filePath), true);
if (@rename($params['tmp_name'], $filePath)) {
@chmod($filePath, 0666);
} elseif (@copy($params['tmp_name'], $filePath)) {
@unlink($params['tmp_name']);
@chmod($filePath, 0666);
} else {
return false;
}
$params['filename'] = $filename;
if ($this->Conf()->get('image-size')) {
$this->App()->get('thumb')->truncate($filePath);
}
$this->removeOldFiles('/upload/tmp', false);
return $this->getFileInfo($filePath, true);
}
removeOldFiles()
$File->removeOldFiles($directoryName, $isAbsolutePath=null, $expire=99999);
function removeOldFiles($directoryName, $isAbsolutePath = null, $expire = 99999) {
$directoryPath = $this->getAbsolutePath($directoryName, $isAbsolutePath);
if ($D = @opendir($directoryPath)) {
$time = time() - $expire;
while (strval($name = @readdir($D)) !== '') {
if (is_string($name) && $name[0] === '.') continue;
$filename = $directoryPath . '/' . $name;
if (@filemtime($filename) < $time) {
@unlink($filename);
}
}
closedir($D);
}
}
getUploadFilename()
$File->getUploadFilename($params);
function getUploadFilename($params) {
$Auth = $this->Auth();
$folders = $this->getFileFolders();
$folder = null;
if (isset($params['folder']) && is_string($params['folder'])) {
if (isset($folders[$params['folder']])) {
$folder = $params['folder'];
}
}
if ($folder === null) {
if (isset($params['type']) && is_string($params['type'])) {
$typePrefix = substr($params['type'], 0, 5);
if ($typePrefix === 'image' || $typePrefix === 'video') {
if (isset($folders['image'])) {
$folder = 'image';
}
} else {
if (isset($folders['file'])) {
$folder = 'file';
}
}
}
if ($folder === null) {
foreach ($folders as $folder => $folderInfo) {
break;
}
if ($folder === null) {
$folder = 'upload';
}
}
}
$folder = '/' . trim($folder, '/');
if (!$Auth->admin && $Auth->login) {
$folder .= '/' . trim($Auth->login, '/');
}
$folder .= date('/Y/m');
if (isset($params['parent']) && is_string($params['parent'])) {
if (preg_match('~([-\\w]+)[^-\\w]*$~', $params['parent'], $m)) {
$folder .= '/' . $m[1];
} else {
$folder .= '/upload';
}
} else {
$folder .= '/upload';
}
$name = '';
if (isset($params['name']) && is_string($params['name'])) {
$name = $params['name'];
$name = strtr($name, '\\', '/');
$name = basename($name);
$name = preg_replace('~[%&"?:*<>\\|//\\x{00}-\\x{1f}]+~', '_', $name);
}
if ($name === '') {
if (isset($params['tmp_name']) && is_string($params['tmp_name'])) {
$name = basename($params['tmp_name']);
}
}
if (substr($name, -4) === '.php') {
$name = substr($name, 0, strlen($name) - 4) . '.txt';
}
if ($name === '') {
$name = uniqid('');
}
return $folder . '/' . $name;
}
getFileInfo()
$File->getFileInfo($filename, $isAbsolutePath=null);
function getFileInfo($filename, $isAbsolutePath = null) {
$filePath = $this->getAbsolutePath($filename, $isAbsolutePath);
$r = array(
'name' => $this->stripRoot($filename),
);
if ($stat = @stat($filePath)) {
$r['size'] = $stat['size'];
$r['date'] = date('Y-m-d H:i:s', $stat['mtime']);
$r['hash'] = @hash_file('sha256', $filePath);
}
return $r;
}
saveFileInfo()
$File->saveFileInfo($fileInfo);
function saveFileInfo($fileInfo) {
if (is_string($fileInfo)) {
$fileInfo = $this->getFileInfo($fileInfo);
}
if (!is_array($fileInfo) || count($fileInfo) <= 1) {
return false;
}
$Data = $this->Data();
$qStamp = $Data->quote(date('Y-m-d H:i:s'));
$qName = $Data->quote($fileInfo['name']);
$qSize = $Data->quote($fileInfo['size']);
$qDate = $Data->quote($fileInfo['date']);
$qHash = $Data->quote($fileInfo['hash']);
if ($fileInfo['name'] === $Data->dLookup("SELECT `name` FROM `file` WHERE `name`=$qName")) {
if ($Data->query("UPDATE `file` SET `date`=$qDate, `size`=$qSize, `date`=$qDate, `hash`=$qHash, `stamp`=$qStamp WHERE `name`=$qName")) {
return true;
} else {
return false;
}
} else {
if ($Data->query("INSERT INTO `file` (`name`, `size`, `date`, `hash`, `stamp`) VALUES ($qName, $qSize, $qDate, $qHash, $qStamp)")) {
return true;
} else {
return false;
}
}
}
getFileHash()
$File->getFileHash($filename, $isAbsolutePath=null);
function getFileHash($filename, $isAbsolutePath = null) {
$filePath = $this->getAbsolutePath($filename, $isAbsolutePath);
return @hash_file('sha256', $filePath);
}
getFileFolders()
$File->getFileFolders();
function getFileFolders() {
static $fileFolders;
if (!isset($fileFolders)) {
$fileFolders = array();
$confFileFolders = $this->Conf()->get('file_folders');
if (is_array($confFileFolders)) {
foreach ($confFileFolders as $name => $folderInfo) {
if (is_array($folderInfo)) {
$fileFolders[$name] = $folderInfo;
} elseif (is_string($folderInfo) && $folderInfo !== '') {
$fileFolders[$name] = array( 'title' => $folderInfo );
} else {
$fileFolders[$name] = array();
}
}
}
foreach ($this->fileFolders as $name => $folderInfo) {
if (!isset($fileFolders[$name])) {
if (is_array($folderInfo)) {
$fileFolders[$name] = $folderInfo;
} else {
$fileFolders[$name] = array();
}
} else {
if (is_array($folderInfo)) {
foreach ($folderInfo as $k => $v) {
if (!isset($fileFolders[$name][$k])) {
$fileFolders[$name][$k] = $v;
}
}
}
}
}
foreach ($fileFolders as $name => &$folderInfo) {
if (!isset($folderInfo['name'])) {
$folderInfo['name'] = '/' . ltrim($name, '/');
}
if (!isset($folderInfo['prefix'])) {
$folderInfo['prefix'] = $folderInfo['name'] === '/' ? '/' : rtrim($folderInfo['name'], '/') . '/';
}
if (!isset($folderInfo['prefixLength'])) {
$folderInfo['prefixLength'] = strlen($folderInfo['prefix']);
}
}
}
return $fileFolders;
}
getFileListItem()
$File->getFileListItem($Page);
function getFileListItem($Page) {
$root = $this->root();
$Page->open();
$fileKeys = array();
foreach ($Page as $key => $value) {
if (!is_string($key) || $key === 'id' || $key === 'parent' || $key === '' || $key[0] === '_') {
# Эти ключи точно не содержат файлов
continue;
}
if ($value === null || $value === '' || is_int($value) || is_float($value) || is_bool($value)) {
# Эти значения точно не содержат файлов
continue;
}
if (is_string($value)) {
if ($value[0] === '/' && strpos($value, '.') !== false) {
# Похоже на файл
if (strpos($value, '|') !== false) {
# Похоже на набор файлов
$fileKeys += $this->getFileKeysArray(explode('|', $value));
} elseif (strpos($value, '/thumb.') !== false) {
# Похоже на миниатюру файла
$fileKeys[$this->stripThumb($value)] = null;
} else {
# Похоже на файл
$fileKeys[$value] = null;
}
} elseif ($value[0] === '<' && $value[strlen($value) - 1] === '>') {
# Похоже на HTML
$fileKeys += $this->getFileKeysHtml($value);
} elseif ($value[0] === '{' && $value[strlen($value) - 1] === '}') {
# Похоже на JSON
$fileKeys += $this->getFileKeysArray(json_decode($value, true));
} elseif ($value[0] === '[' && $value[strlen($value) - 1] === ']') {
# Похоже на JSON
$fileKeys += $this->getFileKeysArray(json_decode($value, true));
} elseif ($value[0] === 'a' && $value[1] === ':' && $value[strlen($value) - 1] === '}') {
# Похоже на массив
$fileKeys += $this->getFileKeysArray(unserialize($value));
} elseif (strpos($value, '//') !== false || strpos($value, '?') !== false) {
# Похоже на URL
$fileKeys += $this->getFileKeysUrl($value);
}
} elseif (is_array($value)) {
# Массив
$fileKeys += $this->getFileKeysArray($value);
}
}
$r = array();
if (!empty($fileKeys)) {
$folders = $this->getFileFolders();
foreach ($fileKeys as $filename => $fileValue) {
foreach ($folders as $folderInfo) {
if (substr($filename, 0, $folderInfo['prefixLength']) === $folderInfo['prefix']) {
$fileValue = true;
$r[] = $filename;
break;
}
}
if (!$fileValue) {
if ($filename[strlen($filename) - 1] !== '.' && substr($filename, -4) !== '.php' && substr($filename, -5) !== '.html') {
if (@is_file($root . $filename)) {
$r[] = $filename;
}
}
}
}
}
return $r;
}
saveFileListItem()
$File->saveFileListItem($Page, $fileList);
function saveFileListItem($Page, $fileList) {
if (!$Page->id) return true;
$Data = $this->Data();
$qId = $Data->quote($Page->id);
$qStamp = $Data->quote($Page->stamp ? $Page->stamp : date('Y-m-d H:i:s'));
$Data->query("DELETE FROM `item_file` WHERE `id`=$qId");
foreach ($fileList as $filename) {
$qName = $Data->quote($filename);
$Data->query("INSERT INTO `item_file` (`id`, `name`, `stamp`) VALUES ($qId, $qName, $qStamp)");
}
if ($Data->query("INSERT INTO `item_file` (`id`, `name`, `stamp`) VALUES ($qId, '', $qStamp)")) {
return true;
}
}
updateFileListItem()
$File->updateFileListItem($Page);
function updateFileListItem($Page) {
return $this->saveFileListItem($Page, $this->getFileListItem($Page));
}
getFileKeysArray()
$File->getFileKeysArray($array);
function getFileKeysArray($array) {
$fileKeys = array();
array_walk_recursive($array, function($value) use (&$fileKeys) {
if (is_string($value) && $value !== '') {
if ($value[0] === '/' && strpos($value, '.') !== false) {
# Похоже на файл
if (strpos($value, '|') !== false) {
# Похоже на набор файлов
$fileKeys += $this->getFileKeysArray(explode('|', $value));
} elseif (strpos($value, '/thumb.') !== false) {
# Похоже на миниатюру файла
$fileKeys[$this->stripThumb($value)] = null;
} else {
# Похоже на файл
$fileKeys[$value] = null;
}
} elseif ($value[0] === '<' && $value[strlen($value) - 1] === '>') {
# Похоже на HTML
$fileKeys += $this->getFileKeysHtml($value);
} elseif ($value[0] === '{' && $value[strlen($value) - 1] === '}') {
# Похоже на JSON
$fileKeys += $this->getFileKeysArray(json_decode($value, true));
} elseif ($value[0] === '[' && $value[strlen($value) - 1] === ']') {
# Похоже на JSON
$fileKeys += $this->getFileKeysArray(json_decode($value, true));
} elseif ($value[0] === 'a' && $value[1] === ':' && $value[strlen($value) - 1] === '}') {
# Похоже на массив
$fileKeys += $this->getFileKeysArray(unserialize($value));
} elseif (strpos($value, '//') !== false || strpos($value, '?') !== false) {
# Похоже на URL
$fileKeys += $this->getFileKeysUrl($value);
}
}
});
return $fileKeys;
}
getFileKeysHtml()
$File->getFileKeysHtml($html);
function getFileKeysHtml($html) {
$fileKeys = array();
foreach (array('src=', 'href=', 'srcset=') as $attribute) {
if (strpos($html, $attribute) !== false) {
foreach (explode($attribute, $html) as $i => $value) {
if (!$i) continue;
$q = $value[0];
if ($q === '"' || $q === "'") {
if (($p = strpos($value, $q, 1)) !== false) {
$value = substr($value, 1, $p - 1);
if (strpos($value, '//') !== false || strpos($value, '?') !== false) {
# Похоже на URL
$fileKeys += $this->getFileKeysUrl($value);
} elseif ($value[0] === '/' && strpos($value, '.') !== false) {
# Похоже на файл
if (strpos($value, '/thumb.') !== false) {
# Похоже на миниатюру файла
$fileKeys[$this->stripThumb($value)] = null;
} else {
# Похоже на файл
$fileKeys[$value] = null;
}
}
}
}
}
}
}
return $fileKeys;
}
getFileKeysUrl()
$File->getFileKeysUrl($url);
function getFileKeysUrl($url) {
$fileKeys = array();
if ($_SERVER['HTTP_HOST']) {
if ($info = parse_url($url)) {
if ($info['host'] === $_SERVER['HTTP_HOST']) {
$fileKeys[rawurldecode($info['path'])] = null;
}
}
}
return $fileKeys;
}
scanFileList()
$File->scanFileList();
function scanFileList() {
$fileList = array();
$root = $this->root();
$rootSize = 0;
foreach ($this->getFileFolders() as $folderInfo) {
$folderSize = 0;
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($root . $folderInfo['name'], \RecursiveDirectoryIterator::SKIP_DOTS)) as $fileInfo) {
if ($fileInfo->isDir()) {
continue;
}
$filename = $filePath = $fileInfo->getPathName();
if (strpos($filename, '/thumb.') !== false) continue;
if (substr($filename, 0, strlen($root)) === $root) {
$filename = substr($filename, strlen($root));
}
$fileSize = $fileInfo->getSize();
$fileList[$filename] = array(
'name' => $filename,
'size' => $fileSize,
'date' => date('Y-m-d H:i:s', $fileInfo->getMTime()),
'hash' => $this->getFileHash($filePath, true),
);
$folderSize += $fileSize;
}
$fileList[$folderInfo['name']] = array(
'name' => $folderInfo['name'],
'size' => $folderSize,
'date' => date('Y-m-d H:i:s'),
'hash' => '',
);
$rootSize += $folderSize;
}
$fileList['/'] = array(
'name' => '/',
'size' => $rootSize,
'date' => date('Y-m-d H:i:s'),
'hash' => '',
);
return $fileList;
}
saveFileList()
$File->saveFileList($fileList);
function saveFileList($fileList) {
$Data = $this->Data();
$qStamp = $Data->quote(date('Y-m-d H:i:s'));
$Data->beginTransaction();
$Data->query("DELETE FROM `file`");
foreach ($fileList as $filename => $fileInfo) {
$qName = $Data->quote($fileInfo['name']);
$qSize = $Data->quote($fileInfo['size']);
$qDate = $Data->quote($fileInfo['date']);
$qHash = $Data->quote($fileInfo['hash']);
$Data->query("INSERT INTO `file` (`name`, `size`, `date`, `hash`, `stamp`) VALUES ($qName, $qSize, $qDate, $qHash, $qStamp)");
}
$Data->commit();
return true;
}
getFileItemList()
$File->getFileItemList($filename);
function getFileItemList($filename) {
$Data = $this->Data();
$Main = $this->Main();
$r = array();
foreach ($Data->getRows("SELECT DISTINCT `id` FROM `item_file` WHERE `name`={$Data->quote($filename)} ORDER BY `stamp` DESC LIMIT 100") as $row) {
$id = $row['id'];
$title = $Main->getTitle($id);
if ($id && $title) {
$href = $Main->href($id);
$r[] = array(
'id' => $id,
'href' => $href,
'title' => $title,
);
}
}
return $r;
}
getFileOtherList()
$File->getFileOtherList($filename);
function getFileOtherList($filename) {
$Data = $this->Data();
$r = array();
if (($pos = strrpos($filename, '.')) !== false) {
$ext = substr($filename, $pos);
foreach ($Data->getRows("SELECT `name` FROM `file` WHERE `name`<>{$Data->quote($filename)} AND `name` LIKE '%{$Data->qLike($ext)}' AND `hash`<>'' ORDER BY `date` DESC LIMIT 12") as $row) {
$r[] = array(
'name' => $row['name'],
);
}
}
if (!$r) {
foreach ($Data->getRows("SELECT `name` FROM `file` WHERE `name`<>{$Data->quote($filename)} AND `hash`<>'' ORDER BY `date` DESC LIMIT 12") as $row) {
$r[] = array(
'name' => $row['name'],
);
}
}
return $r;
}
getFileCloneList()
$File->getFileCloneList($filename);
function getFileCloneList($filename) {
$Data = $this->Data();
$r = array();
$filePath = $this->getAbsolutePath($filename, false);
if (@is_file($filePath)) {
$size = @filesize($filePath);
$hash = $this->getFileHash($filePath, true);
if ($size && $hash) {
foreach ($Data->getRows("SELECT `name` FROM `file` WHERE `name`<>{$Data->quote($filename)} AND `size`={$Data->quote($size)} AND `hash`={$Data->quote($hash)} ORDER BY `date` DESC LIMIT 100") as $row) {
$r[] = array(
'name' => $row['name'],
);
}
}
}
return $r;
}
loadFileInfoData()
$File->loadFileInfoData($filename);
function loadFileInfoData($filename) {
$Data = $this->Data();
if ($data = $Data->dLookup("SELECT `data` FROM `file_data` WHERE `name`={$Data->quote($filename)}")) {
$data = json_decode($data, true);
}
if (!is_array($data)) $data = array();
return $data;
}
saveFileInfoData()
$File->saveFileInfoData($filename, $data);
function saveFileInfoData($filename, $data) {
if (is_array($data)) {
foreach ($data as $key => $value) {
if ($value === null || $value === '') {
unset($data[$key]);
}
}
$data = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
$Data = $this->Data();
$r = $Data->query("DELETE FROM `file_data` WHERE `name`={$Data->quote($filename)}");
if ($data) {
$r = $Data->query("INSERT INTO `file_data` (`name`, `data`) VALUES ({$Data->quote($filename)}, {$Data->quote($data)})");
}
return $r ? true : false;
}