Главная страница статей --> Советы по фотошопу, графике и хитрости в построении php кода

И снова отказываемся от модуля CGI?

Источник: realcoding.net

[2 страница]

5. Заполнение объекта данными


В общем случае мы должны учитывать, что переданный параметр может содержать как одно значение, так и их массив,

sub _include_data {
# Получаем переданные в процедуру данные (вместе с объектом)
   
my ($self, $param, $value) = @_;
# В данных параметра подменяем \r\n на \n
   
$value =~s /(\x0d\x0a)(\x0a\x0d)/\n/sg;
# Проверяем наличие ключа в массиве, для того что бы определить, что параметр
# имеет не одно значение, в соответствии с этим сформировать массив значений
   
if (exists $self->{data}->{$param}) {
# Если массив значений уже сформирован (т.е. значение параметра уже является
# ссылкой на массив)
       
if (ref $self->{data}->{$param}) {
# ...просто прибавляем новый элемент к массиву
            
push @{$self->{data}->{$param}}, $value
       
} else {
# ...иначе, если до этого мы получили только одно значение параметра, и оно еще
# не является массивом, формируем ссылку на массив из двух элементов
            
$self->{data}->{$param} = [$self->{data}->{$param}, $value]
        }
    } else {
# Если параметра до этого не было, создаем соответствующий ключ хеша с значением
       
$self->{data}->{$param} = $value
   
}
# Возвращаем наш оъект
   
return $self
}

6. Разбор данных типа multipart/form-data


Сразу хочу оговорится, что изначально не предусматриваю загрузку нескольких файлов под одним именем параметра, не от лени, а от того, что в своей практике такого никогда не делал, чего и Вам не советую.

Каков алгоритм обработки данных:

  • проверить максимальный объем переданных данных (а то нам никакого хостинга не хватит);
  • определить уникальный разделитель данных;
  • определить тип переноса строк (\n, \r, или \r\n) для разных платформ они могут быть разными;
  • обработать в цикле данные как при обработке текстового типа, но с оговоркой:

    • если данные обычный текст - обрабатывать как обычно;
    • если данные загружаемый файл - специальная обработка;

Код:

sub _parse_MultiPart {
# Получаем массив, объект
   
my $self = shift;
# Проверяем объем полученных данных
   
if ($ENV{CONTENT_LENGTH} > $self->{max_upload}) {return}
# Включаем режим бинарного чтения входного потока
   
binmode STDIN;
# Считываем входящие данные
   
read(STDIN, $data, $ENV{CONTENT_LENGTH});
# Определяем в полученных данных уникальный разделитель, перенос строки и
# непосредственно все данные, по схеме:
# [гр. 1: уникальный разделитель][гр. 2: перенос строки][гр. 3: данные]->
# [гр. 2][гр. 1]--[гр. 2]
# Обращаю внимание, что мы сразу же начинаем использовать группы символов (1,2)
# в регулярном выражении для определения окончания данных (\2\1\-\-\2$)
   
my ($spliter, $end, $data) = $data =~m /^([^\r\n]+)([\n\r]+)(.*?)\2\1\-\-\2$/s;
# Включаем механизм случайных чисел
   
srand;
# Обрабатываем в цикле данные, разделителем данных у нас является:
# [перенос строки][уникальный разделитель][перенос строки]
   
foreach my $block (split($end.$spliter.$end, $data)) {
# обрабатываем параметр, разделителем описания параметра и данными у нас являются:
# два переноса строки
       
my ($header, $content) = split($end.$end, $block, 2);
# Объявляем внутренние переменные цикла
       
my ($param, $data);
# обрабатываем описание параметра, разделителями у нас являются:
# перенос строки и ; с пробельными символами
       
foreach my $line (split(/($end)(\s*\;\s*)/,$header)) {
# получаем имя описания и его значение
            
my ($name, $value) = split(/\=\:\s/,$line, 2);
# если имя описания - name, то это имя параметра
            
if ($name eq name) {
                (
$param) = $value =~/^\(.*)\$/
            }
# если имя описания - filename, то это имя загружаемого файла и, соответственно,
# текстовое значение параметра
            
if ($name eq filename) {
                (
$data) = $value =~/^\(.*)\$/
            }
        }
# если у нас инициализировано(!) значение параметра, то значит этот параметр,
# является загружаемым файлом, соответственно, для него своя обработка
       
if ($data) {
# Заносим тектовые данные параметра в объект
            
$self->{data}->{$param} = $data;
# Определяем имя временного файла, я, по привычке, использую случайное число,
# можно использовать pid процесса, в общем кому как нравится
            
my $temp_file = ./COME_.int(rand 100000)..tmp;
# Открываем файл для записи, создастся он сам, при этом не мешало бы
# использовать flock, но что-то я подумал, блокировать случайный файл - лишняя
# операция, при том что если такой прецедент возникнет, все равно будет ошибка,
# так как, какой-то загруженный, но не обработанный файл будет затерт. Так
# же папка, где расположен скрипт должна быть разрешена для записи, хотя
# обратное - исключение из правил, но все же...
            
open (UPL, >, $temp_file) die Error create temp files!!!;
# Определяем режим, как бинарный
            
binmode UPL;
# Записываем в него данные
            
print UPL $content;
# Закрываем файл
            
close UPL;
# Записываем имя временного файла в наш объект, ключ - имя параметра
            
$self->{tmp}->{$param} = $temp_file;
        } else {
# Иначе, если обрабатываемые данные не файл, то обычная обработка как текста. Но
# В данной обработке мы не используем процедуру URLDecode, потому как при типе
# данных multipart/form-data, управляющие символы не декодируются
            
$self = &_include_data($self, $param, $content);
        }
    }
# возвращаем заполненный массив, объект
   
return $self
}

В итоге у нас формируется два хеша в основном объекте: хеш параметров и текстовых значений, и хеш параметров с именами на временные файлы. Почему временные файлы? Да потому, что у нас и так входной поток забит данными, то бы его еще дублировать в массиве - не рационально.

7. Разбор данных cookies


Процедура обработки cookies такая же, как обычных текстовых данных, даже проще, так как управляющие символы не декодируются.

Код:

sub _parse_COOKIES {

   
my $self = shift;

   
my $cookies = $ENV{HTTP_COOKIE} $ENV{COOKIE};

    foreach
my $line (split(/\;\s*/,$cookies)) {

       
my ($param, $value) = split(=,$line, 2);

       
next unless $param && $value;

       
$self->{cookies}->{$param} = $value;

    }

    return
$self

}

Отмечу, что не включил возможность получения массива значений, так как ни разу такого не требовалось, но расширить данную процедуру будет не сложно.

На этом обработка данных заканчивается, остается только описать методы объекта позволяющие эти данные возвращать в скрипт.

8. Методы получения данных


Всего определим 3 метода:

  • получение текстового значения параметра в виде массива и нет;
  • получение FILEHANDLE закруженного файла (параметра);
  • получение текстового значения cookies;

Код:

# Метод (процедура) возврата текстового значения параметра
sub param {
# Получаем объект и имя параметра
   
my ($self, $param) = @_;
# Если данного параметра нет, возвращаем 0
   
unless ($self->{data}->{$param}) {return 0}
# Иначе получаем значение параметра
   
my $data = $self->{data}->{$param};
# Возврат, практически такой же как у модуля CGI:
# Если вернуть требуется массив:
#    Если значение - ссылка на массив - разыменовываем и возвращаем
#    Иначе возвращаем массив в 1 элемент
# Иначе:
#    Если значение - ссылка на массив - возвращаем первый элемент массива
#    Иначе - возвращаем значение
# то есть возврат зависит от того, что требуется вернуть:
# @param = $query->param(param); - требуется вернуть массив
# $param = $query->param(param); - требуется вернуть одно значение
   
return wantarray ? (ref $data ? @$data : ($data)) : (ref $data ? $data->[0] : $data)
}

# Метод (процедура) FILEHANDLE параметра
sub file {
# Получаем объект и имя параметра
   
my ($self, $param) = @_;
# Если данного параметра во временных файлах нет, возвращаем 0
   
unless ($self->{tmp}->{$param}) {return 0}
# Иначе создаем объект IO::File и возвращаем его
   
my $data = IO::File->new($self->{tmp}->{$param});
    return
$data
}

# Метод (процедура) возврата текстового значения cookies
sub cookies {
# Получаем объект и имя параметра
   
my ($self, $param) = @_;
# Просто возвращаем значение или 0 при его отсутсвии
   
return $self->{cookies}->{$param} 0;
}

Вызов методов осуществляется:

...
use
My::CGI;

my $query = new My::CGI (MAX_UPLOAD => 128000);

my $param = $query->(param_name);
my @param = $query->(param_name);

my $file_name = $query->param(file);
my $file_handle = $query->file(file);

my $cookie = $query->cookies(cookie_name);
...

И все, осталось только почистить "хвосты".

9. Метод DESTROY


В процессе загрузки файлов, у нас создаются временные файлы на сервере, которые после работы скрипта, становятся не нужными. Можно создать отдельный метод, "подчищающий" эти файлы, и вызывать его после обработки и получения данных в скрипте, а можно воспользоваться предопределенным методом DESTROY класса.

Код:

sub DESTROY {
   
my $self = shift;
    foreach (
values %{$self->{tmp}}) {unlink $_}
    return
1
}

Теперь все, со спокойной душой ставим в конце 1; и сохраняем файл.

Заключение


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

Контакты
Редакция:
[0.001]