Nie można zapisać danych w bazie

0

Dzień dobry,
przepisuję różne skrypty i strony firmowe z PHP'a 5 na 7 i napotkałem w końcu na problem, z którym nie jestem w stanie się uporać. Od razu chciałbym napomknąć, że żaden ze mnie programista, tylko sieciowiec typu murarz, tynkarz, akrobata :D!

Poniżej wklejam kod, w którym komentarzami jest wyjaśnione co robi. W skrócie pinguje on hosty w sieci i wrzuca aktualne wyniki do bazy danych, z której dane bierze nasze "drzewo sieciowe" pokazując up/down hostów oraz strukturę sieci:

#!/usr/bin/php
<?php

// sprawdza apki i wrzuca do bazy wyniki

//Czytamy konfig
require("/etc/xxx/xxx");

$db = mysqli_connect($db_host, $db_user, $db_pass, $db_name);

//$fping = "fping -r 3 -t 400";
$fping = "/bin/bin/user/fping ";

$sql = "SELECT ip_apka FROM apki_dane WHERE stansieci_ping=\"T\"";
$result = mysqli_query($db, $sql);

while ($rec = mysqli_fetch_array($result))
{
    $fping .= " $rec[ip_apka]";
}

$wyjscie = explode(";", shell_exec("$fping 2>/dev/null | tr \"\n\" \";\""));
array_pop($wyjscie);

$sql = "INSERT INTO `stan_sieci` (`ip_apka`, `timestamp`, `stan`) VALUES";

while (list ($key, $line) = each ($wyjscie))
{
    $t_line = explode (" ", $line);
// Obcinanie adresu IP do 3 i 4 tego oktetu
//    $ip = substr($t_line["0"],8);
      $ip = $t_line["0"];
    if ($t_line["2"] == "alive") { $alive = "1"; } else { $alive = "0"; }
    if ($key > 0) $sql .= ",";
    $sql .= " (\"$ip\", now(), \"$alive\")";
}

//na czas wykonywania zapytan , tylko my mozemy je wykonywac
mysqli_query($db, "lock table stan_sieci write");
//$result = mysql_query("DELETE from stan_sieci") or die("err1: Nie mozna wyczyscic danych stanu sieci!");
$result = mysqli_query($db, "TRUNCATE TABLE stan_sieci") or die("err1: Nie mozna wyczyscic danych stanu sieci!");
$result = mysqli_query($db, "INSERT INTO `stan_sieci` (`ip_apka`, `timestamp`, `stan`) VALUES") or die("err2: Nie mozna zapisac danych stanu sieci!");
mysqli_query($db, "unlock tables");

?>

Przed odpaleniem skryptu importuję sobie poprzednie dane do bazy danych, aby wszystko poszło po bożemu. Skrypt wykonuje krok pierwszy bez problemu (czyści dane stanu sieci), ale nie umie poradzić sobie z zapisaniem nowych danych ($result = mysqli_query($db, "INSERT INTO stan_sieci (ip_apka, timestamp, stan) VALUES") or die("err2: Nie mozna zapisac danych stanu sieci!");).

Nie robiłem żadnych drastycznych zmian w tym skrypcie, bo w większości wymagane były zmiany mysql na mysqli, więc nie wiem co tutaj może iść nie tak, jeżeli na poprzedniej wersji i innej maszynie działa to bezproblemowo. W logach nic nie widzę.

Z góry dziękuję za pomoc :)!

4

Na razie na szybko widać, że nie robisz wywołania właściwego zapytania.

//wcześniej tworzysz zapytanie z wartościami
$result = mysqli_query($db, "TRUNCATE TABLE stan_sieci") or die("err1: Nie mozna wyczyscic danych stanu sieci!");
//Tutaj robisz Insert ale bez wartości i to wpisane ręcznie
$result = mysqli_query($db, "INSERT INTO `stan_sieci` (`ip_apka`, `timestamp`, `stan`) VALUES") or die("err2: Nie mozna zapisac danych stanu sieci!");
mysqli_query($db, "unlock tables");

Może wystarczy?

//Może powinno być tak? Czyli robisz wywołanie przygotowanego wcześniej zapytania Insert wraz z wartościami,
$result = mysqli_query($db, $sql) or die("err2: Nie mozna zapisac danych stanu sieci!");
0

Próbowałem też wcześniej Twojego sposobu.. Insert w podany przez Ciebie wywalał identyczny błąd :c!

2

@jurek1980: dobrze podpowiada, że zapytanie masz w zmiennej sql, a zapytanie wykonujesz inne,`ale wydaje mi się że je źle budujesz zapytanie

linijka:

$sql .= " (\"$ip\", now(), \"$alive\")";
//wydaje mi się, że powinien być pojedynczy apostrof
$sql .= " ('$ip', now(), '$alive')";

Ostatecznie zobacz co masz za zapytanie w zmiennyj i puść bezposrednio na bazie, zobaczysz błędy

1

matko Boska, nie rób SQLek poprzez złączanie stringów! Toż to niebezpieczne jest!

Po drugie, polecam używanie PDO. Ma ładną składnie i fajnie ogarnia bezpieczne bindowanie danych. Możesz o nim poczytać tutaj: https://phpdelusions.net/pdo

W Twoim przypadku będzie to wyglądać coś na wzór

$sql = "INSERT INTO `stan_sieci` (`ip_apka`, `timestamp`, `stan`) VALUES (?, now(), ?)";
$pdo->prepare($sql)->execute([$ip, $alive]);
0

Dzięki za odpowiedzi.

@Panczo: puszczając zapytanie bezpośrednio na bazie wywalało mi error syntax, prawdopodobnie przez to, że w zapytaniu nie mam żadnych wartości do zainsertowania. Zedytowałem skrypt w taki sposób:

$sql = "INSERT INTO `stan_sieci` (`ip_apka`, `timestamp`, `stan`) VALUES (xxx.xxx, now(), 1)";

while (list ($key, $line) = each ($wyjscie))
{
    $t_line = explode (" ", $line);
//    $ip = substr($t_line["0"],8);
      $ip = $t_line["0"];
    if ($t_line["2"] == "alive") { $alive = "1"; } else { $alive = "0"; }
    if ($key > 0) $sql .= ",";
    $sql .= " ('$ip', now(), '$alive')";
}

//na czas wykonywania zapytan , tylko my mozemy je wykonywac
mysqli_query($db, "lock table stan_sieci write");
//$result = mysql_query("DELETE from stan_sieci") or die("err1: Nie mozna wyczyscic danych stanu sieci!");
$result = mysqli_query($db, "TRUNCATE TABLE stan_sieci") or die("err1: Nie mozna wyczyscic danych stanu sieci!");
$result = mysqli_query($db, $sql) or die("err2: Nie mozna zapisac danych stanu sieci!");
mysqli_query($db, "unlock tables");

i dane, które wpisałem zapisały się w bazie i są odczytywane na schemacie. Na poprzedniej maszynie, na której ten skrypt był wykonywany, działał bezproblemowo (PHP 4.3.11 do bazy na PHP 5.3 bodajże). Tutaj mam do czynienia z PHP 7.0 -> PHP 7.3, więc coś na pewno się pozmieniało. Miałbyś może jakiś pomysł jak to obejść?

1

Nie pomogę bo nie programuje w PHP, ale skoro zapytnie nic nie zwraca to trzeba badać od początku:

  1. czy masz coś w zmiennej $fping
  2. co jest w zmiennej $wyjscie
2

Zobacz coś takiego. Warto by jeszcze sprawdzać czy te otrzymane wartości typu IP są właściwym IP itd. Ale tu masz kolejne pole do popisu.

$wyjscie = [
    '192.168.0.1 jakisnapis alive',
    '192.168.0.2 jakisnapis alive',
    '192.168.0.3 jakisnapis alive'
];
// Ja sobie odwracam tablice żeby ostatni klucz ostatniego elementu był zerem
krsort($wyjscie);
$values = '';
// na wszelki wypadek sprawdzamy czy aby na pewno to $wyjście jest tablicą.
if(is_array($wyjscie)){
    //zmiana na foreach bo each jest już deprecated od PHP7.2
    foreach($wyjscie as $key => $line){
        $t_line = explode(" ", $line);
        $ip = $t_line[0];
        $alive = $t_line[2] === "alive"?1:0;
        $values .= " ('$ip', now(), '$alive')";
        //Przecinek dajemy na końcu za nawiasem
        $key === 0 ?: $values .= ",";
    }
}

$sql = "INSERT INTO `stan_sieci` (`ip_apka`, `timestamp`, `stan`) VALUES ";
// Dla debugowania
print_r($sql.$values);
/*Przygotowane zapytanie wygląda tak
 INSERT INTO `stan_sieci` (`ip_apka`, `timestamp`, `stan`)
 VALUES  ('192.168.0.3', now(), '1'),
 ('192.168.0.2', now(), '1'),
 ('192.168.0.1', now(), '1')
*/

// Insert wykona się tylko jeśli znalazło jakieś wartości
if ($values){
    mysqli_query($db, "lock table stan_sieci write");
    //$result = mysql_query("DELETE from stan_sieci") or die("err1: Nie mozna wyczyscic danych stanu sieci!");
    $result = mysqli_query($db, "TRUNCATE TABLE stan_sieci") or die("err1: Nie mozna wyczyscic danych stanu sieci!");
    $result = mysqli_query($db, $sql.$values) or die("err2: Nie mozna zapisac danych stanu sieci!");
    mysqli_query($db, "unlock tables");
}
0

@Panczo @jurek1980: Dzięki za odpowiedź i pomoc :)!

Adresy IP są na pewno właściwymi adresami, ponieważ brane są z bazy danych z tabeli apki_dane. Twoja zmienna "wyjście" printowała dane, które przykładowo podałeś, lecz dalej występował problem z zapisem danych do bazy.

Próbowałem sobie debugować po kolei każdą rzecz i o to do czego doszedłem (może da coś na dłuższą metę albo sam zreflektuję się i coś wymyślę :P):

  1. Skrypt na sto procent pobiera dane z bazy danych:
$result = mysqli_query($db, $sql);
print_r($result);

wynik:

mysqli_result Object
(
[current_field] => 0
[field_count] => 1
[lengths] =>
[num_rows] => 346
[type] => 0
)

  1. Pobrane dane są w 100% poprawne:
while ($rec = mysqli_fetch_array($result))
{
print_r($rec);
    $fping .= " $rec[ip_apka]";
}

frangment wyniku:

Array
(
[0] => 192.168.xxx.xxx
[ip_apka] => 192.168.xxx.xxx
)
Array
(
[0] => 192.168.xxx.xxx
[ip_apka] => 192.168.xxx.xxx
)
Array
(
[0] => 192.168.xxx.xxx
[ip_apka] => 192.168.xxx.xxx
)
Array
(
[0] => 192.168.xxx.xxx
[ip_apka] => 192.168.xxx.xxx

  1. I tutaj coś się zaczyna psuć (tutaj faktycznie poprawiłem na foreach, bo nie wiem czemu zapomniałem to zrobić, chociaż w każdym innym skrypcie to zmieniałem):
$sql = "INSERT INTO `stan_sieci` (`ip_apka`, `timestamp`, `stan`) VALUES";
foreach($wyjscie as $key => $line)
{
    $t_line = explode (" ", $line);
// Obcinanie adresu IP do 3 i 4 tego oktetu. Po przejsciu na ip zew. ze 192.168 juz nie potrzebne
//    $ip = substr($t_line["0"],8);
      $ip = $t_line["0"];
    if ($t_line["2"] == "alive") { $alive = "1"; } else { $alive = "0"; }
    if ($key > 0) $sql .= ",";
    $sql .= " ('$ip', now(), '$alive')";
}
print_r($wyjscie);
print_r($sql);

wynik:

Array
(
)
INSERT INTO stan_sieci (ip_apka, timestamp, stan) VALUESerr2: Nie mozna zapisac danych stanu sieci!

1

Zwróć uwagę, że ja użyłem dwóch zmiennych do stworzenia zapytania i w pętli dodaję tylko wartości. Wyprintuj sobie stworzone zapytanie przed insertem. Z tego co wkleiłeś znów najpierw dodajesz przecinek a potem nawiasy z wartościami. Zamień te jednolinijkowe ify i elsy albo na kilka linijek, albo na ternary jak zaproponowałem, bo raz że jest to małoczytelne, dwa to niezgodne ze standardami PSR.
Ja pisałem trochę na kolanie, to sprawdź sobie to jeszcze raz u siebie linijka po linijce.

0

@jurek1980: @Panczo: Dzięki za pomoc :D! Rozwiązanie okazało się jednak o wiele łatwiejsze niż myślałem. Bruździła zła ścieżka do fpinga i to wszystko psuło. Do tego pierwsza odpowiedź Jurka też była poprawna, bo po rozwiązaniu mojej gafy ze ścieżką, trzeba było zmienić to, co zasugerował.

I co do tych standardów, to do więzienia za to chyba nie wsadzają :P? Szczególnie, że jako sieciowiec/linuxowiec mam małe pojęcie o PHP'ie i ogólnie programowaniu (jedynie bash i coś tam w perlu umiem skrobnąć).

1 użytkowników online, w tym zalogowanych: 0, gości: 1