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

Ликбез по сетевым технологиям

DHCP

Вы проснулись после дикой пьянки. Первые ваши слова “кто я?” и “где я?”. Сосед, который не запивал водку пивом, вам сообщает все ваши параметры: кто вы и где. Этот сосед выступает в роли DHCP-сервера. Учтите, что в сети могут быть так называемые “ложные DHCP-сервера”, например жена: на ваш вопроc “кто я?” она выдаст неверную информацию — “алкоголик ты проклятый”. Так что не всегда динамическая выдача параметров безопасна, рекомендуется записывать свои параметры (как зовут, ваш адрес и т.д.) на бумажке.

Маршрут по умолчанию

Подойтите к прохожему и спросите “не подскажите ли вы как пройти к моргу имени Невмировича-Данченко?”. С большой долей вероятности вас пошлют. Так вот это и есть маршрут по умолчанию, другими словами если адрес назначения не известен, то пакеты посылаются на маршрут по умолчанию (синонимы: шлюз по умолчанию, default gateway).

Понятие TTL

Представьте себе, что вам 5 лет и вы хотите кушать. Вы идете к папе и говорите: “Папа, я хочу кушать”. Ваш папа смотрит телевизор, согласно таблице маршрутизации он посылает вас к маме. Вы идете к ней и просите “Мамааа, я хочу кушать”. Мама болтает с подругой по телефону и согласно своей таблице маршрутизации посылает вас к папе. И так вы ходите как дурак от папы к маме и обратно, туда-сюда, туда-сюда, а все потому что криворукие админы (родители папы и мамы) неправильно настроили таблицу маршрутизации. Чтобы защититься от таких ситуаций придумали понятие TTL (Time To Live), что применительно к нашей ситуации означает количество терпения у мальчика, пока он не скажет “заколебало” и не упадет перед ногами мамы или папы в беспомощном состоянии. Последний, по правилам (стандарты - это “так заведено в семье”), обязан послать короткий нелестный отзыв в адрес того, кто послал мальчика кушать. Это так называемый icmp-пакет “мальчик издох”.

Ping

Вы конечно бывали в ситуации “сам дурак”. Вы кричите “Петя ты, дурак”, а в ответ слышите “Вася, сам дурак”. Это простеший пинг. Вы только что пропинговали Петю. Не все отвечают на пинги, особо культурные, например Microsoft.com не утруждают себя реагированием на ваши запросы. С такими переругиваться бесполезно, мы знаем, что они слышат и злятся, но реакции добиться не можем. Тем не менее, пинг - неплохой способ узнать жив ли хост, ведь пиная труп ногами не добьешься реакции “сам дурак”.

Traceroute

Представь себе, что ты живешь на 9-м этаже и хочешь узнать всех жильцов которые живут от тебя до Клавки с 3-го. Ты берешь взрывпакет и, исходя из формулы свободного падения, рассчитываешь время взрыва пакета над 8-м этажом. Это TTL=1. После того как пакет взорвется - выглянет озверевшая рожа соседа с 8-го этажа. Время реакции зависит от загруженности сервера, т.е. от занятости соседа и от шейпов, т.е. в воздухе ли ваша система или ты живешь на планете, где атмосфера жидкий азот. Так вот, если вообще не дождешься ответа - твой сосед глухой - у него запрещены icmp ответы, либо он запретил их только для тебя если его уже заколебали твои финты и он научился тебя игнорировать. Дальше выставляешь TTL=2 и т.д. Не забывай, что если Клавка живет выше тебя - это No route to host.

Работа с архивами средствами PHP

На http://www.phpclasses.org можно скачать класс Create ZIP File, предназначенный для создания zip-файлов с иерархией папок. Пример использования:

<?php
// Подключаем класс для работы с zip-архивами
include_once("createZip.inc.php");
$createZip = new createZip; 

// Добавляем директорию
$createZip -> addDirectory("dir/");

// Добавляем файл
$fileContents = file_get_contents("img.jpg")
$createZip -> addFile($fileContents, "dir/img.jpg")

// Создаем архив
$fileName = "archive.zip";
$fd = fopen ($fileName, "wb");
$out = fwrite ($fd, $createZip -> getZippedfile());
fclose ($fd);

// Отдаем архив браузеру
$createZip -> forceDownload($fileName);
@unlink($fileName);
?>

Функция для извлечения файлов из архива:

<?php
/**
 * Распаковывает zip-архив в указанный каталог.
 * $dir  - полный путь к каталогу.
 * $file - полный путь к zip-архиву.
 * $log  - флаг вывода диагностики.
 */

function unpackZip($dir, $file, $log=0) {
    if ($log) echo "Start unpack [$file] into [$dir] directory... <br/>";
 
    if ($zip = zip_open($file)) {
        if ($zip) {
            // создать каталог, если он не существует
            if (! file_exists($dir)) mkdir($dir);
 
            while ($zip_entry = zip_read($zip)) {
                $name = zip_entry_name($zip_entry);
                if ($log) echo "   <b>unpack </b>: $name <br/>";
 
                // последний символ имени
                $last = substr($name, strlen($name)-1);
 
                // если это каталог
                if ($last == "/" || $last == "\\") {
                    $subdir = $dir."/".$name;
                    // создать каталог, если он не существует
                    if (! file_exists($subdir)) mkdir ($subdir);
                } else {
                    // распаковать файл
                    if (zip_entry_open($zip, $zip_entry, "r")) {
                        $buf = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
                        $fp = fopen($dir."/".zip_entry_name($zip_entry), "w");
                        fwrite($fp, $buf);
                        zip_entry_close($zip_entry);
                    } else {
                         return false;
                    }
                }
            }
            zip_close($zip);
        }
    } else {
        return false;
    }
 
    return true;
}
?>

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

Каталог товаров

Итак, каталог продукции некоторой фирмы. Для хранения информации о товарах и категориях используем две таблицы: categories и products. Категория может содержать вложенные категории и/или товары. Проводя аналогию с файловой системой, директория может содержать вложенные директории и/или файлы. Каждый элемент таблиц categories и products содержит ссылку на родительский элемент - parent.

Таблица categories:
id_ctg - уникальный идентификатор категории
parent - ссылка на родительский элемент
title - название категории
sortorder - порядок сортировки

Таблица products:
id_prd - уникальный идентификатор товарной позиции
parent - ссылка на родительский элемент
title - наименование товарной позиции
description - описание товара
sortorder - порядок сортировки

Таблица categories:

1;0;Извещатели охранные;1
2;0;Извещатели пожарные;2
3;0;Приборы приемно-контрольные;3
6;1;Извещатели охранные магнитоконтактные;1
7;1;Извещатели охранные электроконтактные;2
8;1;Извещатели охранные ударноконтактные;3
9;2;Извещатели пожарные тепловые;1
10;2;Извещатели пожарные дымовые;2
11;2;Извещатели пожарные комбинированные;3
17;3;Приборы приемно-контрольные охранные;1
18;3;Приборы приемно-контрольные пожарные;2
19;3;Приборы приемно-контрольные охранно-пожарные;3

Таблица products:

1;6;Извещатель охранный магнитоконтактный ИО 102-2;Технические характеристики ИО 102-2;1
2;6;Извещатель охранный магнитоконтактный ИО 102-4;Технические характеристики ИО 102-4;2
3;6;Извещатель охранный магнитоконтактный ИО 102-14;Технические характеристики ИО 102-14;3
4;7;Извещатель охранный электроконтактный ИО 201-1;Технические характеристики ИО 201-1;1
5;7;Извещатель охранный электроконтактный ВПК 2112;Технические характеристики ВПК 2112;2
6;8;Извещатель охранный ударноконтактный "Окно-4";Технические характеристики "Окно-4";1
7;8;Извещатель охранный ударноконтактный "Окно-5";Технические характеристики "Окно-5";2
8;8;Извещатель охранный ударноконтактный "Окно-6";Технические характеристики "Окно-6";3
9;9;Извещатель пожарный тепловой ИП 114-01;Технические характеристики ИП 114-01;1
10;9;Извещатель пожарный тепловой ИП 101-1A;Технические характеристики ИП 101-1A;2
11;9;Извещатель пожарный тепловой ИП 101-30;Технические характеристики ИП 101-30;3
12;10;Извещатель пожарный дымовой ИП 212-3СМ;Технические характеристики ИП 212-3СМ;1
13;10;Извещатель пожарный дымовой ИП 212-18СИ;Технические характеристики ИП 212-18СИ;2
14;11;Извещатель пожарный комбинированный ИП 212/101-78-А1;Технические характеристики ИП 212/101-78-А1;1
15;11;Извещатель пожарный комбинированный ИП 212/101-18 А3R1;Технические характеристики ИП 212/101-18 А3R1;2
и т.д.

Скрипт каталога товаров:

<?php
$dblocation = "localhost";   // Имя сервера
$dbuser     = "root";        // Имя пользователя
$dbpswrd    = "";            // Пароль
$dbname     = "catalog";     // Имя базы данных

// Соединение с сервером базы данных
$dblink = mysql_connect( $dblocation, $dbuser, $dbpswrd );
mysql_query( 'SET NAMES cp1251' );
// Выбираем базу данных
mysql_select_db( $dbname, $dblink );

if ( isset( $_GET['action'] ) ) {
  $action = $_GET['action'];
  $actions = array( 'showctg', 'showprd' );
  if ( !in_array($action, $actions) ) $action = 'showctg';
} else {
  $action = 'showctg';
}

if ( isset( $_GET['id_ctg'] ) ) {
  $id_ctg = (int)$_GET['id_ctg'];
  if ( $id_ctg < 0 ) $id_ctg = 0;
} else {
  $id_ctg = 0;
}
   
if ( isset( $_GET['id_prd'] ) ) {
  $id_prd = (int)$_GET['id_prd'];
  if ( $id_prd < 0 ) $id_prd = 0;
} else {
    $id_prd = 0;   
}

?>
<html>
<head>
<title>Каталог товаров</title>
</head>
<body>
<table border="1" cellpadding="4" cellspacing="0">
<tr><td colspan="2"><h1>Логотип компании</h1></td></tr>
<tr valign="top">
<td width="30%">
<?php
  // В левой части страницы выводим меню каталога
  // Сначала получаем путь до выбранной категории
  $path = category_path( $id_ctg );
  $out = process_categories(0, $path, $id_ctg);
  // Выводим меню каталога
  foreach ( $out as $ctg )
  {
    // Делаем небольшой отступ слева - чтобы выводить категории "ступенькой":
    // чем больше уровено вложенности, тем болше отступ
    echo '<div style="margin-left:'.($ctg[2]*20).'px">';
    if ( $id_ctg == $ctg[0] )
      echo '<a href="'.$_SERVER['PHP_SELF'].'?action=showctg&id_ctg='.$ctg[0].'" style="font-weight:bold">'.$ctg[1].'</a>';
    else
      echo '<a href="'.$_SERVER['PHP_SELF'].'?action=showctg&id_ctg='.$ctg[0].'">'.$ctg[1].'</a>';
    echo '</div>';
  }
?>
</td>
<td width="70%">
<?php
  // Выводим главное содержание страницы - в зависимости от значения
  // переменной $_GET['action'] мы выводим либо содержимое категории
  // либо карточку товара
  switch( $action ) {
    case 'showctg':
      // запрашиваем из БД вложенные категории и товары и выводим их на страницу
      get_category( $id_ctg );
      break;
    case 'showprd':
      // запрашиваем из БД конкретный товар и выводим его на страницу
      get_product( $id_prd );
    break;
}
?>
</td>
</tr>
<tr><td colspan="2" align="right">Copyright</td></tr>
</table>
</body>
</html>

<?php
// Функция возвращает список подкатегорий и товаров выбраной категории $id_ctg
function get_category( $id_ctg )
{   
  if ( $id_ctg != 0 ) {
    // Получаем из БД название категории
    $query = 'SELECT title FROM categories WHERE id_ctg='.$id_ctg;
    $res = mysql_query( $query );
    // если такая категория не найдена
    if ( mysql_num_rows( $res ) > 0 ) {
      echo '<h2>'.mysql_result( $res, 0, 0 ).'</h2>';
    } else {
      $id_ctg = 0;
      echo '<h2>Каталог</h2>'."\n";
    }
  } else {
    echo '<h2>Каталог</h2>'."\n";
  }
 
  // Получаем данные о всех подкатегориях текущей категории
  $query = 'SELECT id_ctg, title FROM categories WHERE parent='.$id_ctg.' ORDER BY sortorder';
  $res = mysql_query( $query );
  if ( mysql_num_rows( $res ) > 0 ) {
    echo '<ul>';
    while( $ctg = mysql_fetch_array( $res ) ) {
      echo '<li><a href="'.$_SERVER['PHP_SELF'].'?action=showctg&id_ctg='.$ctg['id_ctg'].'">'.$ctg['title'].'</a></li>';
    }
    echo '</ul>';
  }   
   
  // Получаем данные о всех товарах текущей категории
  $query = 'SELECT id_prd, title FROM products WHERE parent='.$id_ctg.'  ORDER BY sortorder';
  $res = mysql_query( $query );
  if ( mysql_num_rows( $res ) > 0 ) {
    echo '<ul>';
    while( $prd = mysql_fetch_array( $res ) ) {
      echo '<li><a href="'.$_SERVER['PHP_SELF'].'?action=showprd&id_ctg='.$id_ctg.'&id_prd='.$prd['id_prd'].'">'.$prd['title'].'</a></li>';
    }
    echo '</ul>';
  } else {
    echo '<p>Нет товаров в этой категории</p>';
  }
}

// Просмотр подробной информации о товаре (карточка товара)
function get_product( $id_prd )
{
  $query = "SELECT title, description FROM products WHERE id_prd=".$id_prd;
  $res = mysql_query( $query );
  if ( mysql_num_rows( $res ) == 1 ) {
    $prd = mysql_fetch_array( $res );
    // Выводим подробную информацию о товаре
    echo '<h2>'.$prd['title'].'</h2>';
    echo '<div>'.$prd['description'].'</div>';
  } else {
    echo '<p>Товар не найден</p>';
  }
}

// Функция возвращает массив категорий с уровнями вложенности для построения
// навигационной панели слева
function process_categories($level, $path, $id_ctg)
{
  // $level - текущий уровень: 0 для корня каталога, 1 для его подкатегорий, и т.д.
  // $path - путь от корня каталога до выбранной категории
  // $id_ctg - ID выбранной категории
  $out = array();
  $cnt = 0;
  $query = "SELECT id_ctg, title FROM categories WHERE parent=".$path[$level]." ORDER BY sortorder";
  $res = mysql_query( $query );
  while ($row = mysql_fetch_row($res))
  {
    $out[$cnt][0] = $row[0];   // category ID
    $out[$cnt][1] = $row[1];   // category name
    $out[$cnt][2] = $level;    // catagory level
    $cnt++;

    // получить подкатегории?
    if ($level+1 < count($path) && $row[0] == $path[$level+1])
    {
      // рекурсивный вызов функции process_categories
      $sub_out = process_categories($level+1, $path$id_ctg);
      //добавляем подкатегории в конец массива $out
      for ($j=0; $j<count($sub_out); $j++)
      {
        $out[] = $sub_out[$j];
        $cnt++;
      }
    }
  }
  return $out;
}

// Функция возвращает путь до текущей категории
// Возвращает массив $path, содержащий categoryID от корня до текущей категории
// $path[0]=0, $path[1]=5, $path[2]=$currentCategoryID
function category_path( $id_ctg )
{
  // строим путь до категории (каталога)
  $path[0] = $id_ctg;
  $curr = $id_ctg;
  while ($curr)
  {
    $res = mysql_query( 'SELECT parent FROM categories WHERE id_ctg='.$curr );
    if ( mysql_num_rows( $res ) > 0 ) {
      $row = mysql_fetch_row($res);
      $curr = $row[0];
      $path[] = $curr;
    } else {
      $curr = 0;
      $path[0] = 0;
    }
  }
  // теперь переворачиваем массив $path
  $path = array_reverse($path);
  return $path;
}
?>