Filesystem wrapper with Exceptions

Redjik

Джедай-мастер
https://github.com/Redjik/Filesystem

Для чего это нужно.

1) Убрать собаку при работе с файловой системой
* между file_exists и @fopen много чего может произойти, такую ошибку крайне сложно отловить
* ошибки, происходящие при работе с файловой системой, это исключительные ситуации и обыного E_WARNING чаще всего мало - приходится лепить кучу условий
2) отказ от кучи условий аля is_writeable, is_dir, file_exists, is_file



Возможно излишне много методов, но я попробовал охватить все основные функции.

Большинство вещей можно было посадить на __call, но решил оставить, чтобы написать полноценный phpDoc с типами эксепшенов.

Почти все методы проверял руками, все что в phpDoc прописано в @throws - эти ошибки я смог вызвать.

Обращаю внимание на stat и производные функции filectime и т.п. c ними возникла проблема, выбирал между двумя способами решения.
1) Пренеправление stderr от exec
2) То что в классе
все производные от stat не пишут информацию об ошибке - они только пишет, что произошла ошибка

Нужно:
1) Feedback
2) Написать тесты - если кому не влом, меня после двухдевной копипасты и проверки каждого чиха ручками уже бесит... возможно сам займусь попзже, но если кто-то поможет - буду рад.
3) Подсказать, если не учел какие-то типы ошибок.

Повторяю - класс рассчитан ТОЛЬКО для работы с файловой системой.
 

AmdY

Пью пиво
Команда форума
Redjik
вроде в следующей версии аыха всё прекратят в исключения.
 

Redjik

Джедай-мастер
чОрт - два дня прокакал =)
да я подумывал засесть в будущем с патчем уже, но раз это сделают за меня на неродном C - то так даже лучше
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Redjik
вроде в следующей версии аыха всё прекратят в исключения.
не в следующей, никто не говорит о всех ошибках, и не факт, что превратят;
пока только обсуждают варианты и пишут сам патч https://wiki.php.net/rfc/engine_exceptions
но что из этого выйдет и включат ли его - я не знаю, это надо internals читать
 
Последнее редактирование:
  • Like
Реакции: AmdY

grigori

( ͡° ͜ʖ ͡°)
Команда форума
все производные от stat не пишут информацию об ошибке - они только пишет, что произошла ошибка
может быть, в исключении можно ее отловить и узнать в чем ошибка? проверить наличие файла и пермиссии
 

Redjik

Джедай-мастер
может быть, в исключении можно ее отловить и узнать в чем ошибка? проверить наличие файла и пермиссии
теряется атмарность операции, через exec можно получить все данные, но как то не кошерно, хотя этот вариант у меня работал

сделал fopen и fstat($handle)
все производные от stat (кроме filetype - вообще какой-то дурдом с этой функцией, в Линуксе типы файлов по-другому называются нежели php отдает) просто обращаются к методу
https://github.com/Redjik/Filesystem/blob/master/vendor/Redjik/Filesystem/Filesystem.php#L826 - вот например
 

Redjik

Джедай-мастер
Кстати в начале два метода, написаны просто, чтобы показать возможности подхода с Эксепшенами.
Для примера - вот что сделал Фабиен
PHP:
    /**
     * Removes files or directories.
     *
     * @param string|array|\Traversable $files A filename, an array of files, or a \Traversable instance to remove
     *
     * @throws IOException When removal fails
     */
    public function remove($files)
    {
        $files = iterator_to_array($this->toIterator($files));
        $files = array_reverse($files);
        foreach ($files as $file) {
            if (!file_exists($file) && !is_link($file)) {
                continue;
            }

            if (is_dir($file) && !is_link($file)) {
                $this->remove(new \FilesystemIterator($file));

                if (true !== @rmdir($file)) {
                    throw new IOException(sprintf('Failed to remove directory "%s".', $file), 0, null, $file);
                }
            } else {
                // https://bugs.php.net/bug.php?id=52176
                if (defined('PHP_WINDOWS_VERSION_MAJOR') && is_dir($file)) {
                    if (true !== @rmdir($file)) {
                        throw new IOException(sprintf('Failed to remove file "%s".', $file), 0, null, $file);
                    }
                } else {
                    if (true !== @unlink($file)) {
                        throw new IOException(sprintf('Failed to remove file "%s".', $file), 0, null, $file);
                    }
                }
            }
        }
    }
Вот, что получилось у меня
PHP:
    /**
     * Unified method to unlink file or remove directory
     * Can remove recursively with $recursive option set to true
     *
     * @param string $dirOrFileName
     * @param bool $recursive
     *
     * @throws PermissionException
     * @throws DoesNotExistException
     * @throws DirectoryNotEmptyException
     *
     * @return bool
     */
    public function purge($dirOrFileName,$recursive = false)
    {
        try{
            $result = $this->unlink($dirOrFileName);
        }catch (IsDirectoryException $e){
            try{
                $result = $this->rmdir($dirOrFileName);
            }catch (DirectoryNotEmptyException $e){
                if ($recursive){
                    $files = array_diff($this->scandir($dirOrFileName), array('.','..'));
                    foreach ($files as $file)
                    {
                        $this->purge($dirOrFileName.DIRECTORY_SEPARATOR.$file,true);
                    }
                    $result = $this->rmdir($dirOrFileName);
                }else{
                    throw new DirectoryNotEmptyException($e->getMessage(),$e->getCode());
                }
            }
        }

        return $result;
    }
ЗЫ. каждый вызов оборачиваю в set_error_handler, чтобы не ломать совместимость во фреймворках и не искать текст в каждой ошибке, а только в функциях работы с файловой системой.
 

AmdY

Пью пиво
Команда форума
Перенёс в более подходящую и менее зафлуженную ветку, чтобы не потерять.
 
Сверху