Eroare mysqli_fetch_assoc() expects parameter 1 to be mysqli_result, Boolean given

Salut,
Am nevoie de ajutor pentru urmatoarea secventa:

<?php
if($_POST['submit']) {
    $ip=$_SERVER['REMOTE_ADDR'];
    $con = mysqli_connect("hostname","user","passwd","database") or die ("<script language='javascript'>alert('Unable to connect to database')</script>");
    $sql = "INSERT INTO IPaddress (submitIP) VALUES ('$ip')";

if ($con->query($sql) === TRUE) {
    echo "New record created successfully";
    } else {
    $result = mysqli_query($con, "SELECT COUNT(Auto_increment) FROM IPaddress WHERE submitIP = {$ip}");
    echo $result+1;
    $row = mysqli_fetch_assoc($result);

    if($row['Auto_increment'] > 2) {
       header("Location");
  exit;
    }
}
}

//urmeaza html form 
//Submit

Deci ideea este ca vreau sa restrictionez numarul de submiteri a form-ului de catre fiecare url care imi acceseaza pagina. Am creat tabelul IPaddress, care contine 2 coloane, “Auto_increment”, definit ca INT, care sa tina evidenta numarului de submiteri, si “submitIP”, definit ca VARCHAR, care sa retina fiecare adresa IP care a actionat form-ul. Tabelul a fost creat nu prin cod php, ci din meniul de MySQL Admin al host-ului (Awardspace.net).

Programul merge bine pana la “$result = mysqli_query”. Ce este pana acolo functioneaza, se insereaza adresa IP, se incrementeaza Auto_increment. Dar “$result = mysqli_query” nu returneaza nimic. “$result= $result +1”, returneaza doar “1”, iar “mysqli_query_assoc($result)” da eroarea de mai sus. Cred ca undeva este o neconcordanta in tabelul MySQL, dar nu pot sa-mi dau seama unde, ca am incercat o multime de variante pentru Indexul tabelului si tipurile de variabile din coloane.
Multumesc

Așa-ți trebuie dacă faci intercalare (“string interpolation”)! :stuck_out_tongue:
Ia bagă tu după
"$result = mysqli_query"
un
"or die(mysqli_error($con))"
și vezi ce zice, să vedem dacă-ți dai seama unde ai greșit!

Deasemenea, sper că-i clar că “or die” e doar pentru depanare; în producție va trebui să tratezi mai elegant erorile.

1 Like

Salut Ionut,

Multumesc pentru sugestie. Am adaugat “or die…” si imi da:

mysql_fetch_assoc(): supplied argument is not a valid MySQL result resource

Stiam ca e ceva in neregula cu “mysqli_query” dar nu stiu ce.

Daca fac acelasi query direct din meniul phpMyAdmin, folosind in fereastra de SQL query:

SELECT `Auto_Increment` FROM `IPaddress` WHERE `submitIP`= '$ip'

rezultatul este de 0 rows, desi am o adresa IP (a mea) inserata in tabel, deci ar trebui sa-mi dea rezultatul 1.

Cred ca problema este la tabelul MySQL, dar nu pot sa-mi dau seama unde, desi tabelul e simplu are doar 2 coloane. Initial facusem 3 coloane, mai era una si cu data, dar am renuntat la ea, ca imi dadea eroare chiar la formarea tabelului. Asa cum e acum, vad ca phpMyAdmin mi-a acceptat tabelul, dar undeva e o necocordanta.
Alte idei?

O regula de aur zice ca e mereu utilizatorul/programatorul de vina. Si in cazul tau, trebuie sa cauti problema in codul scris de tine.

mysql_fetch_assoc(): supplied argument is not a valid MySQL result resource

iti zice ca parametrul de intrare nu e valid. asta inseamna ca eroare e undeva mai sus, in cazul tau pare sa fie la

$result = mysqli_query($con, "SELECT COUNT(Auto_increment) FROM IPaddress WHERE submitIP = {$ip}"); 
// ipul banuiesc ca e string, si atunci ar trebui escapat pentru mysql: '{$ip}' - probabil de aici e problema.

echo $result+1; // aici result nu e de tip integer ci mysql resource sau false in caz de eroare. echo asta iti va face cast la int si iti va afisa cel mai probabil 1.
2 Likes

Multumesc pentru sugestie. Am incercat acuma si cu ‘{$ip}’ si cu ‘$ip’ dar degeaba, imi da aceeasi eroare.

Evrika! Vad ca am schimbat Indexul pentru coloana “submitIP” de la UNIQUE la TEXT si eroarea a disparut! Trebuie sa vad acuma cum pun conditia la numarul de submiteri

Solutie eleganta te referi la throw new exception si try catch, nu?
Eu asa procedez, exista vreo solutie mai eleganta?

sunt mai multe abordari posibile, nu pe acelasi nivel

recomandarea de baza e folosirea unui orm in locul functiilor sql de baza din codul exemplu; in acest fel codul e robust si permite upgradeuri / scalare ulterioare

se poate face bubble-up la eroare, adica fac try-catch si arunc mai departe o eroare prin care sa stiu daca e de vina userul sau codul meu

E foarte importanta diferentierea intre erori de navigare/selectie in site (asa numitele “recoverable errors”), pentru a permite acestuia sa isi remedieze actiunea. Exemple:

  • vina mea / codului: “am intampinat o problema, am fost notificati si o vom remedia in cel mai scurt timp”
  • vina utilizatorului: “ai facut o selectie invaluda; te rugam sa incerci din nou” / “nu mai sunt camere libere in hotelul selectat de tine”

(scriu de pe telefon, e putin nestructurat si sigur incomplet ce am notat)

3 Likes

Dacă nu citești, normal că-ți dă eroarea aia. Eu am zis să pui or die() după mysqli_query, nu după mysqli_fetch_assoc. Dacă ai fi făcut cum ziceam eu, ai fi văzut că ai o eroare de sintaxă în interogarea mysql, și anume exact ce a zis @dabuno: faptul că nu ai pus adresa IP între ghilimele.
De-aia am și zis că “așa-ți trebuie dacă folosești string interpolation”, pentru că din cauza aia s-a strecurat eroarea fără s-o poți observa cu ușurință (dar probabil nu știi nici aia ce-i
și nici nu te-ai obosit să cauți). În loc de ce-ai făcut tu, cam așa trebuia scris codul MySQL:

mysqli_query($con, 'SELECT `Auto_Increment` FROM `IPaddress` WHERE `submitIP`= "' . mysqli_real_escape_string($con, $ip) . '"');

În altă ordine de idei, documentația funcției mysqli_query spune că rezultatul este de tip mixed și că, în cazul interogărilor de tip SELECT poate fi un obiect mysqli_result sau boolean, mai exact FALSE în situația în care interogarea nu a putut fi executată. În situații de genul ăsta, obișnuiește-te să tratezi toate rezultatele posibile; în cazul ăsta n-ai făcut-o și s-a strecurat un bug cu care ai pierdut o zi.

if ($result) {
    $row = mysqli_fetch_assoc($result);
} else {
    // ai o eroare MySQL; fă ceva cu ea
}

Apoi, problema cu indexul nu are nici o legătură cu eroarea de la SELECT; indexul UNIQUE îți genera o altă eroare la INSERT, atunci când încercai să introduci un al doilea rând cu același IP, dar, cum nu ai tratat nici situația aia, nu aveai de unde să știi. Adică acolo ai verificat măcar ca rezultatul interogării să fie TRUE înainte să continui, dar nu faci nimic dacă-i FALSE; sau crezi cumva că trăiești într-o lume ideală și ție ți se vor executa întotdeauna toate interogările fără nici un fel de probleme? :smile:

Ca să-i răspund și lui @noah, nu, nu mă refer (neapărat) la try/catch ci la faptul că nu-i o idee prea bună să omori scriptul cu or die și să-i afișezi utilizatorului o eroare MySQL care ori nu înseamnă nimic pentru el, ori tocmai i-a dezvăluit parțial structura unui tabel din baza de date, ajutându-l în explotarea eventualelor vulnerabilități.

@alchimist Cum ți se pare experiența utilizatorilor în cazul

$con = mysqli_connect("hostname","user","passwd","database") or die ("<script language='javascript'>alert('Unable to connect to database')</script>");

? Adică, după ce dă click pe OK în alert, utilizatorul rămâne cu o pagină albă în față. Ce-are de făcut mai departe? Închide fereastra, sau ce?

@tekkie Răspunsul tău e un pic peste nivelul celui ce a pus întrebarea. El abia se descurcă cu procesarea unui formular simplu în PHP și se împotmolește la interogări simple iar tu îi recomanzi un ORM? Să fim serioși; mai are de învățat chestii de bază până să ajungă acolo.

4 Likes

@Ionut Poate m-am exprimat gresit, dar am pus “or die” dupa “mysqli_query”, asa cum ai zis. Dar nu mi-a dat nici o informatie suplimentara si eroarea a ramas aceeasi, cu sau fara ghilimele. Chestia s-a rezolvat cand am schimbat indexul din UNIQUE in TEXT. E bine ca am facut erorile astea, ca m-am prins cum functioneaza MySQL. Dupa TRUE permiteam inca o re-iterare, in caz contrar trimiteam utilizatorul la alta pagina (nu ramanea cu pagina alba), dar partea asta nu am mai adaugat-o ca oricum programul nu ajungea pana acolo. Normal ca dupa ce fac programul sa mearga scot “or die” si alte controale, echo, etc pe care le-am inserat acuma, de atata lucru m-am prins si eu.
O parte din eroare provenea si din executarea a 3 cicluri “if” incluse unul in altul, care nu lasau programul sa continue. Acuma m-am lamurit, am rearanjat parantezele, si am trecut la faza urmatoare.
Cu faptul ca abia ma descurc in php ai nimerit-o. Cand am facut eu Politehnica se preda Fortran :smile: De php m-am apucat de vre-o luna pescuind ce se poate de pe net, fara sa am o carte, ca oricum nu am timp s-o citesc de la cap la cap ca eu sunt doctor in alt domeniu :smiley:
@IonutBotizan @noah @tekkie Multumesc pentru ajutor si sugestii!

Nu prea ai inteles. Text este tipul coloanei iar unique este o proprietate a coloanei, nu se anuleaza unul pe altul. Unique nu merge cu text pentru ca acest tip de coloana suporta dimensiuni pana la 64kilobytes iar MYSQL nu poate sa-ti asigure unicitatea la asemenea dimensiuni.
le: selecteaza varchar cu unique.

Da, ai dreptate, este vorba de FULLTEXT. In phpMyAdmin, la crearea tabelului, pentru Type am o multime de optiuni (INT, VARCHAR, DATESTAMP, etc) iar pentru Index am optiunile: PRIMARY, UNIQUE, INDEX si FULLTEXT. Atunci cand am schimbat UNIQUE cu FULLTEXT vad ca a disparut eroarea. De fapt nici nu aveam nevoie de UNIQUE, facusem un rationament gresit. In plus, vad ca desi eu am creat 2 coloane, in tabel mai apare si a treia creata din oficiu, intitulata PRIMARY, care presupun ca este un sistem de evidenta interna, care vad ca se modifica la adaugarea fiecarui rand.
Este posibil sa chemi coloana PRIMARY prin instructiuni php?