Otin tuon ajv:n rekisteröitymis/kirjautumis -koodivinkin ja oon nyt yrittäny muokata sitä PDO:n kautta toimivaksi. Pari ongelmaa on tullu vastaan enkä oo löytäny niihin ratkasuja millään..
/* SISÄÄNKIRJAUTUMINEN */ if(isset($_POST['tunnus']) && isset($_POST['salasana'])){ $salasana = md5($_POST['salasana']); $istuntotunnus = md5(uniqid("")); //lisätään istunto tietokantaan käyttäjän tietoihin $login = $lnk->prepare("UPDATE {$tbl_users} SET istunto = '{$istuntotunnus}' WHERE tunnus = ? AND salasana = ?"); //onnistuiko kirjautuminen if($login->execute(array($_POST['tunnus'], $salasana))){ //laitetaan sessioon talteen istuntotunnus $_SESSION['log_key'] = $istuntotunnus; //takaisin edelliselle sivulle header("Location: ".isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : "index.php"); } else { $error = "<div id='error'>Salasana tai käyttäjätunnus väärin!</div><br />\n"; } }
Istuntotunnus ei muutu tietokannassa, vaikka kirjoitan oikean käyttiksen ja salasanan (eli ei kirjautunut sisään) ja väärän salasanan kirjoittaessa en saa tuota virheilmoitusta esiin. Alkuperäisessä koodivinkissä sain virheen näkyviin oikein, mutta tässä ei tule.
/* KÄYTTÄJÄN TUNNISTUS */ if(isset($_SESSION['log_key'])){ //HAETAAN KÄYTTÄJÄN TIEDOT $sql = $lnk->prepare("SELECT id, tunnus, DATE_FORMAT(last_load,'%d.%m.%y %h:%i:%s') AS last_load FROM {$tbl_users} WHERE istunto = ?"); if($sql->execute(array($_SESSION['log_key']))){ //käyttäjä tunnistettu, kaikki kunnossa $user = $sql->fetch(); //lisätään vielä käyttäjän viimeisestä sivunlatauksesta aikaleima $upload = $lnk->prepare("UPDATE {$tbl_users} SET last_load = NOW() WHERE id = ?"); $upload->execute(array($user['id'])) } }
Jokin menee selvästi pieleen tuossa $user = $sql->fetch(); kohdassa, sillä en saa tuosta ulos samanlaista taulukkoa kuin alkuperäisessä koodivinkissä. Eli esim. echo $user['id']; palauttaa vain tyhjää.. Oon myös yrittänyt fetchAll():lla, mutta silläkään ei toimi..
Tervehdys AkeMake.
PDO on minulle aivan uusi asia, mutta nopean perehtymisen jälkeen voisin esittää seuraavat kohdat:
1. koodinpätkässäsi virhettä ei tule näkyviin koskaan, jos kysely saadaan suoritettua. Toisin sanoen "if($login->execute(array($_POST['tunnus'], $salasana))){" tarkistaa vain, että kysely onnistuu. Koodivinkissä on käytetty mysql_affected_rows ja samaan tulokseen pääset PDO:ssa rowcount:lla.
2. koodinpätkästä, oletko varma, että kyseinen istunto on varmasti tallennettu? Saatko tulostettua mitään tekstiä kyseisen if-lauseen sisällä?
Niin tosiaan kysely saadaan siis suoritettua, vaikkei haetulla tunnuksella ja salasanalla löytyisikään tietoja tietokannasta. Silloin vain palautuu nolla riviä?
Muutin nyt kyselyä seuraavanlaiseksi:
/* SISÄÄNKIRJAUTUMINEN */ if(isset($_POST['tunnus']) && isset($_POST['salasana'])){ $salasana = md5($_POST['salasana']); $istuntotunnus = md5(uniqid("")); //lisätään istunto tietokantaan käyttäjän tietoihin $login = $lnk->prepare("UPDATE {$tbl_users} SET istunto = '{$istuntotunnus}' WHERE tunnus = ? AND salasana = ?"); $login->execute(array($_POST['tunnus'], $salasana)); //jos lisättyjä rivejä on yksi, kirjautuminen onnistui if($login->rowCount() == 1){ //laitetaan sessioon talteen istuntotunnus $_SESSION['log_key'] = $istuntotunnus; //takaisin edelliselle sivulle header("Location: ".isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : "index.php"); } else { $error = "<div id='error'>Salasana tai käyttäjätunnus väärin!</div><br />\n"; } }
Nyt virheilmoitus tulee joka kerta; myös oikean käyttäjätunnuksen ja salasanan jälkeen. Istuntotunnus ei tallennu tietokantaan oikenkaan kirjautumisen jälkeen. Mistähän tällä kertaa kiikastaa?
Jotenkin pistää silmään..
$login = $lnk->prepare("UPDATE {$tbl_users} SET istunto = '{$istuntotunnus}' WHERE tunnus = ? AND salasana = ?"); #kokeile vaikka huvikses näin.. liian unessa tajutakseen mitään $login = $lnk->prepare("UPDATE ".$tbl_users." SET istunto=".$istuntotunnus." WHERE tunnus=? AND salasana=?");
ja miksi uniqid("") eikä uniqid().. Ja mikä on tuo mystinen $lnk. toivottavasti ratkaisu löytyy... pitää varmaan keräillä ittensä tästä näppikseltä sänkyyn. Hyvää yötä ja huomenta.
EDIT: http://stackoverflow.com/questions/769767/pdos-rowcount-not-working-on-php-5-2-6
https://www.php.net/manual/en/pdostatement.
Curly-brackets on vaan syntaksi, joka helpottaa muuttujan ja normitekstin erottamista lainausmerkkien sisällä. Myöskään välillä ei pitäisi olla merkitystä SQL-syntaksissa. Uniqid
:n default-arvo ekalle parametrille on tosiaan tyhjä merkkijono, joten voi kutsua suoraan uniqid();
. Toisessa ketjussa tuli ilmi, että tässä riittänee kun tarkistaa, että execute
palauttaa arvon true
, mutta jos istuntotunnus ei tosiaan mene kantaan ollenkaan niin sitten ongelma on kantayhteydessä. Toimiiko $lnk
muuten, yhdistääkö kantaan & voitko suorittaa muita kyselyitä?
tsuriga kirjoitti:
Toisessa ketjussa tuli ilmi, että tässä riittänee kun tarkistaa, että
execute
palauttaa arvontrue
Voisitko antaa esimerkki koodin? Itse yritin päivittää kantaan tietoja olemattomalle id:lle ja kysely silti palauttaa true
.
Vai tarkoititko, että ilmoitusta väärästä käyttäjätunnuksesta ei tarvitse?
Käytän uusinta WampServer 2:sta, PHP 5.3.0, MySql 5.1.36
EDIT: rowCount() nähtävästi näyttää vain ne päivitetyt rivit jotka ovat muuttuneet, mutta ei varmaan pitäisi tulla tilannetta, jossa sama käyttäjä saisi uniqid():llä saman istuntotunnuksen, mitä edellisellä kirjautumisella?
MD5-funktion käyttö istunnon tunnisteessa on tarpeetonta. Kyselyssä kannattaisi käyttää senkin asettamiseen ?-merkkiä, aivan kuten tunnukseen ja salasanaan.
Tässä on testikoodi, joka toimii:
<?php // Varsinainen kiistanalainen koodi funktiona: function kirjaudu() { $tbl_users = "kayttaja"; global $lnk; $istunto = uniqid(); $login = $lnk->prepare("UPDATE {$tbl_users} SET istunto = ? WHERE tunnus = ? AND salasana = ?"); $login->execute(array($istunto, $_POST["tunnus"], md5($_POST["salasana"]))); // Palautetaan tieto onnistumisesta. return ($login->rowCount() == 1); } // Funktio edellisen testaamiseen määrätyillä arvoilla: function testaa($tunnus, $salasana) { $_POST["tunnus"] = $tunnus; $_POST["salasana"] = $salasana; if (kirjaudu()) { echo "$tunnus + $salasana = ok\n"; } else { echo "$tunnus + $salasana = virhe\n"; } } // Funktio, joka luo SQLite-kannan ja testikäyttäjiä ja yrittää kirjautua. function testaa_sqlitella() { // Tyhjennetään testikanta ja avataan se. $db = dirname(__FILE__). "/kokeilu.sqlite"; @unlink($db); global $lnk; $lnk = new PDO("sqlite:{$db}"); $lnk->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Luodaan taulu. $lnk->exec(" CREATE TABLE kayttaja ( id INT AUTO_INCREMENT PRIMARY KEY, tunnus TINYTEXT, salasana TINYTEXT, istunto TINYTEXT ) "); // Luodaan pari esimerkkikäyttäjää. $k = $lnk->prepare("INSERT INTO kayttaja (tunnus, salasana) VALUES (?, ?)"); $k->execute(array("t1", md5("ss1"))); $k->execute(array("t2", md5("ss2"))); $k->execute(array("t3", md5("ss3"))); // Testataan kirjautumista. testaa("t1", "ss1"); testaa("t1", "- -"); testaa("t2", "- -"); testaa("t2", "ss2"); testaa("t3", "- -"); testaa("t3", "ss3"); // Suljetaan yhteys ja poistetaan testikanta. unset($lnk); unlink($db); } // Ajetaan SQLite-testit. Tämä toki epäonnistuu, jos palvelimelta puuttuu PDO-ajuri SQLitelle. testaa_sqlitella();
Othnos kirjoitti:
tsuriga kirjoitti:
Toisessa ketjussa tuli ilmi, että tässä riittänee kun tarkistaa, että
execute
palauttaa arvontrue
Voisitko antaa esimerkki koodin? Itse yritin päivittää kantaan tietoja olemattomalle id:lle ja kysely silti palauttaa
true
En tosiaan muistanut tarkastella itse SQL-lausetta, joka siis toki näyttää tietokannan silmissä ihan toimivalta, vaikka se kohdistettaisiinkin olemattomalle id:lle. Tässä tilanteessa ei siis riitä hakulauseen suorittamisen onnistumisen tarkistaminen.
Nämä {tbl_users} ja uniqid("") tulevat suoraan tuolta koodivinkistä jota käytän. Parempi kai ne on muuttaa..
tsuriga kirjoitti:
Toimiiko $lnk muuten, yhdistääkö kantaan & voitko suorittaa muita kyselyitä?
Toimii. Haen sivujen tekstisisällön onnistuneesti tietokannasta. Ongelma näyttäisi olevan tuossa rowCount() kohdassa, sillä se palauttaa sitkeästi nolla riviä, vaikka salasana ja käyttäjätunnus olisivat oikein. Mitenhän voisin tutkia tuon toisella tavalla? Löysin jostain, että fetchAll() count() yhdistelmällä ...
... mutta tämä heittää vain tyhjälle sivulle.
Muutin hiukan kirjautumista lähemmäksi Metabolixin esimerkkiä ja se näyttää nyt tältä:
/* SISÄÄNKIRJAUTUMINEN */ if(isset($_POST['tunnus']) && isset($_POST['salasana'])){ $istunto = uniqid(); //lisätään istunto tietokantaan käyttäjän tietoihin $login = $lnk->prepare("UPDATE ".$tbl_users." SET istunto = ? WHERE tunnus = ? AND salasana = ?"); $login->execute(array($istunto, $_POST['tunnus'], md5($_POST['salasana']))); //jos lisättyjä rivejä on yksi, kirjautuminen onnistui if($login->rowCount() == 1){ //laitetaan sessioon talteen istuntotunnus $_SESSION['log_key'] = $istunto; //takaisin edelliselle sivulle header("Location: ".isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : "index.php"); } else { $error = "<div id='error'>Salasana tai käyttäjätunnus väärin!</div><br />\n"; } }
Jos rowCount palauttaa 0 niin tietokannassa ei selvästikkään ole muutettu mitään. Oletko varma, että kyseinen taulu on luotu, käyttäjätunnus ja salasana lisätty ja oikein (salasana md5 hashattu?). uniqid palauttaa eri tunnisteen joka tallennetaan?
Olipa nolo ratkaisu ongelmaan.. RowCount palautti tosiaan nolla riviä yksinkertaisesti siitä syystä, että syötin väärää salasanaa. Kävin muuttamassa tietokantaan md5 hashatun salasanan ja katsos kun meni ongelmitta sisään. x)
Nyt on sitten vuorossa toinen kirjautumisongelma.. Nyt kirjautuminen tallennetaan istuntoon ja istuntohan kestää oletusarvoisesti 24 minuuttia, jonka jälkeen se nollataan eli käyttäjä kirjataan ulos. Miten saisin kirjautumisen kestämään vaikka loputtoman kauan mikäli käyttäjä vain pitää sivun auki eikä kirjaa itseään ulos?
Olisiko tuohon viimeisimpään kysymykseeni jotain vastausta?
Istunto kestää oletusarvoisesti 24 minuuttia, jolloin käyttäjä kirjataan ulos. Miten kirjautumisen saisi kestämään niin kauan kuin käyttäjä haluaa? Pitääkö kirjautuminen toteuttaa jotenkin muuten kuin istuntojen avulla?
Toinen ongelma:
Mikä menee seuraavassa käyttäjätunnuksen tarkistuksessa väärin? Olen tämän koodinpätkän saanut tehtyä hiukan kopioimalla Joomla!:n koodia ja samalla hiukan opiskelemalla luokkia..
<?php require_once (dirname(__FILE__)'/factory.php'); require_once (dirname(__FILE__)'/helper.php'); if(isset($_POST['reg_tunnus'])) { $sign = $_POST['reg_tunnus']; $checking =& RegisterHelper::checkSign($sign); if($checking) { echo $checking; } else { echo "Käyttäjätunnus kelpaa."; } } ?> <form method="post" action="<?php echo $_SERVER['PHP_SELF'] ?>"> Käyttäjätunnus: <input name="reg_tunnus" type="text" size="15" maxlength="20" value="<?php echo isset($_POST['reg_tunnus']) ? $_POST['reg_tunnus'] : ""; ?>" /><br /> <input type="submit" value="Rekisteröidy" /> </form>
<?php // factory.php tiedosto /* getTable funktio palauttaa kaksiuloitteisen taulukon, joka sisältää tietokannasta tietyillä ehdoilla haetut tiedot. */ class SFactory { function &getTable($table, $column = NULL, $value = NULL) { if(isset($column) && isset($value)) { $term = " WHERE ".$column." = ".$value; } $contact = $lnk->prepare("SELECT * FROM ".$table.$term); $contact->execute(); $row = 0; while($rows = $contact->fetch(PDO::FETCH_NUM)) { for($i = 0; $i < count($rows); $i++) { $table[$row][$i] = $rows[$i]; } $row += 1; } return $table; } } ?>
<?php // helper.php tiedosto class RegisterHelper { function &checkSign($sign) { $msg = ""; if(strlen($sign) < 4) { $msg = "Käyttäjätunnus on liian lyhyt. Minimipituus on neljä merkkiä.\n"; } $count =& SFactory::getTable('users', 'sign', $sign); if($count) { $msg = "Käyttäjätunnus on varattu.\n"; } return $msg; } } ?>
Ääh.. Ovatko nämä ongelmani liian vaikeita, niin vaikeasti selitetty, ettei kukaan ymmärrä, olenko liian kärsimätön vastauksen odottamisessa vai eikö ketään vain kiinnosta perehtyä näihin?
// Olen kyllä odottanut vastauksia vasta sen seitsemän tuntia, mutta sinä aikana sivuilla on ehtinyt pyörähtää useita henkilöitä, jotka näihin ongelmiin ovat yleensä vastailleet..
Ensimmäiseen kysymyksee vastaus on helppo, joka sinun tulisi kyllä keksiä itsekin, jos meinaat sivuja väännellä enemmänkin. Siis laitat vain elinajaksi hiukan pidemmän ajan kuin 24-minuuttia.
Toiseen kysymykseen kaipaisin hiukan enemmän tietoa mikä siinä menee vikaan, koska koodia seuraamalla se ei (minulle) aukene kunnolla.
Teuro kirjoitti:
Ensimmäiseen kysymyksee vastaus on helppo, joka sinun tulisi kyllä keksiä itsekin, jos meinaat sivuja väänneellä enemmänkin.
Kylläpä iski arkaan paikkaan, koska tarkoitushan olisi todellakin väännellä sivuja enemmänkin. :D Minähän en istunnoista vielä kovin paljoa tiedä vaan tuon 24 minuuttiakin löysin Mureakuhan php-oppaasta.
lainaus:
Käyttämättömät istunnot tuhotaan oletuksena 24min jälkeen (php.ini asetus).
Enkä sen puoliin tiedä mitään tuosta php.ini:stäkään. Minulla ei siis ole minkäänlaista tietoa miten istunnon pituutta säädellään. Onko siihen olemassa jokin session_lifetime() tai jotain?
Paha sanoa tuosta toisesta ongelmasta, että mikä siinä menee vikaan. Tarkoitus olisi siis tarkistaa, että kävijän antamaa käyttäjätunnusta ei ennestään löydy tietokannasta ja että se on vähintään neljä merkkiä pitkä. Mitä tahansa tekstikenttään kirjoitankin niin tuo antaa tulokseksi vain tyhjää sivua..
En tiedä mistä lainasit, mutta lainauksessa on vastaus kysymykseesi. Istunnon elinikää (kuten monta muuta mukavaa asiaa, lue kohta mainittavan tiedoston kommentit läpi) voi muuttaa php.ini-tiedostoa muokkaamalla. Jos enemmän kiinnostaa väännellä niin tutustu session_set_save_handleriin
, Zend DevZone - Trick-Out Your Session Handler.
Toiseen kysymykseen en valitettavasti kerkeä tutustumaan juuri nyt.
Tarkoitus ei ollut pahoittaa mieltäsi tai harrastaa egotrippailua, koska eväät ei siihen riitä. Näyttää siltä, että php.ini tiedostosta tulisi kaivaa seuraava rivi esille.
php.ini kirjoitti:
session.gc_maxlifetime = 1440
Tuo on juurikin 24-minuuttia.
AkeMake kirjoitti:
Paha sanoa tuosta toisesta ongelmasta, että mikä siinä menee vikaan. Tarkoitus olisi siis tarkistaa, että kävijän antamaa käyttäjätunnusta ei ennestään löydy tietokannasta ja että se on vähintään neljä merkkiä pitkä. Mitä tahansa tekstikenttään kirjoitankin niin tuo antaa tulokseksi vain tyhjää sivua..
Joo tuon alun siitä toki päättelinkin, mutta se että tulee vain valkoista oli oleellisesti parempi tieto. Nyt sinun tulee kääntää virheilmoitukset päälle paikka on sama tiedosto (php.ini), kuin äskenkin ja sieltä virheilmoitukset kohdasta E_STRICT tasoiset virheet päälle, joskin E_NOTICE on myös hyvä olla päällä. Näillä eväillä päässet eteenpäin.
Teuro kirjoitti:
Tarkoitus ei ollut pahoittaa mieltäsi tai harrastaa egotrippailua, koska eväät ei siihen riitä.
En minä siitä mieltä pahoittanut. :) Otin ehkä enemmänkin haasteena (lisää opiskeltavaa).
Kiitos näistä vastauksista. Minun pitää siis jostain etsiä yksityiskohtaisen oppaan siitä, että mikä tuo php.ini ylipäätään on (tietämys sen suhteen on siis täysin nollassa). Siihen kun tuntuu törmäävän aina silloin tällöin.
Enää puuttuu siis vastaus tuohon toiseen kysymykseen. Olisin kiitollinen, jos joku jossain vaiheessa ehtisi tutustua siihen. :)
Teuro kirjoitti:
Nyt sinun tulee kääntää virheilmoitukset päälle paikka on sama tiedosto (php.ini)
Eli taas yksi hyvä syy ottaa selvää mikä on php.ini. Kiitos. :)
Minusta taas juuri jälkimmäinen ongelma on noista se, joka pitäisi osata itsekin selvittää. Sehän selviää debuggauskeinoista alkeellisimmalla eli sillä, että tulostat muuttujia matkan varrella:
Virheen näkee helposti myös suoraan koodista, mutta kokeilepa ensin selvittää se itse.
Muiltakin osin tuo getTable-funktio on tehty päin honkia. Käytät alustamattomia muuttujia, ja (siitä johtuen) jos kannasta ei tule yhtään riviä, palautatkin taulun nimen etkä suinkaan tyhjää tulostaulukkoa. Moni asia sujuisi paremmin, jos laittaisit PHP:n kaikki virheilmoitukset näkyviin ja korjaisit virheet.
Lisäksi PDO:n käyttö on näköjään yhä hukassa. Haettavaa arvoa ei pitäisi tunkea suoraan kyselyyn, vaan pitäisi käyttää kysymysmerkkiä ja antaa arvo execute-funktiolle.
Teuroa tarkentaen, virheilmoitusasetuksen arvo kannattaa valita kehitysympäristössä seuraavasti:
error_reporting = E_ALL | E_STRICT
error_reporting = E_ALL
E_STRICT
ei sisällä E_ALLia
, mutta E_ALL
sisältää E_STRICTin
PHP:n kutosversiosta lähtien. Haluat myös tutustua display_errors
asetukseen. Tuotantoympäristössä halunnet mieluummin tallentaa skriptien virheet tiedostoon (log_errors
-asetus) ja näyttää käyttäjälle niiden sijaan jotain informatiivisempaa, kuten "Tietokantaan tallennus ei onnistunut, yritä myöhemmin uudelleen." tmv.
Aihe on jo aika vanha, joten et voi enää vastata siihen.