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

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

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

[1 страница]

Введение


Нет, нет и еще раз нет! Изобретение "велосипедов" не преследуется по закону, но и не особо приветствуется. Просто иногда хочется понять механизм работы некоторых элементов, к которым давно привык, и не обращаешь на них внимание. Для обработки данных, получаемых из формы, существует много модулей: CGI, CGI::Simple, CGI::Lite, CGI::WebIn, это из тех, которые знаю я. Наверняка их еще больше. А что я вижу в скриптах "неизвестного производства"?

$buffer = $ENV{QUERY_STRING};

if (
$ENV{REQUEST_METHOD} eq POST) {
   
read(STDIN, $buffer, $ENV{CONTENT_LENGTH})
}

@
pairs = split(/&/, $buffer);

foreach
$pair (@pairs) {
    (
$name, $value) = split(/=/, $pair);
   
$name =~ tr/+/ /;
   
$name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack(C, hex($1))/eg;
   
$value =~ tr/+/ /;
   
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack(C, hex($1))/eg;
   
$value =~ s/<!--(.\n)*-->/<br>/g;
   
$value =~ s/</&lt;/g;
   
$value =~ s/>/&gt;/g;
   
$value =~ s/\cM/<br>/g;
   
$value =~ s/\n/ /g;
   
$value =~ s/\/ /g;
   
$value =~ s/\/ /g;
   
$value =~ s/<([^>]\n)*>/<br>/g;
   
$FORM{$name} = $value;
}

После чего, начинающие специалисты копируют этот код в свои скрипты и начинают "флудить" на форумах (каюсь: сам таким был и так делал). Но это не самое интересное, проблемы начинаются после того, как потребуется "фильтровать" данные, но не все и не так; потом, иногда форма отправляет несколько значений для одного параметра, а мы получаем только одно; про upload вообще помолчу. В итоге, этот код начинает "обрастать" дополнительными "фишками". А требований все больше и больше:
...выносим этот код в отдельную внешнюю процедуру, так как при доработках постоянно при ходится править кучу скриптов...
...старые доработки и фильтры удалять нельзя, из используют некоторые скрипты, приходится делать дополнительные...
...upload - черт с ним, цепляем модуль CGI, но не везде...
...некоторые параметры надо получить в виде массива, заносим эти параметры отдельно...
и так далее... в общем полный улет... А когда это все надоедает, начинаем писать use CGI в скриптах, и не морочим себе голову.

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

1. Какие данные мы получаем


В основном (если не всегда), данные передаются только двумя методами:

  • GET;
  • POST;

Но во время отправки данных методом POST мы можем передать дополнительные данные в URI.

А так же, данные предаются практически всегда (если не всегда) двумя типами данных:

  • text/plain (text/html);
  • multipart/form-data (только для метода POST);

Формат передаваемых данных можно посмотреть здесь. Но особо не вдаваясь в подробности можно сказать так:

если тип данных text/... данные передаются в виде:

param1=value1&param2=value2

Где "&" - разделитель параметров, а "=" - разделитель между параметром и значением. При этом можно не волноваться по поводу того, что в имени или значении параметра могут быть эти символы, так как браузер автоматически конвертирует эти символы (и некоторые другие) в шестнадцатеричный формат.

если тип данных multipart/form-data:

-----------------------------7d513a1b160308
Content
-Disposition: form-data; name=param1

value1
-----------------------------7d513a1b160308
Content
-Disposition: form-data; name=file; filename=D:\param2.txt
Content-Type: text/plain

blablablablablablablablabla
blablablablablablablabla
blablablablablablabla
blablablablablabla
blablablablabla
blablablabla
;
blablabla?
blabla;

bla
blabla
bla
blablabla

blablablablablabla
-----------------------------7d513a1b160308--

При этом мы видим, что предварительного ковертирования символов - нет, то есть данные передаются "как есть". Отправной точкой для нас является только уникальный разделитель, в нашем случае - "-----------------------------7d513a1b160308" (естественно, что он каждый раз новый).

Какие данные передает нам Cookies:

Данные передаются в переменной окружения $ENV{HTTP_COOKIE} ($ENV{COOKIE}), формат:

param1=value1; param2=value2; param3 = value3;

В общем, ничего сложного, итак:

2. Начало модуля и объявление объекта:


Ничего нового, все как по учебнику:

package My::CGI;
# Без него никак нельзя :)
use strict;
# С помощью этого модуля, будем определять FILEHANDLE
# Модуль выбран первый попавшийся, если кому нравится другой - пожалуйста
use IO::File;
# Версия, что бы потом не запутаться
our $VERSION = 1.0.0;

# Процедура объявления объекта
sub new {
# %common - дополнительные сведения, в нашем случае, максимальный объем принимаемых данных
   
my ($self, %common) = @_;
   
$self = {
            
max_upload => 262144, # Default 256 Kb
# Здесь будем хранить имена и значения параметров
            
data       => {},
# Здесь будем хранить куки
            
cookies    => {},
# Здесь будем хранить ссылки на временные файлы которые загрузили из формы
            
tmp        => {},
    };
# Определяем максимальный объем передаваемых данных, если надо
   
$self->{max_upload} = $common{MAX_UPLOAD} if $common{MAX_UPLOAD};
# Запускаем процедуру разбора полученных данных
   
$self = &_parse_common_data($self);
# Благославляем наш объект
   
bless $self;
# ... и возвращаем
   
return $self;
}

Сама собой выплыла следующая процедура (_parse_common_data) - разбор полученных данных.

3. Разбор полученных данных


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

  • данные переданные методом GET или в URI (QUERY_STRING);
  • данные переданные методом POST (CONTENT_LENGTH + STDIN) при этом определить какого они типа;
  • данные Cookies (HTTP_COOKIE);

Код:

sub _parse_common_data {

   
my $self = shift;

# Проверяем наличие QUERY_STRING, при этом не имеет значение метод передачи
# данных, так при методе GET у нас в этой переменной передаются значения формы,
# при методе POST, дополнительные даные в URI, а может просто быть запрос с
# какими-либо параметрами
   
if ($ENV{QUERY_STRING}) {
# и если у нас есть значение, то обрабатываем данные, при этом отдельно указывая
# метод, так для метода POST - POST, остальные - GET;
       
$self = &_parse_QUERY_STRING($self, GET)
    }

# Проверяем метод передачи данных, для обработки POST
   
if (uc($ENV{REQUEST_METHOD}) eq POST) {
# Если тип данных multipart, то передаем обработку в соответсвующую процедуру
       
if (exists($ENV{CONTENT_TYPE}) && $ENV{CONTENT_TYPE} =~m ^\s*multipart/form-datai) {
            
$self = &_parse_MultiPart($self)
# иначе стандартная обработка, с указанием, что обрабатываются данные метода POST
       
} else {
            
$self = &_parse_QUERY_STRING($self, POST)
        }
    }

# Проверяем наличие переданных Cookies
   
if ($ENV{HTTP_COOKIE} $ENV{COOKIE}) {
# Если есть, то обрабатываем
       
$self = &_parse_COOKIES($self)
    }
# Возвращаем заполненый данными массив
   
return $self
}

Процедура небольшая, и несложная, пора переходить к самому интересному:

4. Разбор данных типа text


Что нам нужно, собственно алгоритм:

  • разобрать по отдельности все параметры;
  • разобрать имя параметра и его значение;
  • обработать эти данные (так как некоторые символы при отправке конвертируются в шестнадцатеричный код);
  • положить результат в наш хеш (который в последствии станет объектом), но мы должны учесть, что параметр может быть один, а значений несколько;

Код:

sub _parse_QUERY_STRING {
   
my ($self, $type) = @_;
   
my $data;
# Выбираем данные в соответсвии с методом передачи данных
   
if ($type && $type eq POST) {
       
read(STDIN, $data, $ENV{CONTENT_LENGTH})
    } else {
       
$data = $ENV{QUERY_STRING}
    }
# Разделяем отдельно параметры. В общем, по сути, достаточно было бы и одного
# символа &, в качестве разделителя, но иногда проявляется символ ?, а в
# модуле CGI еще используется символ ;, но впрочем, хуже не будет если мы укажем
# все символы, тем более как сказано выше, боятся того, что в имени параметра или
# его значении может проскочить этот символ - не стоит, так что:
   
my @pairs = split(/[\?\&\;]/,$data);
    foreach (@
pairs) {
# Отделяем имя параметра от его значения, цифра 2 говорит о том, что переменная $_
# разбивается только на 2 части, хотя это лишнее, но тоже не помешает
       
my ($param, $value) = split(=, $_, 2);
# Если какого-либо значения нет, то данный параметр пропускаем
       
next unless $param && $value;
# Декодируем полученные значения из шестнадцатеричного формата отдельной
# Хотя, отдельно процедуру выносить не обязательно, только для удобства
       
$param = &URLDecode($param);
       
$value = &URLDecode($value);
# Внедряем в наш хеш полученные данные, так как данная функция пригодится нам и
# при обработке данных типа multipart, то выносим её отдельно
       
$self = &_include_data($self, $param, $value);
    }

    return
$self
}

Быстро обрисуем процедуру URLDecode, она взята как есть у Дмитрия Котерова (CGI::WebIn), и сложного в ней ничего нет:

sub URLDecode {my $s = shift; $s =~tr /+/ /; $s =~s /%([0-9A-Fa-f]{2})/chr(hex($1))/esg; return $s}

А вот на внедрении данных заострим внимание:



Похожие статьи:
- Поисковая оптимизация сайта
- Использование шаблонов дизайна в ASP.NET
- BB-коды
- Как узнать по каким запросам находят мой сайт в поисковых машинах?
- Что такое клоакинг?
- Пишем элементарный рассылщик
- И снова отказываемся от модуля CGI?
- Форматируем дату, полученную из БД
- Потенциальная уязвимость php-скриптов
- Десять правил написания безопасного кода на PHP
- CSS: советы и приёмы, часть 2
- Создание системы учета посещений
- Как узнать размер файла на каком-то веб-сервере


Оглавление | Обсудить на форуме | Главная страница сайта | Карта сайта |

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