Op aanvraag van NiFkE zal ik in deze tutorial uitleggen hoe je een basis loginsysteem in elkaar knutselt. Het verschil met de andere tutorial met betrekking tot loginsystemen is dat ik geen gebruik van classes ga maken, zodat de gemiddelde PHP beginner er wijs uit raakt, en vooral, er uit leert.
Opzet
Voor je ergens aan begint is het altijd intressant om er eerst over na te denken. Wat hebben we nodig voor een loginsysteem?
Registratie: de gebruiker kiest een gebruikersnaam, een wachtwoord en e-mailadres
Activatie: de gebruiker moet zijn/haar account activeren per mail
Login: de gebruiker logt in met zijn/haar gebruikersnaam en wachtwoord
Controlepaneel: de gebruiker kan acties doen met betrekking tot zijn/haar account
Opmerking: ik ga er van uit dat je gebruik maakt van een index.php waarin je alle content include. In die index.php is dan reeds een database connectie aanwezig, en staat session_start() voor alle output. Mocht dit niet het geval zijn:
Maak bovenaan iedere pagina een databaseconnectie
Plaats boven iedere pagina session_start() zodat de $_SESSION-variabelen overal bereikbaar zijn.
Voor meer informatie over sessies kan je de tutorial van FangorN in deze categorie doornemen.
Registratie
Bij de registratie is het de bedoeling dat de gebruiker een e-mailadres opgeeft, en een gebruikersnaam en wachtwoord kiest. Daarna moet de account geactiveerd worden, maar als de gebruiker de account niet tijdig activeert moet deze data verwijderd worden, daar dit onnodig plaats inneemt in de database.
We gebruiken dus de volgende databasestructuur:
CREATE TABLE `gebruikers` (
`id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`gebruikersnaam` VARCHAR( 50 ) NOT NULL ,
`wachtwoord` VARCHAR( 40 ) NOT NULL ,
`email` VARCHAR( 128 ) NOT NULL ,
`datum` DATETIME NOT NULL
) ENGINE = MYISAM ;
CREATE TABLE `gebruikers_activaties` (
`gebruikers_id` INT( 11 ) NOT NULL ,
`sleutel` VARCHAR( 40 ) NOT NULL
) ENGINE = MYISAM ;
We hebben dus een tabel voor de gebruikers, alsook voor de activaties. De veldnamen spreken voor zich :)
Vervolgens kunnen we een registatieformulier opstellen:
Bestand: registratie.php
<?php
if($_SERVER['REQUEST_METHOD'] == 'POST') {
// Hier komt alle behandeling van het formulier
// Mocht er een fout optreden, is het leuk dat de gebruiker niet alles opnieuw hoeft
// in te vullen. Daarom vullen we de array $post met de velden,
// uiteraard door htmlentities() gehaald (veiligheidsmaatregel).
// Wachtwoord moeten wel opnieuw ingevuld worden, nogmaals veiligheidsmaatsregel
$post = array(
'gebruikersnaam' => htmlentities($_POST['gebruikersnaam']),
'email' => htmlentities($_POST['email']));
} else {
// Dit stuk wordt uitgevoerd als het formulier nog niet is verzonden.
// Het formulier is nog niet verzonden, dus is er ook geen $post array.
// we maken deze dus zelf aan met initiele waardes, om warnings te voorkomen.
$post = array(
'gebruikersnaam' => '',
'email' => '');
}
?>
<form action="<?=htmlentities($_SERVER['REQUEST_URI'])?>" method="post">
Gewenste gebruikersnaam:<br />
<input type="text" name="gebruikersnaam" value="<?=$post['gebruikersnaam']?>" /><br />
Wachtwoord:<br />
<input type="password" name="wachtwoord" /><br />
Wachtwoord herhalen:<br />
<input type="password" name="wachtwoord_herh" /><br />
E-mailadres:<br />
<input type="text" name="email" value="<?=$post['email']?>" /><br />
<br />
<input type="submit" value="Registreer" />
</form>
Het formulier hebben we dus als, nu moeten we het verwerken. Het blok voor verwerking is reeds gemaakt, en staat ook aangegeven in bovenstaande code.
Wanneer de gebruikers iets invult moeten volgende zaken gecontroleerd worden:
Of de gebruikersnaam ingevuld is, korter is dan 50 tekens, en vrij is.
Of er een wachtwoord ingevuld is
Of het wachtwoord 2 maal identiek is
Of het e-mailadres ingevuld is, geldig is, en nog niet geregistreerd is
Ons verwerkingsblok gaat er dus zo uit zien:
Bestand: registratie (verwerkingsblok)
<?php
// Verwerking
// Eerst enkele functies schrijven die de controle vergemakkelijken
function geregistreerd($zoek, $veld) {
// Ik plaats de query op meerdere lijnen voor de leesbaarheid
$qZoek = mysql_query("
SELECT COUNT(".$veld.")
FROM gebruikers
WHERE ".$veld." = '".mysql_real_escape_string($zoek)."'");
// Als de gebruikersnaam nog niet bestaat, geeft dit false, anders true
return (mysql_result($qZoek,0) == 0 ? false : true);
}
// De reguliere expressies voor de controle op e-mailadressen is door FangorN samengesteld :]
function is_email($in) {
list($local, $host) = explode('@', $in);
$pattern_local = '^([0-9a-z]*([-|_]?[0-9a-z]+)*)(([-|_]?).'
. '([-|_]?)[0-9a-z]*([-|_]?[0-9a-z]+)+)*([-|_]?)$';
$pattern_host = '^([0-9a-z]+([-]?[0-9a-z]+)*)(([-]?).([-]?)'
. '[0-9a-z]*([-]?[0-9a-z]+)+)*.[a-z]{2,4}$'; $match_local = eregi($pattern_local, $local);
$match_host = eregi($pattern_host, $host);
// Hier zullen we de fouten in opslaan
$fouten = array();
if(empty($_POST['gebruikersnaam'])) {
$fouten[] = 'Er is geen gebruikersnaam opgegeven.';
} elseif(strlen($_POST['gebruikersnaam']) > 50) {
$fouten[] = 'De gebruikersnaam mag niet langer dan 50 tekens zijn.';
} elseif(geregistreerd($_POST['gebruikersnaam'], 'gebruikersnaam')) {
$fouten[] = 'De gebruikersnaam is reeds geregistreerd.';
}
if(empty($_POST['wachtwoord'])) {
$fouten[] = 'Er is geen wachtwoord opgegeven.';
} elseif($_POST['wachtwoord'] != $_POST['wachtwoord_herh']) {
$fouten[] = 'De wachtwoorden komen niet overeen.';
}
if(!is_email($_POST['email'])) {
$fouten[] = 'Er is geen geldig e-mailadres opgegeven.';
} elseif(geregistreerd($_POST['email'], 'email')) {
$fouten[] = 'Dit e-mailadres is reeds geregistreerd.';
}
// Het formulier is gecontroleerd, nu kijken we of er fouten opgetreden zijn
if(count($fouten) > 0) {
?>
<span style="color: #FF0000; ">De volgende fouten zijn opgetreden:</span>
<ul>
<?php foreach($fouten as $fout) { ?>
<li><?=$fout?></li>
<?php } ?>
</ul>
<?php
} else {
// Er zijn geen fouten, voer registratie uit!
}
?>
De foutcontrole is af, nu moet de registratieprocedure voltooit kunnen worden. Wat houdt een registratie in:
Er wordt een nieuwe rij in de database aangemaakt voor de gebruiker
Er wordt een nieuwe rij in de database aangemaakt voor de activatie
Er wordt een e-mail gezonden naar de gebruiker voor activatie
Nu zetten we deze drie zinnen om in code:
Bestand: registratie.php (registratiegedeelte)
<?php
// Er zijn geen fouten, voer registratie uit!
mysql_query("INSERT INTO gebruikers
(gebruikersnaam, wachtwoord, email, datum)
VALUES
('".mysql_real_escape_string($_POST['gebruikersnaam'])."',
'".sha1($_POST['wachtwoord'])."',
'".mysql_real_escape_string($_POST['email'])."',
NOW()
)");
$gebruikers_id = mysql_insert_id();
// Een volledig willekeurige code van 40 tekens
$activatiecode = sha1(md5(microtime()*rand(1,10)));
mysql_query("INSERT INTO gebruikers_activaties
(gebruikers_id, sleutel)
VALUES
(".$gebruikers_id.", '".$activatiecode."'");
// Voor de regeleinden
define('EOL', "
");
// Je kan deze mail uit uitbreiden, evt HTML mail,...
$mail = 'Bedankt voor je registratie, blaat, blaat, activeer hier:'.EOL;
$mail .= 'http://www.mijnsite.be/activeer.php?gebruiker='.$gebruikers_id.'&sleutel='.$activatiecode.EOL;
$mail .= 'Bye bye.';
mail($_POST['email'], 'Je account activeren', $mail, $header);
?>
Er is een e-mail gestuurd met activatiedetails.<?=$mail?>
<?php
// Formulier verbergen
$verberg_form = true;
}
?>
Op het einde van de registratie zetten we de variabele $verberg_form, deze moet dan nog rond je formulier geplaatst worden. De totale registratiepagina ziet er dus als volgt uit:
Bestand: registratie.php (volledig)
<?php
if($_SERVER['REQUEST_METHOD'] == 'POST') {
// Hier komt alle behandeling van het formulier
// Mocht er een fout optreden, is het leuk dat de gebruiker niet alles opnieuw hoeft
// in te vullen. Daarom vullen we de array $post met de velden,
// uiteraard door htmlentities() gehaald (veiligheidsmaatregel).
// Wachtwoord moeten wel opnieuw ingevuld worden, nogmaals veiligheidsmaatsregel
$post = array(
'gebruikersnaam' => htmlentities($_POST['gebruikersnaam']),
'email' => htmlentities($_POST['email']));
// Verwerking
// Eerst enkele functies schrijven die de controle vergemakkelijken
function geregistreerd($zoek, $veld) {
// Ik plaats de query op meerdere lijnen voor de leesbaarheid
$qZoek = mysql_query("
SELECT COUNT(".$veld.")
FROM gebruikers
WHERE ".$veld." = '".mysql_real_escape_string($zoek)."'");
// Als de gebruikersnaam nog niet bestaat, geeft dit false, anders true
return (mysql_result($qZoek,0) == 0 ? false : true);
}
// De reguliere expressies voor de controle op e-mailadressen is door FangorN samengesteld :]
function is_email($in) {
list($local, $host) = explode('@', $in);
$pattern_local = '^([0-9a-z]*([-|_]?[0-9a-z]+)*)(([-|_]?).'
. '([-|_]?)[0-9a-z]*([-|_]?[0-9a-z]+)+)*([-|_]?)$';
$pattern_host = '^([0-9a-z]+([-]?[0-9a-z]+)*)(([-]?).([-]?)'
. '[0-9a-z]*([-]?[0-9a-z]+)+)*.[a-z]{2,4}$'; $match_local = eregi($pattern_local, $local);
$match_host = eregi($pattern_host, $host);
// Hier zullen we de fouten in opslaan
$fouten = array();
if(empty($_POST['gebruikersnaam'])) {
$fouten[] = 'Er is geen gebruikersnaam opgegeven.';
} elseif(strlen($_POST['gebruikersnaam']) > 50) {
$fouten[] = 'De gebruikersnaam mag niet langer dan 50 tekens zijn.';
} elseif(geregistreerd($_POST['gebruikersnaam'], 'gebruikersnaam')) {
$fouten[] = 'De gebruikersnaam is reeds geregistreerd.';
}
if(empty($_POST['wachtwoord'])) {
$fouten[] = 'Er is geen wachtwoord opgegeven.';
} elseif($_POST['wachtwoord'] != $_POST['wachtwoord_herh']) {
$fouten[] = 'De wachtwoorden komen niet overeen.';
}
if(!is_email($_POST['email'])) {
$fouten[] = 'Er is geen geldig e-mailadres opgegeven.';
} elseif(geregistreerd($_POST['email'], 'email')) {
$fouten[] = 'Dit e-mailadres is reeds geregistreerd.';
}
// Het formulier is gecontroleerd, nu kijken we of er fouten opgetreden zijn
if(count($fouten) > 0) {
?>
<span style="color: #FF0000; ">De volgende fouten zijn opgetreden:</span>
<ul>
<?php foreach($fouten as $fout) { ?>
<li><?=$fout?></li>
<?php } ?>
</ul>
<?php
} else {
// Er zijn geen fouten, voer registratie uit!
mysql_query("INSERT INTO gebruikers
(gebruikersnaam, wachtwoord, email, datum)
VALUES
('".mysql_real_escape_string($_POST['gebruikersnaam'])."',
'".sha1($_POST['wachtwoord'])."',
'".mysql_real_escape_string($_POST['email'])."',
NOW()
)");
$gebruikers_id = mysql_insert_id();
// Een volledig willekeurige code van 40 tekens
$activatiecode = sha1(md5(microtime()*rand(1,10)));
mysql_query("INSERT INTO gebruikers_activaties
(gebruikers_id, sleutel)
VALUES
(".$gebruikers_id.", '".$activatiecode."')");
// Voor de regeleinden
define('EOL', "
");
// Je kan deze mail uit uitbreiden, evt HTML mail,...
$mail = 'Bedankt voor je registratie, blaat, blaat, activeer hier:'.EOL;
$mail .= 'http://www.mijnsite.be/activatie.php?gebruiker='.$gebruikers_id.'&sleutel='.$activatiecode.EOL;
$mail .= 'Bye bye.';
mail($_POST['email'], 'Je account activeren', $mail, $header);
?>
Er is een e-mail gestuurd met activatiedetails.
<?php
// Formulier verbergen
$verberg_form = true;
}
} else {
// Dit stuk wordt uitgevoerd als het formulier nog niet is verzonden.
// Het formulier is nog niet verzonden, dus is er ook geen $post array.
// we maken deze dus zelf aan met initiele waardes, om warnings te voorkomen.
$post = array(
'gebruikersnaam' => '',
'email' => '');
}
Wanneer de gebruiker een account heeft aangemaakt, moet hij/zij deze nog activeren. De gebruiker heeft reeds een mail ontvangen, die we verzonden hebben in onze registratie.php. Daarin verwezen we naar de link activatie.php?gebruiker=x&sleutel=y. Nu gaan we dus onze activatiepagina opstellen.
Pagina: activatie.php (volledig)
<?php
// Eerst een basiscontrole uitvoeren op de URL
if(!isset($_GET['sleutel']) || !isset($_GET['gebruiker'])) {
echo 'De URL is ongeldig.';
} else {
// Om een account te activeren moeten we gewoon de activatie-
// gegevens uit de tabel gooien:
mysql_query("DELETE FROM gebruikers_activaties WHERE
gebruikers_id = ".intval($_GET['gebruiker'])." AND
sleutel = '".mysql_real_escape_string($_GET['sleutel'])."'");
// Met mysql_affected_rows() kunnen we zien hoeveel rijen er 'aangetast' zijn,
// of in dit geval dus verwijderd. Zo weten we ook gelijk of de gegevens wel juist waren!
if(mysql_affected_rows() == 0) {
echo 'De account bestaat niet of was reeds geactiveerd.';
} else {
// Er is een rij aangetast, dus is het gelukt :)
echo 'De activatie is gelukt. Je kan nu <a href="login.php">Inloggen</a>';
}
}
?>
Zoals je ziet houdt de activatie niet veel in. Vervolgens kunnen we de loginpagina opstellen.
Login
Bij het inloggen hoort de volgende logica:
Controleer of de gebruiker bestaat
Controleer of de account geactiveerd is
Controleer of het wachtwoord klopt
Maak de sessie aan
Deze regels worden snel omgezet in PHP-code:
Pagina: login.php (volledig)
<?php
if($_SERVER['REQUEST_METHOD'] == 'POST') {
// Dit is niet mijn gewoonte om queries op deze manier in te springen, maar om de layout
// van Sitemasters niet naar de knoppen te helpen doe ik dit even gek :)
$qLogin = mysql_query("SELECT id FROM gebruikers WHERE
gebruikersnaam = '".mysql_real_escape_string($_POST['gebruikersnaam'])."' AND
wachtwoord = '".sha1($_POST['wachtwoord'])."'");
// Met deze query selecteren we het gebruikers-id van de gebruikersnaam en controleren we of
// de account geactiveerd is in 1 keer. Als je deze niet snapt, lees hem dan 20 keer :)
// Je kan natuurlijk ook een tutorial over dit soort queries lezen, alhoewel
// ze poepsimpel zijn IMO.
// Deze query mag overigens geen resultaten opleveren om te mogen inloggen. Als deze
// wél een resultaat oplevert is de account nog niet geactiveerd (en bestaat de
// activatiedata dus nog).
$qActivatie = mysql_query("SELECT COUNT(id) FROM gebruikers, gebruikers_activaties WHERE
gebruikers.gebruikersnaam = '".mysql_real_escape_string($_POST['gebruikersnaam'])."' AND
gebruikers.id = gebruikers_activaties.gebruikers_id");
if(mysql_result($qActivatie,0) != 0) {
echo 'De account is nog niet geactiveerd.';
} elseif(mysql_num_rows($qLogin) == 0) {
echo 'De gebruikersnaam/wachtwoord combinatie is niet correct.';
} else {
// Alles ok, inloggen dus.
// Nu is 1 ding belangerijk:
// ZORG DAT ER EEN session_start() GEPLAATST IS, HETZIJ IN JE INDEX.PHP WAAR
// DEZE PAGINA IN GEPLAATS WORDT, HETZIJ BOVENAAN DEZE PAGINA (ALS ER GEEN
// INCLUDESYSTEEM IS)!!!
$_SESSION['gebruiker'] = mysql_result($qLogin, 0);
$_SESSION['gebruikersnaam'] = $_POST['gebruikersnaam'];
// Mocht je nog andere data willen in je sessie,
// zoals een level, dien je dit hier te plaatsen.
?>
Je bent succesvol ingelogged. Klik <a href="controlepaneel.php">hier</a> om naar je controlepaneel te gaan.
<?php
}
}
?>
<form action="<?=htmlentities($_SERVER['REQUEST_URI'])?>" method="post">
Gebruikersnaam:<br />
<input type="text" name="gebruikersnaam" /><br />
Wachtwoord:<br />
<input type="password" name="wachtwoord" /><br />
<br />
<input type="submit" value="Inloggen die handel" />
</form>
Zoals je ziet is ook een loginpagina niet meer dan logisch denken en houdt het dus werkelijk niks in. Op naar het basis controlepaneel!
Controlepaneel
In het controlepaneel zullen we momenteel 2 opties voorzien, namelijk een link naar de uitlogpagina, en een formulier om je e-mailadres te veranderen.
Op het controlepaneel moet eerst en vooral gecontroleerd worden of we wel ingelogged zijn. Dit doe je simpelweg als volgt:
<?php
// Zorg dat session_start() hierboven ergens voorkomt,
// hetzij in je globale index.php,
// hetzij gewoon bovenaan dit bestand.
if(isset($_SESSION['gebruiker'])) {
echo 'Je bent ingelogged!';
} else {
echo 'Je bent niet ingelogged, klik <a href="login.php">hier</a> om dit te doen.';
}
?>
Binnen dit controlepaneel kunnen we gebruik maken van de variabelen $_SESSION['gebruiker'] en $_SESSION['gebruikersnaam'], die we bij het inloggen aangemaakt hebben.
Als we dus het e-mailadres willen wijzigen, is dit gewoon een query met als conditie dat de rij waar id gelijk is aan $_SESSION['gebruiker'] wordt geupdate.
Een linkje naar de uitlogpagina is peanuts en hoeft niet eens uitgelegd worden denk ik :)
Pagina: controlepaneel.php (volledig)
<?php
// Zorg dat hierboven ERGENS session_start() staat!
if(isset($_SESSION['gebruiker'])) {
// E-mailadres bijwerken:
// De variabele fout aanmaken om fouten ivm 'undefined variable' te voorkomen
$fout = '';
if($_SERVER['REQUEST_METHOD'] == 'POST') {
// Zelfde functie als bij registratie
// Een globale functies.php waarin je al je functies gooit is dus wel handig
function is_email($in) {
list($local, $host) = explode('@', $in);
$pattern_local = '^([0-9a-z]*([-|_]?[0-9a-z]+)*)(([-|_]?).'
. '([-|_]?)[0-9a-z]*([-|_]?[0-9a-z]+)+)*([-|_]?)$';
$pattern_host = '^([0-9a-z]+([-]?[0-9a-z]+)*)(([-]?).([-]?)'
. '[0-9a-z]*([-]?[0-9a-z]+)+)*.[a-z]{2,4}$';
$match_local = eregi($pattern_local, $local);
$match_host = eregi($pattern_host, $host);
if(!is_email($_POST['email'])) {
$fout = 'Geen geldig e-mailadres!';
} else {
mysql_query("UPDATE gebruikers SET
email = '".mysql_real_escape_string($_POST['email'])."'
WHERE id = ".$_SESSION['gebruiker']);
// Vervolgens moet de pagina vernieuwd worden.
header('Location: '.$_SERVER['REQUEST_URI']);
}
} else {
// Het formulier is nog niet verzonden, dus vullen we het veldje met het huidig e-mailadres
$email = mysql_result(mysql_query("SELECT email FROM gebruikers WHERE id = ".$_SESSION['gebruiker']),0);
}
?>
<h1>Welkom, <?=htmlentities($_SESSION['gebruikersnaam'])?>!</h1>
Wijzig je e-mailadres:
<?=$fout?>
<form action="<?=htmlentities($_SERVER['REQUEST_URI'])?>" method="post">
<input type="text" name="email" value="<?=htmlentities($email)?>" />
<input type="submit" value="Bijwerken" />
</form>
<a href="uitloggen.php" onclick="return confirm('Ben je zeker dat je je wilt afmelden?');">Uitloggen</a>
<?php
} else {
echo 'Je bent niet ingelogged, klik <a href="login.php">hier</a> om dit te doen.';
}
?>
Opmerking: mocht je hierbij een melding krijgen à la "Headers already sent..", dan kan je de header() vervangen door een JavaScript redirect, of kan je de F.A.Q. of het forumarchief van deze website eens bekijken.
Uitloggen
Het enige wat er moet gebeuren bij het uitloggen, is dat de sessie vernietigd wordt:
<?php
// Weeral, zorg dat session_start() hier ergens boven voorkomt
session_destroy();
?>
Je bent uitgelogd.
Gelieve geen domme comments te geven zoals "ik ben nooit ingelogged" terwijl je nergens een session_start() hebt, etc.