Не радуйтесь. Нет тут видеоуроков. Нет и не будет.
Вернее есть один, для примера. Для того, что бы Вы поняли, что видеоуроки - это жуткое зло, хуже придумать просто невозможно.
Потому что:
1. Они лишают обучающегося возможности думать, анализировать и влиять на процесс.
2. Самое главное. Все допущенные в курсе ошибки остаются в нем навсегда.
Вот тут есть более развернутый обзор пагубного влияния таких курсов на незакаленные умы начинающих пограммистов.
Если Вас еще не коснулась эта зараза, сей раздел можно пропустить, так как он может оказаться сложным для понимания. А самое главное - совершенно неинформативным.
Полезным он будет для тех, кто решил, что овладел премудростями PHP, посмотрев пару мультиков а'ля "Профессиональный сайт за один день".
Я скачал первый попавшийся, для того, чтобы показать в действии насколько много ляпов можно допустить в пятиминутном видеокурсе.
Приступим к вскрытию.
Первое, что бросилось в глаза - дата выпуска скрипта.
<?php
/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Название: | PHPru_Auth +
+ ---------------------------------------------------------- +
+ Версия: | 1.0 +
+ Стоимость: | бесплатный скрипт +
+ Требования: | PHP4, sendmail +
+ Платформа: | любая +
+ Язык: | русский +
+ Автор: | Alex (http://www.phpru.net) +
+ Copyright 2003: | PHPru.net™ - All Rights Reserved. +
+ ---------------------------------------------------------- +
+ Создан: | 11 марта 2003 +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */
#######################################################################
То есть на полном серьёзе нам втюхивают скрипт 7-ми летней давности. За 7 лет настолько много всего изменилось, что после этой строчки можно дальше не читать вообще ничего. А не то что использовать это на боевом сайте.
Продолжаем.
session_name("ADMIN_SESS");
session_start();
session_register("access","no_access");
Не так сейчас регистрируются сессионные переменные. Это отголоски самых первых - обкаточных версий языка php4. Вот так это нужно делать:
session_start();
$_SESSION['access'] = NULL;
$_SESSION['no_access'] = NULL;
Дальше.
include ('config.inc');
Тут вообще отдельная история. В скрипт подключается текстовый! файл конфигурации. Да, попытка защитить информацию есть, через .htaccess Но что будет, если по чьей-нибудь оплошности его не окажется в каталоге?
Стоит набрать в адресной строке это http://test1.ru/secret/config.inc и вся конфига на ладони.
А что в ней:
<?
$SERVER = 'test1.ru'; // замените на имя Вашего сервера (ВАЖНО! без http:// )
$LOGIN = "admin"; // не забудьте поменять
$PASSWORD = "admin"; // не забудьте поменять
$ERROR = "<CENTER><BR><FONT COLOR=RED><B>Неверный логин или пароль.<BR>ДОСТУП ЗАКРЫТ!...";
$MAIL = "admin@ruseller.com"; // замените на свое мыло
?>
А в ней открытые, незашифрованные аутентификационные данные. Берите люди добрые.
Вопрос. Почему, коль скоро этот код исполняемый, не сделать расширение файла .php? Тогда никаких проблем с защитой информации, содержимое файла получить почти невозможно.
Дальше пошли.
$ip = getenv("REMOTE_ADDR"); //ip посетителя
Не всегда ip находится в REMOTE_ADDR. Иногда в HTTP_X_FORWARDED_FOR к примеру. Или в HTTP_X_FORWARDED или HTTP_FORWARDED_FOR... Это, вообще-то, нужно сначала уточнить на хостинге, потом уже использовать. Или хотя бы сделать так:
if(($ip = @$_SERVER['HTTP_X_FORWARDED_FOR']) === null)
$ip = $_SERVER['REMOTE_ADDR'];
Ну и с другими также.
Кроме того, зачем гонять функцию getenv(), если можно сразу обратиться к нужному элементу массива $_SERVER?
Поехали дальше.
if (!ereg("$SERVER",$host))
{
if (isset($access))
$access++;
else
$access = 1;
if (($access == 1) or ($access == 10))
{ SendEmail($ip,$br,$ref,$access); }
echo "<BR><BR><CENTER><B><P><FONT COLOR=RED>Доступ закрыт!<BR>
Вход только через <A HREF='".$SERVER."'>".$SERVER.'</A></BODY></HTML>';
exit;
}
Первое. Функция ereg() имеет статус deprecated, то есть не рекомендовано к применению. В последующих версиях она поддерживаться не будет. Это все из-за древней версии скрипта (никаких претензий автору, а вот тот, кто делал урок, должен бы это знать)
Второе. Зачем "$SERVER" в кавычках? Это снижает читабельность и тормозит процесс.
Третье. Переменные зарегистрированы в массиве $GLOBALS, а это дырка.
И вообще, для чего вообще там регулярка? Проверку на соответствие можно осуществить и просто, не рискуя, что может пройти что-то незапланированное.
Вот так это должно выглядеть:
<?php
if(strpos($SERVER, $_SERVER["SERVER_NAME"]) !== false)
{
if (!empty($_SESSION['access']))
$_SESSION['access']++;
else
$_SESSION['access'] = 1;
if ($_SESSION['access'] == 1 or $_SESSION['access'] == 10)
SendEmail($ip,$br,$ref,$access);
echo "<BR><BR><CENTER><B><P><FONT COLOR=RED>Доступ закрыт!<BR>
Вход только через <A HREF='http://".$SERVER."'>".$SERVER.'</A></BODY></HTML>';
exit;
}
Продолжаем движение.
if (isset($out))
{
session_destroy();
session_unset();
unset($out);
?>
<!-- после выхода редирект на главную страницу -->
<script language="JavaScript">window.location="http://<?=$SERVER?>"</script>
<?
exit;
}
Позвольте полюбопытствовать, откуда у нас переменная $out? Нигде ни пол-слова небыло сказано, что нужно включить диррективу register_globals Делать этого правда нельзя ни в коем случае, так как это дыра.
Эту переменную, кстати, можно и не уничтожать, не мешает она ни кому. А вот с сессией поступают обычно наоборот.
И перенаправление посредством js - совсем моветон. А если он отключен?
Шор-теги(<?=$SERVER?>), обычно применяют только ленивые люди. Это чревато.
Вот так корректно:
/* выход, уничтожение сессии */
if (isset($_GET['out']))
{
session_unset();
session_destroy();
// редирект на главную страницу
header('location: http://'. $SERVER);
exit;
}
Продолжаем разговор.
Это зачем? Это уже было выше по коду.
session_register("sess_login","sess_pass");
Еще дальше.
if (isset($admin))
{
$sess_login = md5(trim($login));
$sess_pass = md5(trim($password));
ValidPass($sess_login,$sess_pass);
}
Улыбнуло. Храним мы данные значит в открытом виде. И в текстовом файле. А вот в сессию (которая достаточно надежно скрыта) пишем хэш. И сами же с ним потом работаем. Прячем данные от собственного скрипта, а хакеру - на блюдечке. Бей своих, чтоб чужие боялись. )))
Ну про глобальные переменные уже не стоит говорить.
Ниже смотрим.
if (isset($no_access))
{
$no_access++;
if ($no_access == 4)
SendEmail($ip, $br, $ref, $access);
if ($no_access < 4)
ShowAdmin();
}
else
{
$no_access = 1;
ShowAdmin();
}
Тут, если честно, разбираться лень в логике. Что-то там считается (попытки взлома) и если не так - шлем письмо. Может быть и пошло бы, но опять глобальные переменные.
Ниже вроде особо ничего страшного, если не брать во внимание, что почта формируется с нарушениями. Ни о каком RFC даже и речи нет.
Так что вот. Практически в каждой строчке ляп.
А самое главное - ведь не исправить теперь этого. Потому что курс пошел по рукам. Даже если исправить ляпы в новой версии, тут (и у тех, кто успел скачать) этот срам будет жить вечно.
А посему мы не планируем выпуска видеокурсов. Наши курсы тоже не идеальны, там тоже есть ляпы. Но мы их постоянно (с Вашей помощью) находим и исправляем.
За что отдельное спасибо. Задавайте побольше вопросов - будет толк. И для Вас и для курсов.
А если интересно дальше - добро пожаловать на разбор самых популярных полетов видеокурсов Жени Попова.
Приступим
Последнее время ко мне стали очень часто обращаться люди с просьбой помочь переделать сайт, в котором те или иные проблемы. Вернее обращались и раньше, но в последнее время проблемы стали повторяться с завидным постоянством. Виной тому (как бы помягче сказать) не совсем корректный код, который распространяется в видеокурсах.
Чесслово, это изрядно поднадоело, и я решился исправить эти ошибки один раз и на всегда. Чтобы не повторяться.
А по сему сейчас по косточкам разберу этот самый код и покажу тонкие места. На правах рецензии.
Я не буду касаться компановки и структуры, не мое дело. Каждый пишет, как он хочет. Но грубые логические и семантические ошибки исправим.
Начнем, как водится, с самого начала.
index.php
<?php
include ("blocks/bd.php"); /*Соединяемся с базой*/
$result = mysql_query("SELECT title,meta_d,meta_k,text FROM settings WHERE page='index'",$db);
$myrow = mysql_fetch_array($result);
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta name="description" content="<?php echo $myrow['meta_d']; ?> ">
<meta name="keywords" content="<?php echo $myrow['meta_k']; ?> ">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title><?php echo $myrow['title']; ?></title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<table width="690" border="0" align="center" cellpadding="0" cellspacing="0" bgcolor="#FFFFFF" class="main_border">
<!--Подключаем шапку сайта-->
<? include("blocks/header.php"); ?>
<tr>
<td><table width="690" border="0" cellspacing="0" cellpadding="0">
<tr>
<!--Подключаем левый блок сайта-->
<? include ("blocks/lefttd.php"); ?>
<td valign="top">
<?php echo $myrow['text']; ?>
</td>
</tr>
</table></td>
</tr>
<!--Подключаем нижний графический элемент-->
<? include ("blocks/footer.php"); ?>
</table>
</body>
</html>
Итак.
<?php
include ("blocks/bd.php"); /*Соединяемся с базой*/
Первое, не плохо бы знать, что по английски "база данных" звучит как "database", а значит подключаемый файл должен называться db.php, а не bd.php.
Оно на работоспособность вроде как не влияет, но звучит глупо. И говорит о уровне исполнителя. Если придется писать сайт за деньги, это непростительно.
Второе. Путь нужно указывать явно. Если считаем от корня сайта, то так: './path/file.php'; И хотя демократичный PHP прощает такие неточности, злоупотреблять не по-христиански.
Третье. Конструкция include не требует скобок. Они создают избыточность кода.
Четвертое. Литеральные константы (в миру - строки) лучше обрамлять апострофами, а не кавычками. Ибо они быстрее и читабельнее.
В итоге корректно эта строка должна выглядеть так:
<?php
include './blocks/db.php'; /*Соединяемся с базой*/
Но это грех не смертный, с опытом приходит умение писать красиво.
А вот дальше уже серьёзнее. Откроем этот файл со смешным названием:
bd.php
<?
$db = mysql_connect ("localhost","php","12345");
mysql_select_db("phpsite",$db);
?>
Не нужно лениться. Не нужно пользоваться short-тегами. Их вообще скоро выведут из употребления, так как они пересекаются с XML. Писать нужно только так: <?php
Ну и коль скоро зашла речь о них, то закрывающий значек в конце файла PHP ?> нельзя ставить ни в коем случае. Это не просто вредно, но и опасно.
Объясню, чтоб не быть голословным.
1. После него может оказаться пробел или перенос строки. А так, как файл подключен выше доктайпа, значит и пробел вылезет туда. А это гарантированно угробит верстку в IE.
2. Всё, что написано после него, интерпретируется как HTML. А это значит, что (не дай Бог) в конец файла попадет вирус, то он сработает.
А тупые боты именно туда норовят прописать всякую гадость. Так вот, ежели его (закрывающего значка) не будет, то вирь попадет в тело php и вызовет ошибку. Плохо конечно, но лучше, чем бы он сработал у клиента.
Продолжаем разговор.
Тут мы вроде спохватились, и пишем правильно - $db. Ура. Однако на радостях почему то упускаем такой важный момент, как кодировка соединения. И это часто вызывает панику среди мирного населения - мол почему у меня из базы лезут кракозяблы???
Поправим и переименуем файл:
db.php
<?php
$db = mysql_connect ('localhost','php','12345');
mysql_select_db('phpsite',$db);
mysql_query('SET NAMES cp1251',$db);
mysql_query('SET CHARACTER SET cp1251',$db);
mysql_query('SET COLLATION_CONNECTION="cp1251_general_ci"',$db);
И хотя мне чужды и противны по религиозным соображениям местечковые кодировки, из песни слов не выкинуть.
Поехали обратно в индекс.
$result = mysql_query("SELECT title,meta_d,meta_k,text FROM settings WHERE page='index'",$db);
$myrow = mysql_fetch_array($result);
Начали красиво, пусть даже без обратных кавычек и в одну строку - вполне допустимо. Но вот продолжение обескураживает.
Прежде чем использовать функцию, стоит заглянуть в мануал, а не копипастить из первого попавшего источника. Дело в том, что функция mysql_fetch_array() возвращает два массива. Нумерованный и ассоциативный. А используется только один.
Для чего брать в одну руку ложку, а в другую вилку, если мы собираемся есть суп?
Если уж использовать её, то с подобающим флагом (MYSQL_ASSOC). А вообще тут уместнее mysql_fetch_assoc()
<?php
$result = mysql_query("SELECT title,meta_d,meta_k,text FROM settings WHERE page='index'",$db);
$myrow = mysql_fetch_assoc($result);
А дальше просто страшно. Такие дыры совсем непростительны:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta name="description" content="<?php echo $myrow['meta_d']; ?> ">
<meta name="keywords" content="<?php echo $myrow['meta_k']; ?> ">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title><?php echo $myrow['title']; ?></title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
На первый взгляд ничего особенного, однако попробуйте через PhpMyAdmin выполнить такой запрос
UPDATE `settings` SET `meta_d` = '"><script>location.href="http://sex.ru";</script>' WHERE `id` = 1
и зайти на главную страницу сайта. Обещаю незабываемые впечатления )).
Как это может попасть в базу, я не покажу по известным причинам. Но поверьте, это не так уж сложно на таком сайте. Называется сие - XSS или межсайтовый скриптинг.
Так вот, чтобы такого никогда не было, нужно всегда обрабатывать данные перед выводом в поток функцией htmlspecialchars()
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta name="description" content="<?php echo htmlspecialchars($myrow['meta_d']); ?> ">
<meta name="keywords" content="<?php echo htmlspecialchars($myrow['meta_k']); ?> ">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title><?php echo htmlspecialchars($myrow['title']); ?></title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
Дальше уже по известному сценарию. Вот так с натяжечкой пойдет:
<?php
include './blocks/db.php'; /*Соединяемся с базой*/
$result = mysql_query("SELECT `title`, `meta_d`, `meta_k`, `text`
FROM `settings`
WHERE `page` = 'index'", $db);
$myrow = mysql_fetch_assoc($result);
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta name="description" content="<?php echo htmlspecialchars($myrow['meta_d']); ?> ">
<meta name="keywords" content="<?php echo htmlspecialchars($myrow['meta_k']); ?> ">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title><?php echo htmlspecialchars($myrow['title']); ?></title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<table width="690" border="0" align="center" cellpadding="0" cellspacing="0" bgcolor="#FFFFFF" class="main_border">
<!--Подключаем шапку сайта-->
<?php include './blocks/header.php'; ?>
<tr>
<td><table width="690" border="0" cellspacing="0" cellpadding="0">
<tr>
<!--Подключаем левый блок сайта-->
<?php include './blocks/lefttd.php'; ?>
<td valign="top">
<?php echo htmlspecialchars($myrow['text']); ?>
</td>
</tr>
</table></td>
</tr>
<!--Подключаем нижний графический элемент-->
<?php include './blocks/footer.php'; ?>
</table>
</body>
</html>
Отличия ищите сами.
С первым покончили... Дальше - больше. Как говорится, чем дальше в лес, тем толшэ партизаны.
Админка
А вот тут, господа хорошие, пора взяться за голову. Но по порядку, чтобы сразу не шокировать.
Начнем опять же с индекса. Того, который в админке.
<? include ("lock.php"); ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Главная страница блока администратора</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<table width="690" border="0" align="center" cellpadding="0" cellspacing="0" bgcolor="#FFFFFF" class="main_border">
<!--Подключаем шапку сайта-->
<? include("blocks/header.php"); ?>
<tr>
<td><table width="690" border="0" cellspacing="0" cellpadding="0">
<tr>
<!--Подключаем левый блок сайта-->
<? include ("blocks/lefttd.php"); ?>
<td valign="top">
<p>Добро пожаловать в админский блок.</p>
</td>
</tr>
</table></td>
</tr>
<!--Подключаем нижний графический элемент-->
<? include ("blocks/footer.php"); ?>
</table>
</body>
</html>
Ничего нового, все исправляем по образу и подобию:
<?php include './lock.php'; ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Главная страница блока администратора</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<table width="690" border="0" align="center" cellpadding="0" cellspacing="0" bgcolor="#FFFFFF" class="main_border">
<!--Подключаем шапку сайта-->
<?php include './blocks/header.php'; ?>
<tr>
<td><table width="690" border="0" cellspacing="0" cellpadding="0">
<tr>
<!--Подключаем левый блок сайта-->
<?php include './blocks/lefttd.php'; ?>
<td valign="top">
<p>Добро пожаловать в админский блок.</p>
</td>
</tr>
</table></td>
</tr>
<!--Подключаем нижний графический элемент-->
<?php include './blocks/footer.php'; ?>
</table>
</body>
</html>
Но самое интересное только начинается. Что мы видим первой строчкой. А видим мы подключение файла с красноречивым названием "loсk", то бишь "замок".
Посмотрим, на сколько он крепок:
<?php
include("blocks/bd.php");
if (!isset($_SERVER['PHP_AUTH_USER']))
{
Header ("WWW-Authenticate: Basic realm=\"Admin Page\"");
Header ("HTTP/1.0 401 Unauthorized");
exit();
}
else {
if (!get_magic_quotes_gpc()) {
$_SERVER['PHP_AUTH_USER'] = mysql_escape_string($_SERVER['PHP_AUTH_USER']);
$_SERVER['PHP_AUTH_PW'] = mysql_escape_string($_SERVER['PHP_AUTH_PW']);
}
$query = "SELECT pass FROM userlist WHERE user='".$_SERVER['PHP_AUTH_USER']."'";
$lst = @mysql_query($query);
if (!$lst)
{
Header ("WWW-Authenticate: Basic realm=\"Admin Page\"");
Header ("HTTP/1.0 401 Unauthorized");
exit();
}
if (mysql_num_rows($lst) == 0)
{
Header ("WWW-Authenticate: Basic realm=\"Admin Page\"");
Header ("HTTP/1.0 401 Unauthorized");
exit();
}
$pass = @mysql_fetch_array($lst);
if ($_SERVER['PHP_AUTH_PW']!= $pass['pass'])
{
Header ("WWW-Authenticate: Basic realm=\"Admin Page\"");
Header ("HTTP/1.0 401 Unauthorized");
exit();
}
}
?>
Впечатляет. Куча грозных проверок, обработка данных... Но опять же писано это полнейшим дилетантом. Особенно это место:
if (!get_magic_quotes_gpc()) {
$_SERVER['PHP_AUTH_USER'] = mysql_escape_string($_SERVER['PHP_AUTH_USER']);
$_SERVER['PHP_AUTH_PW'] = mysql_escape_string($_SERVER['PHP_AUTH_PW']);
}
Чувствуется рука мастера. Логика железная - если директива magic_quotes_gpc выключена, то данные мы обработаем. Не ясно зачем обе переменных, если в запрос идет только одна, ну да не суть. Суть в другом. А если включена?
Немного экскурса. Вот эти буквы magic_quotes_gpc обозначают ни что иное, как GET POST COOKIE И ни полслова про SERVER. А ведь именно оттуда мы берем данные. Так вот их эта директива не касается.
Я долго искал файл .htaccess, где могла бы быть отключена эта директива. Не нашел. А значит мы становимся заложниками настроек сервера.
Повезло тому, у кого на хостинге эта директива отключена. Но многие, и даже весьма крупные (AGAVA к примеру), держат её в положении On
Чем это грозит. Чтобы понять весь трагизм положения, проведем эксперимент. Поместите в корень сайта файл .htaccess такого содержания:
php_flag magic_quotes_gpc On
То есть мы принудительно включим эту директиву, смоделировав такую ситуацию. А теперь фокус-покус. Набираем в поле "логин" такой текст:
1' union select 1/*'
а в поле "пароль" 1(единицу). И - о, чудо! Алчно потирая руки мы заходим в святая святых - админку.
Я не буду исправлять этот скрипт. Вот тут все подробненько описано.
Ну а мы двинемся дальше.
Новый урок
Ну, коль скоро мы проникли в админку - посмотрим, что есть там. Первая ссылка в меню - "добавить урок". Ну добавить, так добавить. Сейчас я это и сделаю.
И так, новый урок:
Инициализация переменных.
Есть разные способы инициализации переменных - тернарным оператором, простым условием, с собачкой и даже в цикле. Но вот это:
admin/add_lessons.php
if (isset($_POST['title']))
{
$title = $_POST['title'];
if ($title == '')
{
unset($title);
}
}
/* Если существует в глобальном массиве $_POST['title'] опр. ячейка, то мы создаем простую переменную из неё. Если переменная пустая, то уничтожаем пе
ременную. */
if (isset($_POST['meta_d'])) {$meta_d = $_POST['meta_d']; if ($meta_d == '') {unset($meta_d);}}
if (isset($_POST['meta_k'])) {$meta_k = $_POST['meta_k']; if ($meta_k == '') {unset($meta_k);}}
if (isset($_POST['date'])) {$date = $_POST['date']; if ($date == '') {unset($date);}}
if (isset($_POST['description'])) {$description = $_POST['description']; if ($description == '') {unset($description);}}
if (isset($_POST['text'])) {$text = $_POST['text']; if ($text == '') {unset($text);}}
if (isset($_POST['author'])) {$author = $_POST['author']; if ($author == '') {unset($author);}}
просто верх сюреализма. Замечательно, когда для иллюстрации к уроку есть материалы из серии "так делать нельзя".
Разберемся, что же тут не так. Логика очевидна, к тому же прямо прописана в комментарии:
/* Если существует в глобальном массиве $_POST['title'] опр. ячейка, то мы создаем простую переменную из неё.
Если переменная пустая, то уничтожаем переменную. */
Так а зачем создавать, если тут же уничтожаем? Гораздо логичнее не создавать, если не существует в глобальном массиве $_POST['title'] опр. ячейка или она пуста. Это избавит код от избыточности, сделает его читабельным, а сервер освободит от лишних телодвижений.
Хотябы так (всё почище):
if (isset($_POST['title']) && $_POST['title'] != '')
{
$title = $_POST['title'];
}
if (isset($_POST['description']) && $_POST['description'] != '') $description = $_POST['description'];
if (isset($_POST['meta_d']) && $_POST['meta_d'] != '') $meta_d = $_POST['meta_d'];
if (isset($_POST['meta_k']) && $_POST['meta_k'] != '') $meta_k = $_POST['meta_k'];
if (isset($_POST['author']) && $_POST['author'] != '') $author = $_POST['author'];
if (isset($_POST['date']) && $_POST['date'] != '') $date = $_POST['date'];
if (isset($_POST['text']) && $_POST['text'] != '') $text = $_POST['text'];
Кроме того, есть замечательная функция empty(), которая сразу проверяет пустоту переменной. Прелесть её в том, что она не вызывает ошибку при обращении к несуществующей переменной. По этому (если предположить, что ни одна из переменных не будет равняться нулю), этот код можно еще сократить, используя empty() с инверсией:
if (!empty($_POST['title']))
{
$title = $_POST['title'];
}
if (!empty($_POST['description'])) $description = $_POST['description'];
if (!empty($_POST['meta_d'])) $meta_d = $_POST['meta_d'];
if (!empty($_POST['meta_k'])) $meta_k = $_POST['meta_k'];
if (!empty($_POST['author'])) $author = $_POST['author'];
if (!empty($_POST['date'])) $date = $_POST['date'];
if (!empty($_POST['text'])) $text = $_POST['text'];
Хотя этот подход тоже верным назвать нельзя. Ну ладно тут, коду три строчки. А если скрипт будет посерьёзнее? Каждый раз, где потребуется обращение к переменной, придется проверять её наличие. По этому лучше определить её сразу, чтобы потом не беспокоиться. Тут это не критично, исправлять смысла вроде нет. Кому интересно, в наших курсах это описано.
Дальше, та самая проверка:
if (isset($title) && isset($meta_d) && isset($meta_k) && isset($date) && isset($description) && isset($text) && isset($author))
{
Не могу смотреть на такие веревки без умиления. Сразу представляется старательный кодер, который сопя и пыхтя, высунув язык, самозабвенно выводит эти каракули, вместо того, чтобы заглянуть в мануал. А там черным по белому -
If multiple parameters are supplied then isset() will return TRUE only if all of the parameters are set.
Что в вольном переводе звучит как
Если параметрами передается несколько переменных, то isset () вернет TRUE только если все переменные определены.
И если сделать из этого определенные орг-выводы, то этот канат можно переписать так:
if (isset($title, $meta_d, $meta_k, $date, $description, $text, $author))
{
/* Здесь пишем что можно заносить информацию в базу */
Урок окончен. По инициализации.
А по коду это далеко не все, причем это были семечки. Настоящие ляпы только начинаются.
Я опущу тот момент, что в базу вносится гипертекст (опираясь на это заявление: Введите полный текст урока с тэгами)
Это отдельная тема, тема безопасности. Фиг с ним, тут есть еще на что обратить внимание.
$result = mysql_query ("INSERT INTO lessons (title,meta_d,meta_k,date,description,text,author)
VALUES ('$title', '$meta_d','$meta_k','$date','$description','$text','$author')");
Шедеврально. Наверняка многие, кто собрал свой сайт по этим урокам, сталкивались с такой непонятной проблемой: почему то админка упрямо не хочет воспринимать текст, в котором присутствуют апострофы. Ради интереса попробуйте запостить такой текст урока:
Апостроф, это одинарная кавычка. Выглядит так - '. Находится на клавише с буквой "Э" в латинской раскладке.
при выключенных магических кавычках. Получите без объявления войны радостное сообщение с восклицательным знаком:
Ваш урок не добален!
Это зачатки SQL-инъекции. И можете сто раз повторить, что это админка и бояться некого, однако текст то не прошел. А это ляп.
Никогда, ни при каких обстоятельствах, нельзя совать в запрос неподготовленные данные. Как минимум это должно выглядеть так:
/* Здесь пишем что можно заносить информацию в базу */
$result = mysql_query ("INSERT INTO lessons
(`title`, `meta_d`, `meta_k`, `date`, `description`, `text`, `author`)
VALUES
('". mysql_real_escape_string($title) ."',
'". mysql_real_escape_string($meta_d) ."',
'". mysql_real_escape_string($meta_k) ."',
'". mysql_real_escape_string($date) ."',
'". mysql_real_escape_string($description) ."',
'". mysql_real_escape_string($text) ."',
'". mysql_real_escape_string($author) ."'
)"
);
А в идеале нужно озаботиться этими самыми магическими кавычками и разграничить типы переменных. Тут на этом подробно останавливаться смысла нет. А вот на следующей строчке есть.
if ($result == 'true') {echo "<p>Ваш урок успешно добален!</p>";}
else {echo "<p>Ваш урок не добален!</p>";}
Во первых, с какого перепуга булево значение true превратилось в строку? Благо PHP прощает такую вольность, но до добра эта привычка не доведет.
Должно быть так:
if ($result === true)
А можно проще:
if ($result)
Дальше. По накатанной тропинке идем в мануал, ищем функцию mysql_query() и читаем:
Значение не равное FALSE говорит о том, что запрос был выполнен успешно. Он не говорит о количестве затронутых или возвращённых рядов.
Вполне возможна ситуация, когда успешный запрос не затронет ни одного ряда.
А это значит, что однозначно заявлять с ликованием:
Ваш урок успешно добален!
нет никаких оснований. Есть основание констатировать факт, что функция вернула что то, отличное от false.
Проверяется внесение данных в базу другой функцией - mysql_affected_rows(), которая возвращает количество измененных рядов.
Вобщем вот, смотрите и сравнивайте сами:
admin/add_lessons.php
<?php
include ('./lock.php');
include ('./blocks/db.php');
if (!empty($_POST['title']))
{
$title = $_POST['title'];
}
if (!empty($_POST['description'])) $description = $_POST['description'];
if (!empty($_POST['meta_d'])) $meta_d = $_POST['meta_d'];
if (!empty($_POST['meta_k'])) $meta_k = $_POST['meta_k'];
if (!empty($_POST['author'])) $author = $_POST['author'];
if (!empty($_POST['date'])) $date = $_POST['date'];
if (!empty($_POST['text'])) $text = $_POST['text'];
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Обработчик</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<table width="690" border="0" align="center" cellpadding="0" cellspacing="0" bgcolor="#FFFFFF" class="main_border">
<!--Подключаем шапку сайта-->
<?php include './blocks/header.php'; ?>
<tr>
<td><table width="690" border="0" cellspacing="0" cellpadding="0">
<tr>
<!--Подключаем левый блок сайта-->
<?php include './blocks/lefttd.php'; ?>
<td valign="top">
<?php
if (isset($title, $meta_d, $meta_k, $date, $description, $text, $author))
{
/* Здесь пишем что можно заносить информацию в базу */
$result = mysql_query ("INSERT INTO lessons
(`title`, `meta_d`, `meta_k`, `date`, `description`, `text`, `author`)
VALUES
('". mysql_real_escape_string($title) ."',
'". mysql_real_escape_string($meta_d) ."',
'". mysql_real_escape_string($meta_k) ."',
'". mysql_real_escape_string($date) ."',
'". mysql_real_escape_string($description) ."',
'". mysql_real_escape_string($text) ."',
'". mysql_real_escape_string($author) ."'
)"
);
if (mysql_affected_rows() > 0)
echo "<p>Ваш урок успешно добален!</p>";
else
echo "<p>Ваш урок не добален!</p>";
}
else
{
echo "<p>Вы ввели не всю информацию, поэтому урок в базу не может быть добален.</p>";
}
?>
</td>
</tr>
</table></td>
</tr>
<!--Подключаем нижний графический элемент-->
<?php include './blocks/footer.php'; ?>
</table>
</body>
</html>
А мы продолжим вскрытие.
Редактируем
Следующая ссылка в админке - Редактировать.
Ну что же, воспользуемся советом и отредактируем текущий файл. Потому что редактировать там есть огого сколько.
Итак:
admin/edit_lesson.php
<?php
include ("lock.php");
include ("blocks/bd.php");
if (isset($_GET['id'])) {$id = $_GET['id'];}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<title>Страница добавления нового урока</title>
<link href="style.css" rel="stylesheet" type="text/css">
</head>
<body>
<table width="690" border="0" align="center" cellpadding="0" cellspacing="0" bgcolor="#FFFFFF" class="main_border">
<!--Подключаем шапку сайта-->
<? include("blocks/header.php"); ?>
<tr>
<td><table width="690" border="0" cellspacing="0" cellpadding="0">
<tr>
<!--Подключаем левый блок сайта-->
<? include ("blocks/lefttd.php"); ?>
<td valign="top">
<?
if (!isset($id))
{
$result = mysql_query("SELECT title,id FROM lessons");
$myrow = mysql_fetch_array($result);
do
{
printf ("<p><a href='edit_lesson.php?id=%s'>%s</a></p>",$myrow["id"],$myrow["title"]);
}
while ($myrow = mysql_fetch_array($result));
}
else
{
$result = mysql_query("SELECT * FROM lesson