Система управления «Сайт PRO»
Версия 20240107

Класс File

Объект $File: Cms\Root\File наследует Cms\Site\Base

Объект для работы с файлами

Исходный код
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

Cms\Root\Main\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->redirect(substr($self, 0, -1) . '.html' . (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

array; null Параметры

Информация о файлах хранится в таблице 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;
    }