Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Веб программирование.Начало.doc
Скачиваний:
2
Добавлен:
01.05.2025
Размер:
7 Мб
Скачать
☆

Сериализация объектов

Сериализация - это процесс, посредством которого массив или объект класса преобра­зуется в символьную строку для дальнейшей передачи или хранения. Сериализация вы­полняется следующим образом: serialize($obj);. Рас­смотрим пример:

<?php //pr30

$a= array("key" =>"testing", 1 => 10, 2 => "mystring");

echo $b=serialize($a) ,"<br>";

print_r (unserialize($b));

?>

Вывод :

a:3:{s:3:"key";s:7:"testing";i:1;i:10;i:2;s:8:"mystring";}

Array ( [key] => testing [1] => 10 [2] => mystring )

Строка сериализации не может непосредственно использоваться для передачи с по­мощью HTTP-протокола или для сохранения в базе данных, так как она может содержать недо­пустимые символы. Если данные нужно сохранить в базе данных, то подойдет простое использование функции addslashes(). При работе с HTTP-протоколом нужно использовать функцию urlencode(). После сериализации и кодирования строка может быть передана в базу данных как скрытый элемент HTML-формы или записана в файл для дальнейшего использования.

Для восстановления переменной из ее сериализованного представления в РНР преду­смотрена функция unserialize ():

unserialize($obj_string [, $callback_function])

где $obj_string представляет строку сериализации для восстановления переменной, $callback_function — имя необязательной функции обратного вызова для использования, если unserialize () реконструирует объект, который не был определен. При успешном за­вершении функция unserialize() возвращает реконструированный объект, или же false, если РНР не может реконструировать сериализованные данные.

Сериализация объектов в сессиях

Функция serialize() возвращает строку, содержащую представление объекта в виде по­тока байтов. При сериализации объекта PHP сохраняет в строке имя класса и значения свойств объекта. Методы объекта и данные о родительских классах или имплементируе­мых интерфейсах не сохраняются. Чтобы иметь возможность дессериализации объекта, должен быть определён класс этого объекта. То есть, если у вас имеется объект $a класса A в файле page1.php и вы его сериализуете, вы получите строку, которая ссылается на класс A и содержит все значения переменных, содержащихся в $a. Если вы хотите выпол­нить десериализацию в файле page2.php, воссоздав объект $a класса A, определение класса A обязано иметься в page2.php. Это можно сделать, например, сохранив определе­ние класса A в include-файле и подключив этот файл и в page1.php, и в page2.php.

//classa.php:

<?php

class A

{

private $one = 1;

public function show_one()

{

echo $this->one;

}

}

?>

<?php

//pr31.php:

include 'classa.php';

$a = new A;

$s = serialize($a);

echo 's=',$s;

// сохраняем $s там, где pr32.php может его найти.

$fp = fopen("store", "w");

fputs($fp, $s);

fclose($fp);

?>

Вывод:

s=O:1:"A":1:{s:6:"Aone";i:1;}

<?php

//pr32.php:

// это необходимо для правильной работы десериализации.

include ("classa.php");

$s = implode("", @file("store"));

$a = unserialize($s);

// теперь используем функцию show_one() объекта $a.

$a->show_one();

?>

Вывод:

1

Если вы используете сессии и session_register() для регистрации объектов, эти объ­екты сериализуются автоматически в конце каждой PHP-страницы и десериализуются ав­томатически на каждой последующей странице. Это означает, что эти объекты могут поя­виться на любой странице, после того как стали частью вашей сессии. Рекомендуется включать определение классов всех таких зарегистрированных объектов во все страницы, даже если вы и не используете эти классы во всех. Если объект десериализуется в отсут­ствие определения класса, он потеряет ассоциацию с этим классом и станет практически бесполезен.

Итак, если в вышеприведённом примере $a стало частью сессии через запуск ses­sion_register("a"), вы должны включить/include файл classa.inc во все ваши страницы, а не только в page1.php и page2.php.

Подготовка данных к сохранению.

Данные сессии, хранятся в массиве $_SESSION.

Данные хранятся на стороне сервера и об их наличии и составе пользователь может только догадываться. В программе эти данные могут состоять из любых типов данных, доступных программисту: строки, числа, массивы и объекты, но не ресурсы (об этом ниже). При записи данные подвергаются процедуре сериализации (serialization) - преобра­зования структур любой сложности (не содержащих ресурсов) в строку, из которой в по­следствии можно восстановить структуру назад. Для сериализации используется функция serialize(). Для обратного процесса - десериализации - unserialize(). При старте сессии эти PHP, получив от обработчика сессии строку, посредством функции unserialize() восста­навливает данные и помещает их в массив $_SESSION. При завершении программы про­исходит обратный процесс: массив $_SESSION автоматически сериализуется и строка пе­редается обработчику сессии для записи. Т.е. обработчик сессии работает только со стро­кой и не зависит от сложности структуры хранимых данных.

Рассмотрим особенности работы сериализации:

2. Объекты и их классы.

Особенность сериализации объекта в том, что сохраняется только состояние объекта - его данные, а для "оживления" обязательно заранее должен быть объявлен класс.

Выходов из этого положения два: заранее загружать объявления необходимых клас­сов, либо воспользоваться специальным обработчиком, который сможет попытаться под­грузить требуемый класс. Назначается обработчик в ini.php (unserialize_callback_func'), но может быть изменен во время работы программы.

Выглядит этот обработчик так:

Код:

<?php

ini_set("unserialize_callback_func", "my_unserialize_callback_func");

function my_unserialize_callback_func($classname)

{

// Предпологаем, что файлы, содержащие классы,

// находятся в поддиректории classes и имеют имя

// соответствующее имени класса.

$filename = "classes/$classname.php";

if (!is_file($filename))

die("Не удалось подгрузить класс '$classname'");

include_once($filename);

}

?>

Для того чтобы можно было управлять процессом сериализации/ десериализации в классах PHP предусмотрены специальные методы: __sleep() и __wakeup(). Метод __sleep вызывается перед сериализацией чтоб можно было убрать ненужные связи и должен вернуть массив со списком свойств, которые нужно сериализовать. Метод __wakeup() нужен для выполнения процедур инициализации объекта после де­сериализации. __wakeup вызывается после unserialize, чтоб можно было необходимые связи вернуть. Пример:

class A implements Serializable {

private $x='y';

function __construct()

{ echo __METHOD__ . " called\n"; }

function __destruct()

{ echo __METHOD__ . " called\n"; }

function serialize()

{ return $this->x; }

function unserialize($s)

{ $this->x=$s; }

}

class B {

private $x='y';

function __construct()

{ echo __METHOD__ . " called\n"; }

function __destruct()

{ echo __METHOD__ . " called\n"; }

function __sleep()

{ return array('x'); }

}

echo "Serializable (the new way)\n";

$x1=new A;

$s=serialize($x1);

var_dump($s);

$y1=unserialize($s);

var_dump($y1);

echo "__sleep/__wakeup (the old way)\n";

$x2=new B;

$s=serialize($x2);

var_dump($s);

$y2=unserialize($s);

var_dump($y2);

Сетевые соединения и сокеты

Понятие "сокет" означает "гнездо", "разъем". Два "гнезда" можно связать соедине­нием и передавать между ними данные. Каждое гнездо принадлежит определённому хосту (host – хозяин, держатель), имеющему уникальный IP адрес и порт, который определяется числом от 0 до 65535. По­сле этого сокет имеет адрес [host]:[port]. Например, 127.0.0.1:8888 означает, что сокет занимает порт 8888 на хосте 127.0.0.1(на данном компьютере). Переданная информация по­ступает на указанный порт по IP адресу. Чтобы не использовать труднозапоминаемый IP адрес, для доступа к хостам используется система символьных имен DNS.

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

Создание нового сокета

Независимо от типа (клиентский или серверный), сокеты создаются с помощью функ­ции socket_create ():

$socket=socket_create($domain, $type, $protocol);

Здесь $domain представляет тип создаваемого сокета и может принимать значения: AF_INET (протокол IPv4), AF_INET6 (протокол IPv6), AF_UNIX (локальное взаимодейст­вие). Второй параметр $type определяет тип взаимодействия через сокет. Значение $type=SOCK_STREAM означает надежный двунаправленный поток, основанный на TCP. Значение $type=SOCK_DGRAM означает ненадежный сокет, без подключения, передаю­щий данные по протоколу UDP. Используется, если важна скорость передачи. Значение $type= SOCK_RAW начает сокетное подключение, удобное для выполнения операций ICMP (Internet Control Message Protocol — протокол управляющих сообщений ), таких как trace и ping. Последний параметр $protocol может возвращаться функцией getprotobyname() или задаваться константой SOL_TCP или SOL_UDP для соединений TCP/UDP.

Функция socket_create() возвращает ссылку на сокет, либо значение false в случае ошибки. Сокет может быть уничтожен функцией socket_close(($socket);

Кроме возврата значения false, информацию об последней ошибке в данном сокете можно получить с помощью функции $error_code =socket_last_error($socket);. Чтобы транслировать его из целого числа в понятную форму, в API-интерфейсе сокетов преду­смотрена дополнительная функция socket_strerror($error_code);

Создание подключения клиентских сокетов

Создание подключения сокета - клиента к серверу-сокету выполня­ется с помощью функции

socket_connect($socket, $address [, $port]);

где $socket — это сокет - клиент, участвующий в соединении, $address — IP-адрес сервера-сокета, к которому нужно подключиться, а необязательный параметр $port — это порт сервера. Хотя параметр $port ие обязателен в прототипе функции, при подключении доменов AF_INET или AF_INET6 он должен присутствовать. Функция подклю­чает сокет- клиент к указанному серверу или возвращает булевское зна­чение false.

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

socket_write($socket, $buffer [, $length]);

Здесь $socket — это сокет для записи данных, переданных в пара­метре $buffer. Третий параметр $length может быть ука­зан, если не надо записывать весь буфер. Функция отправляет содержи­мое буфера через подключенный сокет и возвращает коли­чество запи­санных байт либо значение false в случае ошибки.

Для чтения данных применяется функция

socket_read($socket,$length [, $type]);

где $socket — это сокет, из которого нужно прочитать максимум $length байт. Необя­зательный параметр $type принимает следующие зна­чения:

$type =PHP_BINARY_READ -интерпретировать данные как бинар­ные (по умолча­нию).

$type =PHP_NORMAL_READ-читать данные заданной длины либо пока не встретится символ новой строки (\г или \n).

Пример. Извлечение Web-сайта с помощью сокетов

<?php

$address = "127.0.0.1";

$port =80;

$socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);

socket_connect($socket, $address, $port);

socket_write($socket, "GET /index.php HTTP/1.1\n\n");

$result = "";

while($read = socket_read($socket, 1024))

{ Sresult .= Sread;}

echo "Полученный результат: '$result'\n";

socket_close($socket);

?>

Создание серверных сокетов

Создание серверного сокета состоит из нескольких шагов. Первый шаг – создать со­кет с помощью функции socket_create(). Второй шаг— привязка сокета к адресу и порту, с помощью функции socket_bind($socket, $address [, $port]);

После привязки сервер-сокета привязан к хосту, он должен быть настроен на прослу­шивание трафика с помощью функции

socket_listen($socket [, $backlog]); Необязательный параметр $backlog используется для создания очереди посредством указания максимально допустимого числа подключений клиентов. Если этот параметр не указан, то сокет, пы­тающийся подключиться, получит отказ в обслуживании, пока серверный сокет недосту­пен. В результате выполнения функция возвращает булевское значение, указывающее на успешность настройки серверного сокета на прослушивание.

Последний шаг в создании серверного сокета — дать команду на прием входящих подключений. Это делается функцией socket_accept($socket);

где $socket — сервер-сокет, включенный на прослушивание, который должен прини­мать соединения. Как только соединение будет установлено, функция вернет новый со­кетный ресурс, используемый для подключения. Если указанный в параметре $socket со­кет настроен как неблокирующий, функция socket_accept() всегда будет возвращать false. Сокетный ресурс, возвращенный функцией socket_accept (), не может быть повторно ис­пользован, поскольку он обслуживает только одно определенное текущее подключение. Сокет, переданный ей в параметре $socket, может быть использован повторно. Пример. Создание простого сервера на основе сокета

<?рhр

/* Запретить прерывание сценария по тайм-ауту */

set_time_limit(0);

$address = "127.0.0.1";

$port = 4545;

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

socket_bind($socket, $address, $port);

socket listen($socket);

$connection = socket__accept ($socket);

$result = trim(socket_read($connection, 1024));

echo "Принят результат: '$result'\n";

socket_close($connection);

socket_shutdown($socket) ;

socket_close($socket) ;

?>

Одновременная работа с несколькими сокетами

Сервер сокетов должен работать одновременно с множеством сокетов-клиентов. Чтобы сделать это, понадобится функция:

socket_select($read, $write, $$error, $sec [, $usec]);

Здесь $read, $write и $error — переданные по ссылке массивы, которые должны содержать список всех сокетов, наблюдаемых на предмет чтения, записи и перехвата ошибок соответственно. Например, помещение активного сокета в массив, передаваемый в параметре $read, заставляет РНР проверять, есть ли в этом сокете данные для чтения. Последние два параметра — $sec и необязательный $usec — это значения тайм-аута, управляющие тем, как долго будет ожидать функция socket_select (), прежде чем вернуть управление РНР. Функция socket_select() возвращает целое число, указывающее общее количество измененных сокетов из переданного списка. Функция модифицирует массивы $read, $write и $error, удаляя из них те элементы, которые не были изменены. В результате каждый из этих массивов будет содержать только список сокетов, от­вечающих следующим требованиям:

Сокеты, перечисленные в массиве $read, содержат данные, подлежащие чтению из них, либо входящие подключения к ним.

Сокеты, перечисленные в массиве $write, содержат данные, подлежащие записи в них.

Сокеты, перечисленные в массиве Serror, содержат ошибки, которые нужно обработать.

В случае ошибочного завершения socketselect () возвращает значение false.

Для использования этой функции в реальном приложении первым делом должен быть создан сокет, представляющий сервер в целом. Этот главный сокет будет привязан к оп­ределенному адресу и порту и начнет прослушивание подключений. Этот сокет будет также добавлен в массив $read и будет запущен управляемый бесконечный цикл. Затем с помощью функции socketselect () будет организован мониторинг главного сокета на пред­мет новых подключений. Когда появляется новое подключение, автоматически вызыва­ется функция socket_accept (), что приводит к созданию нового серверного сокета, исполь­зуемого для взаимодействия с подключенным клиентом. Этот новый подключенный сокет затем подвергается мониторингу через тот же вызов socket_select () (за счет добавления его к тому же массиву, куда уже добавлен наш главный сокет) и реализуется логика при­ложения, обеспечивающая функциональность сервера.

Пример. Создание многосокетного сервера на РНР

<?php

set_time_limit (0) ;

$NULL = NULL;

$address = "127.0.0.1";

$port = 4545;

$max_clients = 10;

$client_sockets = array();

$master = socket_create(AF_INET, SOCK_STREAM, SOLJTCP);

$res = true;

$res s= @socket_bind(Smaster, Saddress, Sport);

$res S= @socket_listen(Smaster); if (! Sres) {

die("Невозможно привязать и прослушивать Saddress:Sport\л");

}

$abort = false;

$read = array(Smaster);

while(!Sabort) { $num_changed = socket_select(Sread, SNULL, SNULL, 0, 10);

/* Изменилось что-нибудь? */

if(Snum_changed) {

/* Изменился ли главный сокет (новое подключение) */

if(in_array(Smaster, Sread)) ( if(count($client_sockets) < $max_clients) { $client_sockets[) - socket_accept(Smaster);

echo "Принято подключение (" . count($client_sockets) . " of $max_clients)\n";

1

)

/* Цикл по всем клиентам с проверкой изменений в каждом из них */ foreach($client_sockets as $key => Sclient) ( /* Новые данные в клиентском сокете? Прочи­тать и ответить */

if(in_array($client, $read)) {

Sinput = socket_read($client, 1024);

if(Sinput === false) (

socket_shutdown($client);

unset($client_sockets[$key]); ) else {

$input = trim($input);

if(!@socket_write(Sclient, "Высказали: $input\n")) ( socket_close($client); un­set($client_sockets(Skey]);

}

}

if(Sinput == 'exit') ( socket_shutdown{Smaster); Sabort = true;

}

}

}

}

$read = $client_sockets;

$read[] = $master;

}

?>

Функция fsockopen()

Функция fsockopen() предназначена для соединения клиента с сервером. Она является универсальной и объединяет в себе функции, которые есть в PHP для работы с сокетами. Функция имеет следующий синтаксис:

int fsockopen (string target, int port[, int errno[, string errstr [, float timeout]]])

Первый аргумент функции представляет собой URL хоста target, с которым необхо­димо соединиться, второй — номер порта port по которому произойдет соединение. Не­обязательный аргумент errno указывает на номер ошибки, а errstr содержит текст сообще­ния об ошибке. Аргумент timeout позволяет указать количество секунд, в течение кото­рого устанавливается соединение.

В примере мы разработаем простое приложение, позволяющее пользователю узнать на кого и где зарегистрирован IP-адрес. Для этого мы с помощью функции fsockopen ус­тановим соединение с сервером whois.ripe.net, как это показано ниже:

<html>

<head>

<title>IP information</title>

</head>

<body>

<center>

<FORM action=ip.php method=post>

<br>

</i></b></font>

<br>

<INPUT type=text name=ip size=35>

<input type=submit value='Введите IP-адрес'>

</form>

</center>

<?

if ($ip!="")

{

$sock = fsockopen ("whois.ripe.net", 43, $errno, $errstr);

if (!$sock)

{

echo("$errno($errstr)");

return;

}

else

{

fputs ($sock, $ip."\r\n");

while (!feof($sock))

{

echo (str_replace(":",":      ",

fgets ($sock,128))."<br>");

}

}

fclose ($sock);

}

?>

</body></html>