Тупые вопросы про Exception

Silentland

Новичок
Можно практическую задачку разобрать, с которой бьюсь ни один день. Урезал как мог код, надеюсь, можно будет понять. Тут как раз проблема с исключениями. Например, функция saveFile принимает параметр $maxSize и отсеивает большие файлы. Но параметр $maxSize так же высчитывается исходя из оставшегося свободного места. Сейчас это делается в функции add, но понятно, что такой же код будет и в update, т.е. придется выносить его в какую-нибудь функцию validate. Но тогда получится, что эта функция должна будет так же загружать файл, вызывая saveFile, получать от нее информацию о размере файла и генерировать исключение либо «превышен допустимый размер файла» либо «закончилось место на диске». В общем, запутанная история. А без эксепшенов ИМХО будет еще запутаннее. Как бы вы поступили?

PHP:
define ("MAX_FILE_SIZE", min(convertbytes(ini_get('upload_max_filesize')), convertbytes(ini_get('post_max_size'))));

try {
    new FileUpload($param);

} catch(Exception $e) {
    switch ($e->getCode()) {		
        case 1: //Размер принятого файла превысил максимально допустимый размер, который задан директивой upload_max_filesize конфигурационного файла php.ini
        case 2: //Размер загружаемого файла превысил значение MAX_FILE_SIZE, указанное в HTML-форме.
        case 5: //Размер загружаемого файла превысил все мыслимые значения.
        case 12: //Размер загружаемого файла превысил допустимое значение.
            $error_report = 'Файл ' . $_FILES['name'] . ' превышает разрешенный для загрузки объем ' . round($this->maxFileSize/1048576, 2) . ' Мб';
            break;
        case 3: //Загружаемый файл был получен только частично.
        case 4: //Файл не был загружен.
        case 6: //Отсутствует временная папка. Добавлено в PHP 4.3.10 и PHP 5.0.3.
        case 7: //Не удалось записать файл на диск. Добавлено в PHP 5.1.0.
        case 8: //PHP-расширение остановило загрузку файла.
        case 9: //Не удалось переместить из временной директории
            $error_report = 'Загрузка не удалась';
            break;
        case 10: $error_report = 'Файл неразрешенного типа';
            break;
        case 11: $error_report = 'Обновите браузер';
            break;
        case 13: $error_report = 'Превышено количество загружаемых файлов';
            break;
        case 14: $error_report = 'Не хватает места на диске для записи этого файла'; //Откуда должно поступать исключение такого типа???
    }
}

class FileUpload {

    private $uploadDir = 'uploads/'; //Папка для хранения изображений
    private $maxNumberOfFiles = 100; //Максимальное количество загружаемых файлов
    private $maxSpace = 1000000; //Место, отведенное для загруженных файлов
    private $maxSize = MAX_FILE_SIZE; //Максимально допустимый вес загружаемого файла
    private $allowedType = array('jpeg', 'jpg', 'png', 'gif'); //Допустимые расширения для загрузки

    public function __construct($param) {
        $this->init($param);
    }
	
    private function init($param) {

        switch ($param['type']) {
        
            case add:     $this->add(); break;
							
            case delete:  $this->delete($param['id']); break;
				
            case update:  $this->update($param['id']); break;
			
            default:  throw new Exception('Еще какая-то шняга', 15);
        }
    }
    
    private function add() {

        //Загружаем файл на сервер
        list($file_name, $file_size) = $this->saveFile($this->validate(), $this->allowedType);
                        
        //Добавляем информацию в базу
        mysql_query("insert into `album1` set `title` = '".$data['title']."', file = '".$file_name."', size = '".$file_size."'");
	}

    private function validate() {
         //Проверяем, не превышено ли количество загружаемых файлов
        $res = mysql_query("select count(*) as quantity from `album1`");
        $row = mysql_fetch_assoc($res);
        if ($row['quantity'] >= $this->maxNumberOfFiles) {
            throw new Exception('Превышено количество загружаемых файлов', 13);
        }
        
        //Проверяем сколько осталось свободного места, и, если нужно, уменьшаем допустимый размер загружаемого файла
        $space = mysql_query("select sum(`size`) from `album1`");
        list($space) = mysql_fetch_row($space);
        
        if ($this->maxSpace && ($this->maxSpace - $space) < $this->maxSize) {
             return $this->maxSpace - $space;
             //В этом случае, если файл окажется слишком большим, нужно выдавать ошибку, что закончилось место на диске
        } elseif ($this->maxSpace && ($this->maxSpace - $space) >= $this->maxSize) {
             return $this->maxSize;
             //В этом случае, если файл окажется слишком большим, нужно выдавать ошибку, что превышен разрешенный размер
        }
    }
    
    private function delete($id) {}
	
    private function update($id) {}

    public function saveFile($maxSize, $allowedType) {
		
        if(!empty($_FILES)) {
            $file = $_FILES[$this->fieldName];

            if ($file['error']) {			
                throw new Exception('Загружен с ошибкой...', $file['error']);
            }			
            if ($file['size'] > $maxSize){	
                throw new Exception('Превышен допустимый размер файла', 12);
            }
				
            //Если файл без расширения, узнаем его из MIME-типа
            if (!($ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)))) $ext = preg_replace('/^.*([^\/]*)$/U', '$1', $file['type']);
				
            if (!in_array($ext, $allowedType)){		
                throw new Exception('Файл неразрешенного типа', 10);
            }
            $new_file_name = uniqid() . '.' . ($allowedType[$file['type']] ? $allowedType[$file['type']] : strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)));
            if (!move_uploaded_file($file['tmp_name'], $this->uploadDir . $new_file_name)) {
                throw new Exception('Не удалось переместить из временной директории', 9);
            }
				
            return array($new_file_name, $file['size'], $file['type']);

         } else {		

            $headers = getallheaders();
            if($headers['Content-Length'] > $maxSize) {
                throw new Exception('Превышен размер файла, установленный на сервере', 5);
            }
            throw new Exception('Старые браузеры пока не поддерживаются', 11);
        }
}
 

Фанат

oncle terrible
Команда форума
непонятно, чем отличается update от add в контексте сохранения файлов.
 

Silentland

Новичок
непонятно, чем отличается update от add в контексте сохранения файлов.
Каждому файлу соответствует свой id в базе (это может быть id формы, письма и проч.) При обновлении старый файл удаляется, а вместо него заливается новый по старому id.

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

P.S. Ф-я update использует другой запрос к БД, поэтому заменить ее комбинацией delete+add нельзя
 

Фанат

oncle terrible
Команда форума
об этом и речь.
Сейчас это делается в функции add, но понятно, что такой же код будет и в update
если апдейт будет состоять из двух строчек - делит и адд, то и проблемы не будет?
 

Silentland

Новичок
Апдейт это другой запрос к БД. Он использует так же ф-ю fileSave, куда пихать проверки свободного места не резон. Получается, что нужно делать общую для add и update функцию validate. Другого решения пока не вижу
 

WMix

герр M:)ller
Партнер клуба
PHP:
        try {
            throw new Exception('Превышено количество загружаемых элементов', 13);
        } catch(Exception $e) {
жжешь!

PHP:
if(!empty($_FILES)) {
//...
}
else{
            $headers = getallheaders();
            if($headers['Content-Length'] > $maxSize) {
                throw new Exception('Превышен размер файла, установленный на сервере', 5);
            }
            throw new Exception('Старые браузеры пока не поддерживаются', 11);
}
тут никакой хакер уже не пройдет !
 

WMix

герр M:)ller
Партнер клуба
Silentland
ну как... всегда сработает catch
можно было просто писать...
PHP:
$e = new Exception('Превышено количество загружаемых элементов', 13);
по сути это и написано!
 

Silentland

Новичок
Да, увидел, забыл почистить. Спасибо за наводку! Ветка где никакой хакер не пройдет всегда ошибку вызывает, пока до нее просто руки не дошли.
 

WMix

герр M:)ller
Партнер клуба
в случае else это empty($_FILES) ... файлов небыло поидеи....
 

Silentland

Новичок
в случае else это empty($_FILES) ... файлов небыло поидеи....
Если браузер старый или экзотический, массив $_FILES может не заполниться, тогда файл можно будет выдрать из входного потока. Так же, если файл превысит разрешенный сервером размер, то $_FILES опять же останется пустым (если передача аяксом), а узнать размер загружаемого файла можно будет только из заголовка.
 

WMix

герр M:)ller
Партнер клуба
тут точто эксепшин
PHP:
default: echo 'error';
add кажись пишется в кавычках
PHP:
case add:
раздели конструктор и вызов функций... не выноси мозг...


думаем внимательно
PHP:
private $maxSpace = 1000000; //Место, отведенное для загруженных файлов
...
$maxSize = $this->maxSpace ? min($this->maxSize, $this->maxSpace - $space) : $this->maxSize;
непонятно почему бы $this->allowedType не использовать непосредственно в saveFile как и $maxSize
PHP:
        $allowedType = $this->allowedType;
        
        //Загружаем файл на сервер
        list($file_name, $file_size) = $this->saveFile($maxSize, $allowedType);
удали из add проверку на Space перенеси в другое место... и я прочту так уж и быть еще разок!


Если браузер старый или экзотический, массив $_FILES может.....
то твоя программа всеравно ничего не запишет....
 

WMix

герр M:)ller
Партнер клуба
задача № 2
не знаю как тебе, но мне было бы сложно ореентироваться на код ошибки...
тут либо нужен доп файл где все коды описаны либо даже не знаю...
загляни сюда... может мыслей навеет
exceptions.extending
 

Silentland

Новичок
тут точто эксепшин
Так точно!

add кажись пишется в кавычках
Работает и так.

раздели конструктор и вызов функций... не выноси мозг...
Т.е.?

думаем внимательно
PHP:
private $maxSpace = 1000000; //Место, отведенное для загруженных файлов
...
$maxSize = $this->maxSpace ? min($this->maxSize, $this->maxSpace - $space) : $this->maxSize;
Тут мы подгоняем разрешенный для загрузки размер файла так, чтобы после загрузки, не было бы превышено отведенное под файлы пространство.

непонятно почему бы $this->allowedType не использовать непосредственно в saveFile как и $maxSize
По историческим причинам. Исправил уже.

удали из add проверку на Space перенеси в другое место... и я прочту так уж и быть еще разок!
Перенес.

то твоя программа всеравно ничего не запишет....
Не проверял пока. Но размер файла, в этом случае, она возвращает верный.
 

WMix

герр M:)ller
Партнер клуба
Работает и так.
поспорить хочешь? хочешь написать правильно или лишь бы работало? лень было кавычки расставить или возмутиться хотелось?
Тут мы подгоняем разрешенный для загрузки размер файла так, чтобы после загрузки, не было бы превышено отведенное под файлы пространство.
а когда $this->maxSpace вернет false?
те одна строка это new и одна строка вызов метода!
иначе я даже не понимаю зачем тебе это строка
PHP:
return array($new_file_name, $file['size'], $file['type']);
Не проверял пока. Но размер файла, в этом случае, она возвращает верный.
те. если у человека старый как ты выражаешься браузер, мы его поучим загружать файлы а после пошлем его куда подальше с его браузером... правильно?

задание помер 2 читал, есть мысли по этому поводу?... или предлагаешь мне каждую цифорку в коде выискивать?

и еще один вопрос, ты хочешь чтоб я тебе говорил о том что я вижу и нахожу не правильным или нет?
 

Silentland

Новичок
поспорить хочешь? хочешь написать правильно или лишь бы работало? лень было кавычки расставить или возмутиться хотелось?
В продакшене, конечно, кавычки ставлю, хоть уже и не помню зачем. Хотя действительно, зачем? Это же на JS, с переменной не спутается.
а когда $this->maxSpace вернет false?
Если $this->maxSpace вернет false это означает, что maxSpace у нас, вообще, отключен. Т.е. maxSpace = 0 просто снимает ограничение на объем загруженных файлов.
те одна строка это new и одна строка вызов метода!
Так:
PHP:
new FileUpload;
FileUpload->init($param);
?
Так же сложнее пользоваться классом.
иначе я даже не понимаю зачем тебе это строка
PHP:
return array($new_file_name, $file['size'], $file['type']);
Загружаем файл, возвращаем его параметры и новое имя. Функцию fileSave можно будет легко перетащить в другой проект.
те. если у человека старый как ты выражаешься браузер, мы его поучим загружать файлы а после пошлем его куда подальше с его браузером... правильно?
Ну да, так и есть. Если у меня руки дойдут, то сделаю загрузку и для таких людей, но сначала исследую, может и браузеров-то таких не осталось, тогда, конечно, послать будет самым верным решением.
задание помер 2 читал, есть мысли по этому поводу?... или предлагаешь мне каждую цифорку в коде выискивать?
Читал. Что-то не могу найти где бы у меня использовались коды ошибок без описания. По-моему, там даже дублирование описаний есть.
и еще один вопрос, ты хочешь чтоб я тебе говорил о том что я вижу и нахожу не правильным или нет?
Если аргументированно, почему бы и нет
 

WMix

герр M:)ller
Партнер клуба
Silentland
в пхп без кавычек и доллара константы пишуться,
при комманде new создается экземнляр, инстанция ... этого класса и назад возвращается обьект, это не пользоваться,... если хочется писать в одну строчку, напиши статический метод...
но вобщем мне пофигу, если ты считаешь это достойным кодом, удобным на столько что можно метод вырезать в проекты, то непонимаю что ты хочешь?

оффигенный у тебя код!!...

еслиб я это писал, яб разделил бы логику ввода данных от пользователя (form), логику записи файлов (file_transfer) и логику записи в базу (model).. у меня былиб совершенно другие исключения, твои описанные исключения, были бы у сообщениями пользователю на уровне контроллера.

по моему мнению, исключения нужны чтоб программиста ткнуть носом: тут "чтото забыл", это, в твоем случае, папки не существует, нет прав для записи, нет свободного места вообще (не в альбоме) и тд. когда метод ожидает некие предпосылки, но они, как это по русски, не удовлетворены чтоли...

ну как-то так... желаю удачи
 

Silentland

Новичок
при комманде new создается экземнляр, инстанция ... этого класса и назад возвращается обьект, это не пользоваться,... если хочется писать в одну строчку, напиши статический метод...
Ага, изначально и делал все статически и вообще, планировал использовать класс только через прототип fileUploader::init(); Потом понял, что лажа. Сейчас у меня вызов идет так: new fileUploader(array('maxFileSize'=>1000, 'allowedType'=>array('png', 'jpeg', 'gif'))), т.е. для каждого пользователя создаются экземпляры с уникальными настройками.

еслиб я это писал, яб разделил бы логику ввода данных от пользователя (form), логику записи файлов (file_transfer) и логику записи в базу (model).. у меня былиб совершенно другие исключения, твои описанные исключения, были бы у сообщениями пользователю на уровне контроллера.
Согласен. У меня также, только логику записи в БД не стал приводить, чтобы не загромождать код. У меня исключения как раз больше для пользователя. Специфика такая. Думаешь, сделать все на if-else раз так?
 

WMix

герр M:)ller
Партнер клуба
я не знаю что ты хочешь... а так как я предлагаю тебе не нравиться....
 
Сверху