Javascript-форум (https://javascript.ru/forum/)
-   Серверные языки и технологии (https://javascript.ru/forum/server/)
-   -   База данных PMA и PHP-сайт (Денвер) (https://javascript.ru/forum/server/80463-baza-dannykh-pma-i-php-sajjt-denver.html)

Безнадежный программист 07.06.2020 20:09

База данных PMA и PHP-сайт (Денвер)
 
Вложений: 2
Здравствуйте еще раз! Я пытаюсь решить это - правильно (конкретно для базы данных и ее кодировки) вывести в PHP таблицу с данными, затем организовать с этим же PHP их добавление и удаление по полям ввода (для добавления - вводить все данные для полей, для удаления - лишь id поля).
Как примерно сейчас выглядит БД на PMA показано ниже. Я надеюсь, что здесь все в порядке, хотя если что будет не так - я слушаю. Разрешение постаралась сделать как можно больше - вроде все видно.

А вот с кодом все сложнее, конечно - как уже упоминалось выше, все данные из базы данных вывелись нормально, в кодировке utf-8 BOM, но когда я пытаюсь добавить записи через форму PHP на русском в БД - весь текст деформируется. Советовали изменить AddDefaultCharset или другие кодировки попробовать - лучше не становилось, к сожалению. Я думала, может смена кодировки для текстовых полей в базе данных как-то поможет, но, я так понимаю, что все-таки надо разбираться именно в php.

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

Вот такие коды:

Основная страница с таблицей и кнопками:

<meta http-equiv="content-type" content="text/php; charset=utf-8" />
<?php
  echo "<h1>Создание базы данных</h1><br>";
  echo "<table border=1><tr  align=center><td width=10%><b>Номер</td><td width=30%><b>Название</td><td width=20%><b>Цена</td><td width=20%><b>Количество</td><td width=20%><b>Примечание</td></tr>";
  $sqlhost="localhost"; $sqluser="homeuser"; $sqlpass=""; $bd="TOVAR"; 
  mysql_connect($sqlhost,$sqluser,$sqlpass) or die ("нет доступа!".mysql_error());
  mysql_query("SET NAMES utf8");
  mysql_select_db($bd) or die ("нет соединения".mysql_error());
  $zap="SELECT * FROM tov ORDER BY id";
  $zap_res=mysql_query($zap);
   while (list($id, $name, $cost, $kol, $prim)=mysql_fetch_row($zap_res))
    {
      echo "<tr>  <td>$id</td>   <td>$name</td>   <td> $cost</td>   <td>$kol</td>   <td>$prim</td>  </tr>";
    }
  echo "</table>"; 
?>

</br>
<form action="add.php" method="POST">
  <input type="submit" value="Добавить данные">
</form>

<form method="GET">
  <input type="submit" value="Удалить запись">
  <input type="text" name="id">
</form>


Форма для добавления записи:

<html>

  <head>
    <meta charset="utf-8">
    <title>Add</title>
  </head>

  <body>
    <?php
      if(isset($_POST['id']) && isset($_POST['name']) && isset($_POST['cost']) && isset($_POST['kol']) && isset($_POST['prim'])){
        $sqlhost="localhost"; $sqluser="homeuser"; $sqlpass=""; $bd="TOVAR"; 
        $link = mysqli_connect($sqlhost, $sqluser, $sqlpass, $bd) or die("Ошибка " . mysqli_error($link)); 
    
        $id = htmlentities(mysqli_real_escape_string($link, $_POST['id']));
        $name = htmlentities(mysqli_real_escape_string($link, $_POST['name']));
        $cost = htmlentities(mysqli_real_escape_string($link, $_POST['cost']));
        $kol = htmlentities(mysqli_real_escape_string($link, $_POST['kol']));
        $prim = htmlentities(mysqli_real_escape_string($link, $_POST['prim']));
     
        $query ="INSERT INTO tov VALUES('$id', '$name','$cost', '$kol', '$prim')";     
        $result = mysqli_query($link, $query) or die("Ошибка " . mysqli_error($link)); 
        if($result) {
          echo "Данные добавлены";
        }
        mysqli_close($link);
      }  
    ?>

    <h2>Введите данные для новой записи в базе данных</h2>
    <form method="POST">
      <p>Номер:
        <br>
        <input type="text" name="id" />
      </p>
      <p>Назание:
        <br>
        <input type="text" name="name" />
      </p>
      <p>Цена:
        <br>
        <input type="text" name="cost" />
      </p>
      <p>Количество:
        <br>
        <input type="text" name="kol" />
      </p>
      <p>Примечание:
          <br>
          <input type="text" name="prim" />
      </p>
      <input type="submit" value="Добавить запись">
    </form>

  </body>
</html>


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

laimas 07.06.2020 20:30

Выбрасывайте Денвер, он давно умер, установите вместо него Open Server.

1) В коде нет создания базы, она уже вас создана, а здесь вы заполняете ее таблицу. Создавая базу, сразу указывайте кодировку (при создании таблицы также можно указывать кодировку ее полей).
2) Вместо mysqli_real_escape_string используйте подготовленные запросы и псевдопеременные.
3) При записи в базу не стоит пропускать данные через функцию htmlspecialchars, а тем более через htmlentities. Подумайте и попробуйте ответить сами - почему?

or die("Ошибка " . mysqli_error($link)); - такое только в режиме отладки показывать надо.

Безнадежный программист 07.06.2020 21:27

Цитата:

Сообщение от laimas (Сообщение 525544)
Выбрасывайте Денвер, он давно умер, установите вместо него Open Server.

Да, слышала уже об этом - не могу в другой среде работать на данный момент. Должна работать именно с денвером.

Цитата:

Сообщение от laimas (Сообщение 525544)
1) В коде нет создания базы, она уже вас создана, а здесь вы заполняете ее таблицу. Создавая базу, сразу указывайте кодировку (при создании таблицы также можно указывать кодировку ее полей).

Я знаю. То есть думаете, с этим проблем не видно? Тогда ладно.
Мне не совсем это нужно. Мне надо, чтобы добавление и удаление здесь заработало.

Цитата:

Сообщение от laimas (Сообщение 525544)
2) Вместо mysqli_real_escape_string используйте подготовленные запросы и псевдопеременные.

Хорошо, я посмотрю на эту тему материалы, спасибо.

Цитата:

Сообщение от laimas (Сообщение 525544)
3) При записи в базу не стоит пропускать данные через функцию htmlspecialchars, а тем более через htmlentities. Подумайте и попробуйте ответить сами - почему?

Тоже посмотрю, хорошо.

Цитата:

Сообщение от laimas (Сообщение 525544)
or die("Ошибка " . mysqli_error($link)); - такое только в режиме отладки показывать надо.

Я подумывала о том, чтобы их всех удалить - как-то они мешали в коде частично.

laimas 07.06.2020 23:13

Цитата:

Сообщение от Безнадежный программист
Должна работать именно с денвером.

Почему? Вы, конечно, можете оставаться и в нем, но со времени "панихиды по Денверу" веб технологии шагнули далеко вперед, и вы неизбежно с ними столкнетесь. А Open Server, это такой же локальный сервер как и Денвер, так же не требующий инсталляции, также с возможностью его запуска с переносимых носителей, но имеющий гораздо большие встроенные возможности. Вам рано или поздно придется использовать "готовые инструменты", устанавливают которые обычно с помощью composer. В Open Server он уже установлен и сам сервер обладает хорошей оболочкой командной строки.

Цитата:

Сообщение от Безнадежный программист
Тоже посмотрю, хорошо.

Что? Здесь просто нужно прочесть внимательно еще раз об этих функциях и подумать. База данных может хранить в себе любые "режущие и колющие предметы", а вот когда их "показывают пользователю", пропускают через htmlspecialchars, чтобы как раз "не уколоть" пользователя. Функции htmlspecialchars/htmlentities никак не могут спасти базу от sql-инъекции, а вот доставить хлопот, этого будет хоть отбавляй. Представьте, что вы записали в базу "a < b = ...". Как будут в ней хранится эти данные, если вы их пропустите через htmlspecialchars/htmlentities? Вы даете пользователю возможность искать данные в базе (любыми способами, включая и полнотекстовый поиск). Сможет ли пользователь найти в базе "a < b"?

Здесь дамп тестовой таблицы, можете ее импортировать, запустить этот код и все будет работать. Создавая в РМА таблицу, нужно правильно задать и кодировку сравнения, почитать тут. У вас просто путаница. Стоит только заметить, проверки входных данных нет, это просто легкий пример. На самом же деле вы обязаны их проверять, для этого в РНР есть фильтры.

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h4>Структура таблицы Сотрудники</h4>
<pre>--
-- Структура таблицы `employees`
--

CREATE TABLE `employees` (
  `id` int(11) NOT NULL COMMENT 'Идентификатор',
  `surname` varchar(32) NOT NULL COMMENT 'Фамилия',
  `name` varchar(12) NOT NULL COMMENT 'Имя',
  `post` varchar(24) NOT NULL COMMENT 'Должность'
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='Сотрудники';

--
-- Индексы таблицы `employees`
--
ALTER TABLE `employees`
  ADD PRIMARY KEY (`id`);

--
-- AUTO_INCREMENT для таблицы `employees`
--
ALTER TABLE `employees`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Идентификатор';
COMMIT;</pre>

<h4>Содержание таблицы Сотрудники</h4>

<?
$db = new mysqli('localhost', 'root', '', 'test');
$db->set_charset("utf8");

if($_POST) {
    
    if(isset($_POST['add'])) {
        if($q = $db->prepare("INSERT INTO `employees` (`surname`, `name`, `post`) VALUES (?, ?, ?)")) {
            $q->bind_param("sss", $_POST['surname'], $_POST['name'], $_POST['post']);
            $q->execute();
        }
    }
    
    if(isset($_POST['del']) && $id = (int)$_POST['del']) $db->query("DELETE FROM `employees` WHERE id=$id");
        
}

if($q = $db->query("SELECT * FROM `employees`")) {
    if($q->num_rows) {
?>
<form method="post">
    <table>
        <tr><th>Фамилия</th><th>Имя</th><th>Должность</th><th>Удаление</th></tr>
<?
    while($row = $q->fetch_row()) echo '<tr><td>' . implode('</td><td>',array_map('htmlspecialchars', array_slice($row, 1))) . 
        "</td><td><button type=\"submit\" name=\"del\" value=\"$row[0]\">Удалить</button></td></tr>";
?>
    </table>
</form>
<?        
    } else echo '<p>Таблица Сотрудники не имеет записей</p>';
?>
<h4>Добавление сотрудников</h4>
<form method="post">
    <input name="surname" required="" pattern="[а-яёА-ЯЁ-]{5,32}" placeholder="Фамилия" />
    <input name="name" required="" pattern="[а-яёА-ЯЁ]{4,12}" placeholder="Имя" />
    <input name="post" required="" pattern="[а-яёА-ЯЁ]{5,24}" placeholder="Должность" />
    <button name="add">Добавить</button>
</form>    
<?        
} else echo 'xxxx';
$db->close();
?>


PS. В структуре вашего кода есть один "очень неприятный сюрприз". Я в примере тоже таковой "оставил сознательно". Большой плюс для вас будет, если вы поймете о каком "сюрпризе" речь. Подсказка - поставить себя на место пользователя, вспомнить о своих возможных действиях на странице.

Безнадежный программист 08.06.2020 04:43

Цитата:

Сообщение от laimas (Сообщение 525551)
Почему? Вы, конечно, можете оставаться и в нем, но со времени "панихиды по Денверу" веб технологии шагнули далеко вперед, и вы неизбежно с ними столкнетесь.

Я это понимаю и я бы более не пользовалась Денвером, но в дальнейшем я уже не буду пользоваться только своим ПК, и на ПК, где будут рассматривать и проверять эти задания, просто нету OpenServer-а - только Денвер, как я видела. Хотя если он и там появится, то будет прекрасно (но особо не вериться, уже года 3 прошло - все тоже самое).

Цитата:

Сообщение от laimas (Сообщение 525551)
Что? Здесь просто нужно прочесть внимательно еще раз об этих функциях и подумать.

Я буду потом смотреть и читать уже все сразу. Просто не все здесь понятно.

Цитата:

Сообщение от laimas (Сообщение 525551)
Здесь дамп тестовой таблицы, можете ее импортировать, запустить этот код и все будет работать. Создавая в РМА таблицу, нужно правильно задать и кодировку сравнения, почитать тут.

Ладно, спасибо.

Цитата:

Сообщение от laimas (Сообщение 525551)
PS. В структуре вашего кода есть один "очень неприятный сюрприз". Я в примере тоже таковой "оставил сознательно".

Возможно, я уже догадываюсь, о чем вы, но должно описать не могу. Надеюсь, я это увижу.

laimas 08.06.2020 06:34

Цитата:

Сообщение от Безнадежный программист
в дальнейшем я уже не буду пользоваться только своим ПК, и на ПК, где будут рассматривать и проверять эти задания, просто нету OpenServer-а - только Денвер

Ознакомтесь с описанием этого сервера - вы его можете установить на свою флешку и запускать с нее. Этот сервер может работать и при запущенном Денвер. Если при запуске он попросит что-либо, то только одно - установку нужных "приблуд" от MVC. Также может потребоваться добавить папку сервера в доверенные в антивирус установленный. Если это затруднительно будет сделать, ну тогда сидите на Денвере. Хотя хозяина этого другого ПК не мешало бы со знанием дела убедить - пора забывать о старье, с ним далеко не уедешь. ;)

То что выше запустить, отправить форму, а затем нажать F5. Если на запрос браузера "Повторить" ответить утвердительно, то в базе появится дубликат предыдущей записи. Это и есть "неприятность", которая может возникнуть при отправлении формы естественным путем, при асинхронном обмене данными между сервером и клиентом такой проблемы не возникает. Не пытайтесь защитится очищением массива $_POST, ибо это браузер повторную отправку производит, а не сервер сам себе шлет данные. Один из способов избежать дубликатов, это сделать перенаправление после приема формы. Здесь перенаправление производится "на саму себя", то есть на ту же самую страницу.

Если при приеме формы были обнаружены ошибки заполнения, то нужно сохранить пришедшие данные, чтобы заполнить ими форму, негоже заставлять пользователя вводить все заново. Хранить эти данные, а также информацию какие поля с ошибками заполнены, можно в сессии. Старт/обновление сессии, а также перенаправление, это передача в браузер заголовков, а вывод заголовков должен производиться до любого вывода в браузер. По этой причине прием и обработка формы определена в самом начале кода страницы (работая с UTF, имея кодировку эту же и на странице, нужно сохранять и код страницы в UTF, и обязательно без BOM, так как он станет причиной ошибки, ибо будет выводится до заголовков). Хотя можно буферизировать вывод данных до поры до времени, но в данном случае представленная структура проще.

<?
//стартуем/продолжаем сессию
session_start();
$db = new mysqli('localhost', 'root', '', 'test');
$db->set_charset("utf8");
//опции фильтра проверки полей формы
//объявление массивов под версию PHP >= 5.4
//если будет ниже, будут "маты"
$args = [
    'surname'   =>  [
                'filter'    => FILTER_VALIDATE_REGEXP,
                'options'   => ['regexp' => "/^[а-яё-]{5,32}$/iu"]
                ],
    'name'      => [
                'filter'    => FILTER_VALIDATE_REGEXP,
                'options'   => ['regexp' => "/^[а-яё]{4,12}$/iu"]
                ],
    'post'      => [
                'filter'    => FILTER_VALIDATE_REGEXP,
                'options'   => ['regexp' => "/^[а-яё]{5,24}$/iu"]
                ]
];
//параметры полей формы
$fields = [
    'surname'   => ['label' => 'Фамилия', 'reg' => '^[а-яёА-ЯЁ-]{5,32}$'],
    'name'      => ['label' => 'Имя', 'reg' => '^[а-яёА-ЯЁ]{4,12}$'],
    'post'      => ['label' => 'Должность', 'reg' => '^[а-яёА-ЯЁ]{5,24}$'] 
];
//если запрос формы, массив $_POST не пуст
if($_POST) {
    if(isset($_POST['add'])) { //добавление сотрудника?
        //получаем данные формы, удаляя крайние пробелы у значений
        $post = array_map('trim', array_slice($_POST, 0, -1));
        //фильтруем данные формы
        if(!$error = array_filter(filter_var_array($post, $args), function($v) {return !$v;})) {
            //если проверка фильтрами не вернула пустых значений, все Ок, то пишем в базу
            //подготавливаем запрос, экранированием займется драйвер
            $q = $db->prepare("INSERT INTO `employees` (`surname`, `name`, `post`) VALUES (?, ?, ?)");
            //привязываем переменные (метки) к данным формы
            $q->bind_param("sss", $post['surname'], $post['name'], $post['post']);
            //выполняем запрос
            $q->execute();
        } else $_SESSION['error'] = ['key' => $error, 'post' => array_map('htmlspecialchars', $post)]; //есть ошибки заполнения формы, сохраняем ключи полей с ошибками и данные формы 
    }
    //запрос на удаление?
    if(isset($_POST['del']) && $id = (int)$_POST['del']) $db->query("DELETE FROM `employees` WHERE id=$id"); //если id не 0, выполняем запрос
    
    $db->close();
    //защита от F5 - сброс POST
    header('Location: ' . $_SERVER["PHP_SELF"]);
    exit;    
}
?>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<?
//выборка из таблицы
if($q = $db->query("SELECT * FROM `employees`")) {
    if($q->num_rows) { //есть записи, выводим в таблице с возможностью их удаления
?>
<h4>Содержание таблицы Сотрудники</h4>
<form method="post">
    <table>
        <tr><th>Фамилия</th><th>Имя</th><th>Должность</th><th>Удаление</th></tr>
<?
    while($row = $q->fetch_row()) echo '<tr><td>' . implode('</td><td>',array_map('htmlspecialchars', array_slice($row, 1))) . 
        "</td><td><button type=\"submit\" name=\"del\" value=\"$row[0]\">Удалить</button></td></tr>";
?>
    </table>
</form>
<?        
    } else echo '<p>Таблица Сотрудники не имеет записей</p>';
?>
<h4>Добавление сотрудников</h4>
<!-- Если были ошибки заполнения полей формы, выводим имена этих полей 
Версия РНР должна быть не ниже 5.5.0, иначе будет ругаться на функцию array_column -->
<p><?=$_SESSION['error'] ? 'Ошибки заполнения полей: ' . implode(', ', array_column(array_intersect_key($fields, $_SESSION['error']['key']), 'label')) . '!' : null?></p>
<form method="post">
<?
    //выводим форму добавления сотрудников, если были ошибки при приеме формы, заполняем поля пришедшими значениями
    foreach($fields as $k=>$v):
?>
    <input name="<?=$k?>" required="" pattern="<?=$v['reg']?>" placeholder="<?=$v['label']?>" value="<?=$_SESSION['error']['post'][$k]?>" />
<?
    endforeach
?>
    <button name="add">Добавить</button>
</form>    
<?         
} else echo 'Ошибка';
//удаляем сохраненные в сессии ошибки
unset($_SESSION['error']); 
$db->close();
?>
</body>
</html>


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

micscr 08.06.2020 07:06

Если по кодировкам у вас не получается то гляньте тут.

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

mysql_ расширение вы имеете полное право использовать, если древняя версия php. Но если новая, то или mysqli_ или pdo.

Про проблему редиректа сказали выше.

Безнадежный программист 09.06.2020 18:31

Цитата:

Сообщение от laimas (Сообщение 525571)
Ознакомьтесь с описанием этого сервера - вы его можете установить на свою флешку и запускать с нее. Этот сервер может работать и при запущенном Денвер.

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

Цитата:

Сообщение от laimas (Сообщение 525571)
пора забывать о старье, с ним далеко не уедешь. ;)

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

Цитата:

Сообщение от laimas (Сообщение 525571)
Если на запрос браузера "Повторить" ответить утвердительно, то в базе появится дубликат предыдущей записи. Это и есть "неприятность", которая может возникнуть при отправлении формы естественным путем, при асинхронном обмене данными между сервером и клиентом такой проблемы не возникает.

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

Цитата:

Сообщение от laimas (Сообщение 525571)
Если при приеме формы были обнаружены ошибки заполнения, то нужно сохранить пришедшие данные, чтобы заполнить ими форму, негоже заставлять пользователя вводить все заново.

Действительно не справедливо вышло, но ладно. Я слишком сосредоточилась на проблеме кодировок. Постараюсь больше не забывать.
Спасибо большое снова - вы мне много чего из этого дали понять.

laimas 09.06.2020 18:57

Цитата:

Сообщение от Безнадежный программист
На этих ПК не дают права Администратора и доступ очень ограниченный

Да, это проблема. А приложения то разрабатывают под реальное окружение, а в Open Server достаточно просто выбрать нужно и все.

И потом, ваш код может еще быть как "проба пера", но в реальности такое представить сложно.


Часовой пояс GMT +3, время: 03:18.