Архив за Июнь 2008

Отправка данных из скрипта методом POST

Пусть у нас есть простая форма, состоящая из двух полей и кнопки для отправки данных:

<form action="action.php" method="post">
Имя: <input name="name" type="text" /><br/>
Пароль: <input name="password" type="password" /><br/>
<input name="submit" type="submit" value="Отправить" />
</form>

Обработчик формы action.php выводит в браузер текст, введенный в поля формы:

<?php
echo 'Имя: '.$_POST['name'].'<br/>';
echo 'Пароль: '.$_POST['password'].'<br/>';
?>

HTML форма позволяет пользователю сформировать POST-запрос, который затем отсылается браузером. Мы сформируем такой запрос скриптом.

Метод POST, в отличие от метода GET, посылает данные не в строке запроса, а в области данных, после заголовков. Передача данных аналогична методу GET: группы name=value объединяются при помощи амперсанда (&):

name=Евгений&password=qwerty

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

Отправка данных методом POST через сокеты

<?php
$hostname = 'localhost';
$path = '/handler/action.php';
$content = '';
// Устанавливаем соединение с сервером $hostname
$fp = fsockopen($hostname, 80, $errno, $errstr, 30);
// Проверяем успешность установки соединения
if (!$fp) die('<p>'.$errstr.' ('.$errno.')</p>');

// Данные HTTP-запроса
$data = 'name='.urlencode('Евгений').'&password='.urlencode('qwerty');
// Заголовок HTTP-запроса
$headers = 'POST '.$path." HTTP/1.1\r\n";
$headers .= 'Host: '.$hostname."\r\n";
$headers .= "Content-type: application/x-www-form-urlencoded\r\n";
$headers .= 'Content-Length: '.strlen($data)."\r\n\r\n";
// Отправляем HTTP-запрос серверу
fwrite($fp, $headers.$data);
// Получаем ответ
while ( !feof($fp) ) $content .= fgets($fp, 1024);
// Закрываем соединение
fclose($fp);
// Выводим ответ в браузер 
echo $content;
?>

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

HTTP/1.1 200 OK
Date: Sat, 28 Jun 2008 07:53:19 GMT
Server: Apache/2.0.55 (Win32) PHP/5.2.1
X-Powered-By: PHP/5.2.1
Content-Length: 42
Content-Type: text/html

Имя - Евгений<br/>
Пароль - qwerty<br/>

Загрузка POST-данных с использованием CURL

Помимо сокетов, обеспечивающих низкоуровневое обращение к серверу, PHP располагает специальным расширением CURL (Client URL Library).

<?php
// Задаем адрес удаленного сервера
$curl = curl_init("http://localhost/handler/action.php");
// Передача данных осуществляется методом POST
curl_setopt($curl, CURLOPT_POST, 1);
// Задаем POST-данные
$data = 'name=Евгений&password=qwerty';
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
// Выполняем запрос и выводим ответ в браузер
curl_exec($curl);
// Закрываем CURL соединение
curl_close($curl);
?>

С помощью функции curl_init() задается адрес удаленного сервера и путь к файлу на нем. В отличие от функции fsockopen(), необходимо задавать адрес полностью, включая префикс http://, т.е. расширение CURL позволяет работать с несколькими видами протоколов (HTTP, HTTPS, FTP). Если соединение с указанным сервером происходит успешно, функция curl_init() возвращает дескриптор соединения, который используется во всех остальных функциях библиотеки.

Для того, чтобы сообщить CURL о том, что данные будут передаваться методом POST, необходимо задать параметр CURLOPT_POST. POST-данные устанавливаются при помощи параметра CURLOPT_POSTFIELDS.

По умолчанию библиотека удаляет HTTP-заголовки, возвращаемые сервером. Однако CURL можно настроить на выдачу заголовков, если установить при помощи функции curl_setopt() ненулевое значение параметра CURLOPT_HEADER.

P.S. Из-за ошибки библиотеки сокетов протокол HTTP 1.1 под Windows работает медленно. При работе скрипта использующего сокеты под управлением этой ОС, лучше использовать версию HTTP 1.0.

Ссылки по теме:

Что такое кэширование?

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

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

Единственная проблема — это устаревание кэша. Допустим, что страница изменились, а кэш страницы — еще нет, и пользователю будет выдаваться старая версия. Способы борьбы:

  1. Выставлять более-менее приемлимое время устаревания кэша. Например, через 10 минут страница устаревает и кэш генерится заново.
  2. Сделать в административной части сайта кнопку «очистить кэш». В некоторых CMS вообще нет кнопки «очистить кэш», вместо нее есть кнопка «перегенерить сайт целиком». Нажимаем на эту кнопку — и весь сайт генерится в статичные файлы, то есть, фактически, в кэш.
  3. «Перегенерить сайт целиком» — ресурсоемкая операция. Гораздо лучше при редактировании страницы удалять кэш только той страницы, которую мы изменили. Но тут есть сложность: часто изменение одной страницы затрагивает и несколько других. Главное — понять, каких именно и очистить кэш у них тоже.

А теперь посмотрим, как мы можем применить полученные знания на практике. В начало скрипта, готорый генерит страницы нашего сайта поместим такой код:

<?php
// раздел настроек, которые вы можете менять
$cachedir = $_SERVER['DOCUMENT_ROOT'].'/cache/';
$cachetime = 3600; //время жизни кэша (1 час)

$thispage = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$cachelink = $cachedir.md5($thispage).".html";
if (file_exists($cachelink)) {
    $cachelink_time = filemtime($cachelink);
    // если кэш еще не устарел, читаем страницу из файла и отдаем браузеру
    if ((time() - $cachetime) < $cachelink_time) {
        readfile($cachelink);
        die();
    }
}
ob_start();
?>

А в конце скрипта добавим такие строчки

<?php
$fp = fopen($cachelink, 'w');
fwrite($fp, ob_get_contents());
fclose($fp);
ob_end_flush();
?>

Вот, собственно, и все. Все ваши страницы будут кэшироваться на 3600 секунд. Кэш страниц будет сохранен в папке cache.

Функции, которые мы использовали:

void ob_start()
Эта функция включает буферизацию вывода. Если буферизация вывода активна, никакой вывод скрипта не высылается (кроме headers); вывод сохраняется во внутреннем буфере. Содержимое этого внутреннего буфера может быть скопировано в строковую переменную с использованием ob_get_contents(). Для вывода содержимого этого внутреннего буфера используйте ob_end_flush(). Альтернативно ob_end_clean() втихую отбрасывает содержимое буфера.

string ob_get_contents()
Возвращает содержимое буфера вывода или FALSE, если буферизация вывода неактивна.

void ob_end_flush()
Эта функция отправляет содержимое буфера вывода (если оно имеется) и выключает буферизацию вывода. Если вы хотите в дальнейшем обработать содержимое буфера, вы должны вызвать ob_get_contents() до ob_end_flush(), так как содержимое буфера выбрасывается после вызова ob_end_flush().

Ссылки по теме:

Как сделать, чтобы записи в таблице нумеровались “без пробоев”?

Несколько раз слышал на форумах такой вопрос:

Как сделать, чтобы записи в таблице нумеровались «без пробоев», т.е. первичные ключи шли по порядку 1,2,3,4,5? Ведь когда я удаляю одну из записей, то получается что-то типа 1,2,4,5, а 3-го нет.

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

Допустим, у нас на сайте есть каталог продукции. Таблица categories (группы товаров):

+-------------------------------+---------------------+
| category_id INT [PRIMARY KEY] | name VARCHAR(255)   |
+-------------------------------+---------------------+
| ............................. | ................... |
+-------------------------------+---------------------+

и таблица products (товары):

+------------------------------+-----------------+---------------------+
| product_id INT [PRIMARY KEY] | category_id INT | name VARCHAR(255)   |
+------------------------------+-----------------+---------------------+
| .............................| ................| ................... |
+------------------------------+-----------------+---------------------+

Мы завели товары, группы товаров, все связали между собой с помощью поля category_id в таблице products. Спустя какое-то время шеф решил убрать какую-то группу товаров. Вы её убираете, заполняете «дыры» в нумерации таблицы categories, а про то, что надо изменить все соответствующие записи в products случайно забыли.

И начинается самое интересное – часть товаров куда-то пропадает, часть волшебным образом «переносится» в другие группы. Мониторы продаются в книжном отделе, пиво в компьютерах и так далее. Да, конечно, вы все предусмотрите и ничего не забудете. Но зачем? Для того, чтобы иметь напротив каждой строки порядковый номер? Но нумеровать строки можно и нужно в скриптах. Вот простейший пример:

<?php
$i = 1;
$res = mysql_query('SELECT *  FROM products');
echo '<table>';
while( $row = mysql_fetch_array($res) )
{
  echo '<tr><td>'.$i.'</td><td>'.$row['name'].'</td></tr>';
  $i = $i + 1;
}
echo '</table>';
?>

Источник: http://phpfaq.ru