Битрикс. Работа с файловой системой

02.11.2018

Теги: CMSWeb-разработкаБитриксДиректорияКлассКопироватьНовоеЯдроУдалитьФайл

За работу с файловой системой в новом ядре отвечают классы пространства имен Bitrix\Main\IOIO\Directory, IO\File и IO\Path.

use Bitrix\Main\IO,
    Bitrix\Main\Application;

Класс IO\File

$file = new IO\File(Application::getDocumentRoot().'/file.txt');

Информация о файле:

$isExist = $file->isExists(); // true, если файл существует

$dir = $file->getDirectory();     // директория файла как объект IO\Directory
$dir = $file->getDirectoryName(); // директория файла в виде текста

$fileName = $file->getName();           // имя файла
$fileExt = $file->getExtension();       // расширение файла
$fileSize = $file->getSize();           // размер файла в байтах
$contentType = $file->getContentType(); // тип файла, Content-type

$createdAt = $file->getCreationTime();      // дата создания, timestamp
$accessAt = $file->getLastAccessTime();     // дата последнего доступа, timestamp
$modifiedAt = $file->getModificationTime(); // дата модификации, timestamp

$perms = $file->getPermissions(); // права на файл в виде десятичного числа
$perms = substr(sprintf('%o', $file->getPermissions()), -3); // права на файл в виде восьмеричного числа

Действия над файлами:

$content = $file->getContents();    // получить содержание файла
$file->putContents('some content'); // записать содержимое в файл с заменой
$file->putContents('other content', IO\File::APPEND); // дописать содержимое в конец файла
$file->readFile();  // вывести содержимое файла

$file->rename(Application::getDocumentRoot().'/new-file.txt'); // переместить/переименовать файл
$file->delete();  // удалить файл

У некоторых методов есть статические варианты:

$path = Application::getDocumentRoot().'/file.txt';
IO\File::isFileExists($path); // проверить существование файла

IO\File::getFileContents($path); // получить содержание файла
IO\File::putFileContents($path, 'some content'); // записать содержимое в файл с заменой
IO\File::putFileContents($path, 'other content', self::APPEND); // дописать содержимое в конец файла

IO\File::deleteFile($path); // удалить файл

Класс IO\Directory

$dir = new IO\Directory(Application::getDocumentRoot().'/test/');

Если директории не существует, её можно создать:

$dir->create(); // создаёт директорию с указанным в конструкторе путём

Информация о директории:

$isExist = $dir->isExists(); // true, если директория существует

$createdAt = $dir->getCreationTime();      // дата создания, timestamp
$accessAt = $dir->getLastAccessTime();     // дата последнего доступа, timestamp
$modifiedAt = $dir->getModificationTime(); // дата модификации, timestamp

$perms = $dir->getPermissions(); // права на директорию в виде десятичного числа
$perms = substr(sprintf('%o', $dir->getPermissions()), -3); // права на директорию в виде восьмеричного числа

Действия над директориями:

$childDir = $dir->createSubdirectory('child'); // создает и возвращает вложенную директорию с указанным именем 
$dir->rename(Application::getDocumentRoot().'/other-path/'); // переместить/переименовать директорию
$dir->delete(); // удалить директорию

Получить массив файлов в директории:

$files = $dir->getChildren(); // массив объектов IO\File

У некоторых методов есть статические варианты:

$path = Application::getDocumentRoot().'/some-dir/';
IO\Directory::createDirectory($path);   // создать директорию
IO\Directory::deleteDirectory($path);   // удалить директорию
IO\Directory::isDirectoryExists($path); // проверить существование

Класс IO\Path

$path = Application::getDocumentRoot().'/some-dir/some-file.ext';
$fileName = IO\Path::getName($path);     // возвращает имя файла
$fileDir = IO\Path::getDirectory($path); // возвращает директорию файла (полный путь)
$fileExt = IO\Path::getExtension($path); // возвращает расширение файла

Исходные коды

<?php
/*
 * Файл bitrix/modules/main/lib/io/file.php
 */

namespace Bitrix\Main\IO;

class File extends FileEntry implements IFileStream
{
    const REWRITE = 0;
    const APPEND = 1;

    /** @var resource */
    protected $filePointer;

    public function __construct($path, $siteId = null)
    {
        parent::__construct($path, $siteId);
    }

    /**
     * Opens the file and returns the file pointer.
     *
     * @param string $mode
     * @return resource
     * @throws FileOpenException
     */
    public function open($mode)
    {
        $this->filePointer = fopen($this->getPhysicalPath(), $mode."b");
        if (!$this->filePointer)
        {
            throw new FileOpenException($this->originalPath);
        }
        return $this->filePointer;
    }

    /**
     * Closes the file.
     *
     * @throws FileNotOpenedException
     */
    public function close()
    {
        if(!$this->filePointer)
        {
            throw new FileNotOpenedException($this->originalPath);
        }
        fclose($this->filePointer);
        $this->filePointer = null;
    }

    public function isExists()
    {
        $p = $this->getPhysicalPath();
        return file_exists($p) && (is_file($p) || is_link($p));
    }

    public function getContents()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        return file_get_contents($this->getPhysicalPath());
    }

    public function putContents($data, $flags = self::REWRITE)
    {
        $dir = $this->getDirectory();
        if (!$dir->isExists())
            $dir->create();

        if ($this->isExists() && !$this->isWritable())
            $this->markWritable();

        return $flags & self::APPEND
            ? file_put_contents($this->getPhysicalPath(), $data, FILE_APPEND)
            : file_put_contents($this->getPhysicalPath(), $data);
    }

    /**
     * Returns the file size.
     *
     * @return float|int
     * @throws FileNotFoundException
     * @throws FileOpenException
     */
    public function getSize()
    {
        if (!$this->isExists())
        {
            throw new FileNotFoundException($this->originalPath);
        }

        static $supportLarge32 = null;
        if($supportLarge32 === null)
        {
            $supportLarge32 = (\Bitrix\Main\Config\Configuration::getValue("large_files_32bit_support") === true);
        }

        $size = 0;
        if(PHP_INT_SIZE < 8 && $supportLarge32)
        {
            // 32bit
            $this->open(FileStreamOpenMode::READ);

            if(fseek($this->filePointer, 0, SEEK_END) === 0)
            {
                $size = 0.0;
                $step = 0x7FFFFFFF;
                while($step > 0)
                {
                    if (fseek($this->filePointer, -$step, SEEK_CUR) === 0)
                    {
                        $size += floatval($step);
                    }
                    else
                    {
                        $step >>= 1;
                    }
                }
            }

            $this->close();
        }
        else
        {
            // 64bit
            $size = filesize($this->getPhysicalPath());
        }

        return $size;
    }

    /**
     * Seeks on the file pointer from the beginning (SEEK_SET only).
     *
     * @param int|float $position
     * @return int
     * @throws FileNotOpenedException
     */
    public function seek($position)
    {
        if(!$this->filePointer)
        {
            throw new FileNotOpenedException($this->originalPath);
        }

        if($position <= PHP_INT_MAX)
        {
            return fseek($this->filePointer, $position, SEEK_SET);
        }
        else
        {
            $res = fseek($this->filePointer, 0, SEEK_SET);
            if($res === 0)
            {
                do
                {
                    $offset = ($position < PHP_INT_MAX? $position : PHP_INT_MAX);
                    $res = fseek($this->filePointer, $offset, SEEK_CUR);
                    if($res !== 0)
                    {
                        break;
                    }
                    $position -= PHP_INT_MAX;
                }
                while($position > 0);
            }
            return $res;
        }
    }

    public function isWritable()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        return is_writable($this->getPhysicalPath());
    }

    public function isReadable()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        return is_readable($this->getPhysicalPath());
    }

    public function readFile()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        return readfile($this->getPhysicalPath());
    }

    public function getCreationTime()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        return filectime($this->getPhysicalPath());
    }

    public function getLastAccessTime()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        return fileatime($this->getPhysicalPath());
    }

    public function getModificationTime()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        return filemtime($this->getPhysicalPath());
    }

    public function markWritable()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        @chmod($this->getPhysicalPath(), BX_FILE_PERMISSIONS);
    }

    public function getPermissions()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        return fileperms($this->getPhysicalPath());
    }

    public function delete()
    {
        if ($this->isExists())
            return unlink($this->getPhysicalPath());

        return true;
    }

    public function getContentType()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        $finfo = \finfo_open(FILEINFO_MIME_TYPE);
        $contentType = \finfo_file($finfo, $this->getPath());
        \finfo_close($finfo);

        return $contentType;
    }

    public static function isFileExists($path)
    {
        $f = new self($path);
        return $f->isExists();
    }

    public static function getFileContents($path)
    {
        $f = new self($path);
        return $f->getContents();
    }

    public static function putFileContents($path, $data, $flags=self::REWRITE)
    {
        $f = new self($path);
        return $f->putContents($data, $flags);
    }

    public static function deleteFile($path)
    {
        $f = new self($path);
        return $f->delete();
    }
}
<?php
/*
 * Файл bitrix/modules/main/lib/io/directory.php
 */

namespace Bitrix\Main\IO;

class Directory extends DirectoryEntry
{
    public function __construct($path, $siteId = null)
    {
        parent::__construct($path, $siteId);
    }

    public function isExists()
    {
        $p = $this->getPhysicalPath();
        return file_exists($p) && is_dir($p);
    }

    public function delete()
    {
        return self::deleteInternal($this->getPhysicalPath());
    }

    private static function deleteInternal($path)
    {
        if (is_file($path) || is_link($path))
        {
            if (!@unlink($path))
                throw new FileDeleteException($path);
        }
        elseif (is_dir($path))
        {
            if ($handle = opendir($path))
            {
                while (($file = readdir($handle)) !== false)
                {
                    if ($file == "." || $file == "..")
                        continue;

                    self::deleteInternal(Path::combine($path, $file));
                }
                closedir($handle);
            }
            if (!@rmdir($path))
                throw new FileDeleteException($path);
        }

        return true;
    }

    /**
     * @return array|FileSystemEntry[]
     * @throws FileNotFoundException
     */
    public function getChildren()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        $arResult = array();

        if ($handle = opendir($this->getPhysicalPath()))
        {
            while (($file = readdir($handle)) !== false)
            {
                if ($file == "." || $file == "..")
                    continue;

                $pathLogical = Path::combine($this->path, Path::convertPhysicalToLogical($file));
                $pathPhysical = Path::combine($this->getPhysicalPath(), $file);
                if (is_dir($pathPhysical))
                    $arResult[] = new Directory($pathLogical);
                else
                    $arResult[] = new File($pathLogical);
            }
            closedir($handle);
        }

        return $arResult;
    }

    /**
     * @param $name
     * @return Directory|DirectoryEntry
     */
    public function createSubdirectory($name)
    {
        $dir = new Directory(Path::combine($this->path, $name));
        if (!$dir->isExists())
            mkdir($dir->getPhysicalPath(), BX_DIR_PERMISSIONS, true);
        return $dir;
    }

    public function getCreationTime()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        return filectime($this->getPhysicalPath());
    }

    public function getLastAccessTime()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        return fileatime($this->getPhysicalPath());
    }

    public function getModificationTime()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        return filemtime($this->getPhysicalPath());
    }

    public function markWritable()
    {
        if (!$this->isExists())
            throw new FileNotFoundException($this->originalPath);

        @chmod($this->getPhysicalPath(), BX_DIR_PERMISSIONS);
    }

    public function getPermissions()
    {
        return fileperms($this->getPhysicalPath());
    }

    /**
     * @param $path
     *
     * @return Directory
     */
    public static function createDirectory($path)
    {
        $dir = new self($path);
        $dir->create();

        return $dir;
    }

    public static function deleteDirectory($path)
    {
        $dir = new self($path);
        $dir->delete();
    }

    public static function isDirectoryExists($path)
    {
        $f = new self($path);
        return $f->isExists();
    }
}
<?php
/*
 * Файл bitrix/modules/main/lib/io/path.php
 */

namespace Bitrix\Main\IO;

use Bitrix\Main;
use Bitrix\Main\Text;

class Path
{
    const DIRECTORY_SEPARATOR = '/';
    const DIRECTORY_SEPARATOR_ALT = '\\';
    const PATH_SEPARATOR = PATH_SEPARATOR;

    const INVALID_FILENAME_CHARS = "\\/:*?\"'<>|~#&;";

    // the pattern should be quoted, "|" is allowed below as a delimiter
    const INVALID_FILENAME_BYTES = "\xE2\x80\xAE"; // Right-to-Left Override Unicode Character

    protected static $physicalEncoding = "";
    protected static $logicalEncoding = "";

    protected static $directoryIndex = null;

    public static function normalize($path)
    {
        if (!is_string($path) || ($path == ""))
            return null;

        // slashes does not matter for Windows
        static $pattern = null, $tailPattern;
        if (!$pattern)
        {
            if(strncasecmp(PHP_OS, "WIN", 3) == 0)
            {
                // windows
                $pattern = "'[\\\\/]+'";
                $tailPattern = "\0.\\/+ ";
            }
            else
            {
                // unix
                $pattern = "'[/]+'";
                $tailPattern = "\0/";
            }
        }
        $pathTmp = preg_replace($pattern, "/", $path);

        if (strpos($pathTmp, "\0") !== false)
            throw new InvalidPathException($path);

        if (preg_match("#(^|/)(\\.|\\.\\.)(/|\$)#", $pathTmp))
        {
            $arPathTmp = explode('/', $pathTmp);
            $arPathStack = array();
            foreach ($arPathTmp as $i => $pathPart)
            {
                if ($pathPart === '.')
                    continue;

                if ($pathPart === "..")
                {
                    if (array_pop($arPathStack) === null)
                        throw new InvalidPathException($path);
                }
                else
                {
                    array_push($arPathStack, $pathPart);
                }
            }
            $pathTmp = implode("/", $arPathStack);
        }

        $pathTmp = rtrim($pathTmp, $tailPattern);

        if (substr($path, 0, 1) === "/" && substr($pathTmp, 0, 1) !== "/")
            $pathTmp = "/".$pathTmp;

        if ($pathTmp === '')
            $pathTmp = "/";

        return $pathTmp;
    }

    public static function getExtension($path)
    {
        $path = self::getName($path);
        if ($path != '')
        {
            $pos = Text\UtfSafeString::getLastPosition($path, '.');
            if ($pos !== false)
                return substr($path, $pos + 1);
        }
        return '';
    }

    public static function getName($path)
    {
        // $path = self::normalize($path);

        $p = Text\UtfSafeString::getLastPosition($path, self::DIRECTORY_SEPARATOR);
        if ($p !== false)
            return substr($path, $p + 1);

        return $path;
    }

    public static function getDirectory($path)
    {
        return substr($path, 0, -strlen(self::getName($path)) - 1);
    }

    public static function convertLogicalToPhysical($path)
    {
        if (self::$physicalEncoding == "")
            self::$physicalEncoding = self::getPhysicalEncoding();

        if (self::$logicalEncoding == "")
            self::$logicalEncoding = self::getLogicalEncoding();

        if (self::$physicalEncoding == self::$logicalEncoding)
            return $path;

        return Text\Encoding::convertEncoding($path, self::$logicalEncoding, self::$physicalEncoding);
    }

    public static function convertPhysicalToLogical($path)
    {
        if (self::$physicalEncoding == "")
            self::$physicalEncoding = self::getPhysicalEncoding();

        if (self::$logicalEncoding == "")
            self::$logicalEncoding = self::getLogicalEncoding();

        if (self::$physicalEncoding == self::$logicalEncoding)
            return $path;

        return Text\Encoding::convertEncoding($path, self::$physicalEncoding, self::$logicalEncoding);
    }

    public static function convertLogicalToUri($path)
    {
        if (self::$logicalEncoding == "")
            self::$logicalEncoding = self::getLogicalEncoding();

        if (self::$directoryIndex == null)
            self::$directoryIndex = self::getDirectoryIndexArray();

        if (isset(self::$directoryIndex[self::getName($path)]))
            $path = self::getDirectory($path)."/";

        if ('utf-8' !== self::$logicalEncoding)
            $path = Text\Encoding::convertEncoding($path, self::$logicalEncoding, 'utf-8');

        return implode('/', array_map("rawurlencode", explode('/', $path)));
    }

    public static function convertPhysicalToUri($path)
    {
        if (self::$physicalEncoding == "")
            self::$physicalEncoding = self::getPhysicalEncoding();

        if (self::$directoryIndex == null)
            self::$directoryIndex = self::getDirectoryIndexArray();

        if (isset(self::$directoryIndex[self::getName($path)]))
            $path = self::getDirectory($path)."/";

        if ('utf-8' !== self::$physicalEncoding)
            $path = Text\Encoding::convertEncoding($path, self::$physicalEncoding, 'utf-8');

        return implode('/', array_map("rawurlencode", explode('/', $path)));
    }

    public static function convertUriToPhysical($path)
    {
        if (self::$physicalEncoding == "")
            self::$physicalEncoding = self::getPhysicalEncoding();

        if (self::$directoryIndex == null)
            self::$directoryIndex = self::getDirectoryIndexArray();

        $path = implode('/', array_map("rawurldecode", explode('/', $path)));

        if ('utf-8' !== self::$physicalEncoding)
            $path = Text\Encoding::convertEncoding($path, 'utf-8', self::$physicalEncoding);

        return $path;
    }

    protected static function getLogicalEncoding()
    {
        if (defined('BX_UTF'))
            $logicalEncoding = "utf-8";
        elseif (defined("SITE_CHARSET") && (strlen(SITE_CHARSET) > 0))
            $logicalEncoding = SITE_CHARSET;
        elseif (defined("LANG_CHARSET") && (strlen(LANG_CHARSET) > 0))
            $logicalEncoding = LANG_CHARSET;
        elseif (defined("BX_DEFAULT_CHARSET"))
            $logicalEncoding = BX_DEFAULT_CHARSET;
        else
            $logicalEncoding = "windows-1251";

        return strtolower($logicalEncoding);
    }

    protected static function getPhysicalEncoding()
    {
        $physicalEncoding = defined("BX_FILE_SYSTEM_ENCODING") ? BX_FILE_SYSTEM_ENCODING : "";
        if ($physicalEncoding == "")
        {
            if (strtoupper(substr(PHP_OS, 0, 3)) === "WIN")
                $physicalEncoding = "windows-1251";
            else
                $physicalEncoding = "utf-8";
        }
        return strtolower($physicalEncoding);
    }

    public static function combine()
    {
        $numArgs = func_num_args();
        if ($numArgs <= 0)
            return "";

        $arParts = array();
        for ($i = 0; $i < $numArgs; $i++)
        {
            $arg = func_get_arg($i);
            if (is_array($arg))
            {
                if (empty($arg))
                    continue;

                foreach ($arg as $v)
                {
                    if (!is_string($v) || $v == "")
                        continue;
                    $arParts[] = $v;
                }
            }
            elseif (is_string($arg))
            {
                if ($arg == "")
                    continue;

                $arParts[] = $arg;
            }
        }

        $result = "";
        foreach ($arParts as $part)
        {
            if ($result !== "")
                $result .= self::DIRECTORY_SEPARATOR;
            $result .= $part;
        }

        $result = self::normalize($result);

        return $result;
    }

    public static function convertRelativeToAbsolute($relativePath)
    {
        if (!is_string($relativePath))
            throw new Main\ArgumentTypeException("relativePath", "string");
        if ($relativePath == "")
            throw new Main\ArgumentNullException("relativePath");

        return self::combine($_SERVER["DOCUMENT_ROOT"], $relativePath);
    }

    public static function convertSiteRelativeToAbsolute($relativePath, $site = null)
    {
        if (!is_string($relativePath) || $relativePath == "")
            $site = SITE_ID;

        $basePath = Main\SiteTable::getDocumentRoot($site);

        return self::combine($basePath, $relativePath);
    }

    protected static function validateCommon($path)
    {
        if (!is_string($path))
        {
            return false;
        }

        if (trim($path) == "")
        {
            return false;
        }

        if (strpos($path, "\0") !== false)
        {
            return false;
        }

        if(preg_match("#(".self::INVALID_FILENAME_BYTES.")#", $path))
        {
            return false;
        }

        return true;
    }

    public static function validate($path)
    {
        if(!static::validateCommon($path))
        {
            return false;
        }

        return (preg_match("#^([a-z]:)?/([^\x01-\x1F".preg_quote(self::INVALID_FILENAME_CHARS, "#")."]+/?)*$#isD", $path) > 0);
    }

    public static function validateFilename($filename)
    {
        if(!static::validateCommon($filename))
        {
            return false;
        }

        return (preg_match("#^[^\x01-\x1F".preg_quote(self::INVALID_FILENAME_CHARS, "#")."]+$#isD", $filename) > 0);
    }

    /**
     * @param string $filename
     * @param callable $callback
     * @return string
     */
    public static function replaceInvalidFilename($filename, $callback)
    {
        return preg_replace_callback(
            "#([\x01-\x1F".preg_quote(self::INVALID_FILENAME_CHARS, "#")."]|".self::INVALID_FILENAME_BYTES.")#",
            $callback,
            $filename
        );
    }

    /**
     * @param string $filename
     * @return string
     */
    public static function randomizeInvalidFilename($filename)
    {
        return static::replaceInvalidFilename($filename,
            function()
            {
                return chr(rand(97, 122));
            }
        );
    }

    public static function isAbsolute($path)
    {
        return (substr($path, 0, 1) === "/") || preg_match("#^[a-z]:/#i", $path);
    }

    protected static function getDirectoryIndexArray()
    {
        static $directoryIndexDefault = array(
            "index.php" => 1,
            "index.html" => 1,
            "index.htm" => 1,
            "index.phtml" => 1,
            "default.html" => 1,
            "index.php3" => 1
        );

        $directoryIndex = Main\Config\Configuration::getValue("directory_index");
        if ($directoryIndex !== null)
            return $directoryIndex;

        return $directoryIndexDefault;
    }
}

Поиск: CMS • Web-разработка • Битрикс • Директория • Класс • Копировать • Удалить • Файл • IO\Directory • IO\File • IO\Path • createDirectory • deleteDirectory • isDirectoryExists • getDocumentRoot • isFileExists • getFileContents • putFileContents • deleteFile • getDirectory

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.