Tutorial PHP (Μέρος ΙΙ): Βάσεις Δεδομένων & Επαλήθευση χρηστών

dimitris | Τρί, 07/08/2008 - 12:38 | 23' | 6

Στο πρώτο μέρος αυτής της σειράς οδηγών για PHP, κάναμε την πρώτη μας επαφή με τη γλώσσα με ένα απλό παράδειγμα. Είδαμε πως χρησιμοποιούμε μεταβλητές, arrays και μερικές ενσωματωμένες συναρτήσεις. Παραλείψαμε όμως να αναφερθούμε στην var_dump() και την count(). Η πρώτη παίρνει ως παραμέτρους μια ή περισσότερες μεταβλητές και επιστρέφει καλά δομημένες πληροφορίες για αυτές. Μπορεί να χειριστεί και arrays, εμφανίζοντας όλες τις μεταβλητές τους. Καλό είναι να ενσωματώνουμε την var_dump() μέσα στις ετικέτες < pre > και </pre> της HTML προκειμένου να εμφανίζονται οι πληροφορίες με την αρχική δομή τους.

Μια ακόμη χρήσιμη συνάρτηση για arrays είναι η count() που δέχεται ως μόνη παράμετρο ένα array και επιστρέφει το πλήθος των στοιχείων του. Να θυμάστε ότι η PHP δουλεύει με arrays που ξεκινούν από το στοιχείο με δείκτη 0 και έτσι το τελευταίο στοιχείο θα αναφέρεται κάπως έτσι:

count($myarray) - 1

Πριν περάσουμε στο πρώτο ολοκληρωμένο παράδειγμα της PHP πρέπει να δούμε τη συνάρτηση mail() για αποστολή μηνύματος. Η mail() δέχεται κατ' ελάχιστο τρεις παραμέτρους: την διεύθυνση του παραλήπτη, τη γραμμή με το θέμα και το σώμα του κειμένου του emai. Μπορούμε όμως να βάλουμε και μια τέταρτη παράμετρο, για εξτρά headers. Το κείμενο του μηνύματος και τα headers πρέπει να διαχωρίζονται από μια νέα γραμμή (\n):

 <?php
   $message = "Line 1\n Line 2\n Line 3\n Line 4\n Line 5\n";
   mail("[email protected]", "Δοκιμή της mail()!", $message, "From: [email protected]");
 ?>

Με τα εξτρά headers στην τέταρτη παράμετρο, μπορούμε να στείλουμε HTML μηνύματα, να καθορίσουμε την διεύθυνση απάντησης (Reply-To) ή κάθε άλλη πληροφορία.

Ένα "πλήρες" πρόγραμμα

Με όσα είδαμε την προηγούμενη φορά και τις παραπάνω συναρτήσεις μπορούμε να φτιάξουμε ένα αρκετά καλό σκριπτάκι της PHP. Προς το παρόν δεν υποστηρίζει βάσεις δεδομένων, αλλά αυτό θα το δούμε αμέσως μετά:

<?php
   if (isset($_POST[‘user’])) {
     if (($_POST[‘user’] != ‘’) && ($_POST[‘pass’]) != ‘’) {
       echo "Ο λογαριασμός σας δημιουργήθηκε επιτυχώς!";
       mail($_POST[‘email’], "Πληροφορίες λογαριασμού", "Ευχαριστούμε που εγγραφήκατε στο  somesite.com!", "From: [email protected]");
     } else {
       echo "Πρέπει να δώσετε ένα όνομα χρήστη και κωδικό!";
     }
   } else {
 ?>

<p>Για να δημιουργήσετε νέο λογαριασμό, δώστε όνομα, κωδικό και email διεύθυνση.</p>
 
  <form method="post">
    Username: <input name="user" type="text" /><br />
    Password: <input name="pass" type="password" /><br />
    Email Address: <input name="email" type="text" /><br />
    <input type="submit" /> 
  </form>
 }
?>

Βάσεις δεδομένων

Από την έκδοση 4, η PHP έχει το πλεονέκτημα να υποστηρίζει ενδογενώς τη MySQL. Αυτό ήταν το αποτέλεσμα πολλών συναντήσεων που είχαν οι δύο ομάδες ανάπτυξης με σκοπό να ενισχύσουν την ήδη πολύ καλή σχέση μεταξύ των δύο έργων. Το αποτέλεσμα αυτής της ολοκλήρωσης αλλά και των πολλών πλεονεκτημάτων της MySQL είναι ότι η τελευταία είναι σήμερα με διαφορά το πιο δημοφιλές σύστημα διαχείρισης βάσης δεδομένων (ΒΔ) για χρήση με την PHP.  

Στα παρακάτω, υποθέτουμε ότι έχει δημιουργήσει ένα χρήστη 'php' στη MySQL, του έχετε δώσει τον κωδικό 'alm65z', και έχετε φτιάξει μια βάση δεδομένων με όνομα phpdb. Ο τρόπος δημιουργίας χρηστών στη MySQL περιγράφεται στην τεκμηρίωσή της αλλά συνήθως αρκούν οι εξής δύο εντολές το prompt της MySQL:

create database 'lxf';

grant all privileges on lxf.* to 'php'@'localhost' identified by 'alm65z';

Ας επανέλθουμε όμως στην PHL. Το σκριπτάκι μας τώρα γίνεται:

<?php
 mysql_connect("localhost", "php", "alm65z");
 mysql_select_db("lxf');
 $result = mysql_query("select * from users where id = ‘$ID’ and password = ‘$pass’;");
 if (mysql_num_rows($result)) {
  echo "You have been authenticated successfully!";
 } else {
  echo "Invalid username and password!";
 }
?>

Οι πρώτες δύο γραμμές κάνουν τη σύνδεση με το MySQL server στο τοπικό μηχάνημα (localhost) χρησιμοποιώντας όνομα χρήστη 'php' και κωδικό 'alm65z'. Η υποβολή ενός ερωτήματος (query) στη MySQL γίνεται με τη συνάρτηση mysql_query(). Αυτή επιστρέφει μια αναφορά (στη μεταβλητή $result) σε όλες τις γραμμές της ΒΔ που ταιριάζουν με το ερώτημά μας. Μόλις έχουμε την αναφορά μπορούμε να κάνουμε διάφορες ενέργειες σε αυτήν μέσω συναρτήσεων για να πάρουμε περισσότερες πληροφορίες από τα δεδομένα μας. Στο παραπάνω παράδειγμα καλούμε την mysql_num_rows(). Η τελευταία επιστρέφει τον αριθμό των γραμμών που περιέχει η αναφορά, δηλαδή τον αριθμό των γραμμών που ταιριάζουν με το ερώτημά μας. Η διακλάδωση if μας εξασφαλίζει ότι θα τυπωθεί το μήνυμα επιτυχίας στην περίπτωση που υπάρχει τουλάχιστον μια γραμμή στα δεδομένα που να ταιριάζει με το ερώτημά μας (το όνομα χρήστη και τον κωδικό).

H PHP υποστηρίζει πολλές ακόμα ΒΔ, όπως την PostgreSQL, την Oracle και το MS SQL Server. Μπορούμε να υποβάλουμε ένα ερώτημα στην PostgreSQL αλλάζοντας τη συνάρτηση mysql_query() με την pg_query() και να μετρήσουμε τις γραμμές που θα επιστρέψει με την pg_num_rows().

Ας δούμε όμως ένα ακόμα παράδειγμα αλληλεπίδρασης με ΒΔ. Αυτό λειτουργεί ως υπενθύμιση του κωδικού ενός χρήστη:

<?php
 if (isset($_POST[‘email’])) {
   mysql_connect("localhost", "php", "alm65z");
   mysql_select_db("lxf");
   $EmailToCheck = $_POST[‘email’];
   $result = mysql_query("select username, password from users where emailaddress = ‘$EmailToCheck’;");
   if (mysql_num_rows($result)) {
     $r = mysql_fetch_array($result);
     $TheirUser = $r[‘username’];
     $TheirPW = $r[‘password’];
     mail($EmailToCheck, "Password Reminder", "Your username is $TheirUser, and your password is $TheirPW.", "From: [email protected]");
   echo "Your info have been emailed to you at $EmailToCheck";
   } else {
     echo "We do not have an account for that email address!";
  }
} else {
?>
Για να ανακτήσετε το όνομα χρήστη και τον κωδικό σας, πρέπει να δώσετε τη διεύθυνση email σας.
<form method="post">
 Email Address: <input name="email" type="text" /><br />
 <input type="submit" /> 
</form>
<?php
}
?>

Τα νέα στοιχεία του παραδείγματος βρίσκονται κοντά στη συνάρτηση mysql_fetch_array(). Επειδή ένα ερώτημα στη MySQL μπορεί να επιστρέψει πολλές γραμμές, η PHP μας επιτρέπει να χειριστούμε κάθε γραμμή ανεξάρτητα. Για παράδειγμα, η mysql_fetch_row() παίρνει την επόμενη γραμμή ξεκινώντας από την πρώτη, διαβάζει τις τιμές σε όλα τα πεδία της και τα τοποθετεί σε ένα array (εδώ το $r). Έτσι τα στοιχεία $r[‘username’] και $r[‘password’] αποκτούν αυτόματα τις τιμές του ονόματος χρήστη και του κωδικού που ταιριάζει με την διεύθυνση email που έδωσε ο χρήστης.

Επαλήθευση

Απ' ότι είδαμε μέχρι εδώ, καταλαβαίνει κανείς ό,τι η PHP είναι μια γλώσσα που επιτρέπει ακόμα και στον αρχάριο να γράψει απλά σκριπτάκια σε πολύ μικρό χρόνο. Παρακάτω θα χρησιμοποιήσουμε ό,τι μάθαμε για να κάνουμε αναγνώριση και επαλήθευση (authentication) χρήστη μέσω HTTP.

Η επαλήθευση HTTP παραδοσιακά έχει τη μορφή διάσπαρτων αρχείων .htaccess που βρίσκονται σε όσους φακέλους θέλουμε να κρατήσουμε απροσπέλαστους. Ένα τυπικό αρχείο .htaccess σε συνδυασμό με ένα αρχείο .htpasswd περιέχει πληροφορίες για τους χρήστες που έχουν δικαίωμα πρόσβασης σε ένα φάκελο. Αν και ο Apache επιτρέπει την προσαρμογή των δικαιωμάτων πρόσβασης, αυτή η μέθοδος δεν είναι βολική. Κανείς δεν θέλει να επεξεργάζεται αρχεία κάθε φορά που προσθέτει χρήστες.

Η επαλήθευση HTTP συνίσταται κυρίως στην αποστολή ειδικών HTTP headers στον "πελάτη' (client) με τους οποίους του ζητείται να αποστείλει στοιχεία πρόσβασης, και είναι αρκετά εύκολη στην υλοποίηση με PHP αρκεί να την έχουμε εγκαταστήσει ως module του Apache. Για να δούμε ένα βασικό παράδειγμα επαλήθευσης, πρέπει να δημιουργήσουμε το αρχείο 'auth.php' το οποίο θα περιέχει τα εξής:

<?php
 if (!isset($_SERVER[‘PHP_AUTH_USER’])) {
  header('WWW-Authenticate: Basic realm="LXF Private Area"');
  header('HTTP/1.0 401 Unauthorized');
  // Αν δεν δώσει στοιχεία
  echo "Λυπούμαστε. Πρέπει να είστε μέλος για να δείτε αυτή την ιδιωτική περιοχή!\n";
  exit;
 } else {
  // Αν δώσει στοιχεία
  echo "Καλωσήλθατε στην περιοχή,
  {$_SERVER[‘PHP_AUTH_USER’]} - χρησιμοποιήσατε το {$_SERVER[‘PHP_AUTH_PW’]} ως κωδικό.";
 }
?>

Η πρώτη γραμμή είναι ο πυρήνας της επαλήθευσης με PHP.  Όταν οι χρήστες δίνουν όνομα και κωδικό, η PHP τα αποθηκεύει στις μεταβλητές $_SERVER[‘PHP_AUTH_USER’] και $_SERVER[‘PHP_AUTH_PW’] αντίστοιχα. Ελέγχοντας αν έχει οριστεί (isset) η $_SERVER[‘PHP_AUTH_USER’] είναι σαν να λέμε "Έδωσε ο χρήστης το όνομά του;"

Αν ο χρήστης δεν έχει δώσει ονομα και κωδικό, τότε στέλνουμε ένα αίτημα επαλήθευσης με την WWW-Authenticate. Αυτό γίνεται στέλνοντας δύο HTTP headers με τη συνάρτηση header() της PHP. Με την header() μπορούμε να στείλουμε οποιαδήποτε header θέλουμε αρκεί να το κάνουμε πριν την αποστολή κώδικα HTML. Θα χρησιμοποιήσουμε την header() στο μέλλον, αλλά προς το παρόν μας ενδιαφέρει μόνο ο header WWW- Authenticate και οι κωδικοί κατάστασης HTTP. To WWW-Authenticate μας επιτρέπει να ορίσουμε μια περιοχή όπου η πρόσβαση είναι περιορισμένη. Αυτή μπορεί να είναι, λόγου χάρη, μια περιοχή για μέλη μόνο με τίτλο "Members Area". Το όνομα της περιοχής εμφανίζεται στους χρήστες όταν καλούνται να δώσουν όνομα μέλους (χρήστη) και τον κωδικό τους.

Η δεύτερη header() στέλνει τον HTTP κωδικό κατάστασης "401" που σημαίνει απλά "δεν έχετε πρόσβαση". Αυτό συνήθως σημαίνει ότι είτε δεν δόθηκε όνομα χρήστη και κωδικός είτε δεν είναι σωστά. Έτσι, ενώ η WWW-Authenticate λέει στον browser τι χρειάζεται για να ταυτοποιηθεί ο χρήστης ως μέλος, η 401 του απαγορεύει την πρόσβαση και φυσικά χρειάζονται και οι δύο για σωστή επαλήθευση. Εάν ο χρήστης πατήσει "Cancel" θα πρέπει να εμφανιστεί κάτι άλλο και όχι μια απλή κενή ιστοσελίδα. Στο παραπάνω παράδειγμα, εμείς έχουμε βάλει τη γραμμή echo() που του λέει "Λυπούμαστε. Πρέπει να είστε μέλος...".

Αν ο χρήστης δώσει όνομα και κωδικό, τότε το σκριπτάκι ξεκινά ξανά. Αυτή τη φορά η δήλωση if αληθεύει και κάνει echo το μήνυμα καλωσορίσματος.  Δηλαδή η τελευταία δήλωση echo, "Καλωσήλθατε στην περιοχή" αφορά όσους επαληθεύτηκαν επιτυχώς. Οι αγκύλες (τα σύμβολα { και }) χρησιμοποιούνται μέσα στην τελευταία δήλωση echo γιατί με αυτήν τυπώνουμε μια τιμή ενός array και οι αγκύλες λένε στην PHP ότι θα πρέπει να χειριστεί το $_SERVER[‘PHP_AUTH_USER’] σαν μια μεταβλητή array που πρέπει να την αντικαταστήσει με την τιμή της.

Θα παρατηρήσατε όμως ότι, προς το παρόν, αρκεί να δώσει κανείς οποιοδήποτε όνομα και κωδικό για να περάσει την επαλήθευση, αφού δεν ελέγχουμε τι ακριβώς δίνει! Τα περισσότερα sites θα θέλουν φυσικά να έχουν κάποιο τρόπο ελέγχου του ονόματος χρήστη και του κωδικού του για να έχει αξία η επαλήθευση. Γι' αυτό αλλάζουμε το σκριπτάκι μας προσθέτοντάς του έναν απλό έλεγχο των πληροφοριών:

<?php
 if (!isset($_SERVER[‘PHP_AUTH_USER’])) {
   header('WWW-Authenticate: Basic realm=\"LXF Private Area\"');
   header('HTTP/1.0 401 Unauthorized');
   echo "Λυπούμαι. Δεν υπάρχει τέτοιος χρήστης.\n";
   exit;
 } else {
   if (($_SERVER[‘PHP_AUTH_USER’] == ‘lxf’) && ($_SERVER[‘PHP_AUTH_PW’] == ‘bestseller’)) {
     echo "Καλωσήλθατε στην ιδιωτική περιοχή!";
   } else {
     header('WWW-Authenticate: Basic realm="LXF Private Area"');
     header('HTTP/1.0 401 Unauthorized');
    echo "Λυπούμαι. Δεν υπάρχει τέτοιος χρήστης.\n"exit;
   }
 }
?>

Ο παραπάνω κώδικας επιτρέπει την πρόσβαση μόνο στους χρήστες με όνομα 'lxf' και κωδικό 'bestseller'. Έχουμε δύο συνθήκες συνδυαζόμενες με ένα λογικό ΚΑΙ (&&) που σημαίνει ότι η δήλωση if είναι αληθής μόνο αν και το όνομα είναι 'lxf' και ο κωδικός είναι 'bestseller'. Το σύστημά μας είναι πολύ καλύτερο τώρα, αλλά θα πρέπει να γράφουμε μέσα στον κώδικα τα ονόματα των χρηστών και τους κωδικούς τους, πράγμα ασύμφορο.

Δυναμική επαλήθευση

Μια πολύ καλύτερη μέθοδος επαλήθευσης των χρηστών είναι να συγκρίνουμε τα στοιχεία τους με έναν πίνακα μελών μιας Βάσης Δεδομένων (ΒΔ). Αποθηκεύοντας όλα τα δεδομένα σε μία ΒΔ, μπορούμε εύκολα να προσθέτουμε, να τροποποιούμε και να αφαιρούμε την πρόσβαση από τους χρήστες χρησιμοποιώντας απλές PHP ιστοσελίδες και λίγες εντολές SQL. Στα επόμενα άρθρα θα δούμε περισσότερα για τις ΒΔ, αλλά προς το παρόν ας δούμε ένα απλό παράδειγμα για να κάνουμε πολύ πιο καλή την επαλήθευση μέσω SQL. Φυσικά θα πρέπει να έχουμε εγκαταστήσει τη MySQL στο σύστημά μας.

Μετά, πηγαίνουμε στην κονσόλα της MySQL:

sudo mysql

επιλέγουμε τη βάση δεδομένων μας:

use lxf;

και δίνουμε το εξής ερώτημα με το οποίο δημιουργείται ο απαραίτητος πίνακας που θα κρατά τα στοιχεία των χρηστών-μελών:

CREATE TABLE userauth (ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, Username VARCHAR(30), Password VARCHAR(30));

Η εντολή αυτή δημιουργεί έναν πίνακα με όνομα "userauth' που περιέχει τρία πεδία δεδομένων σε κάθε γραμμή: έναν ακέραιο ID και τα μεταβλητού μεγέθους πεδία χαρακτήρων "Username" και "Password" , δηλαδή ό,τι χρειαζόμαστε για να επαληθεύουμε τους χρήστες. Το ID το έχουμε βάλει για να αναφερόμαστε σε κάθε ξεχωριστή γραμμή. Έτσι μπορούμε να αναφερόμαστε σε έναν έγκυρο χρήστη με τον αριθμό της γραμμής παρά με το όνομα και τον κωδικό του. Για ευκολία, θα επιτρέψουμε στους χρήστες να προσθέτουν τους εαυτούς τους στον πίνακα επαλήθευσης. Δημιουργούμε λοιπόν το αρχείο ‘addauth.php’ που θα έχει τον εξής κώδικα:

<html>
<body>
<?php
 if (isset($_POST[‘username’])) {
  mysql_connect("localhost", "php", "alm65z");
  mysql_select_db("lxf");
  mysql_query("INSERT INTO userauth (Username, Password) VALUES ({$_POST[‘username’]}, {$_POST[‘password’]});");
  echo "Welcome to the system, {$_POST[‘username’]}!";
 ?>
 <?php } else { ?>
  <form method="post" action="addauth.php">
    Username: <input type="text" name="username" /><br />
    Password: <input type="password" name="password" /><br />
   <input type="submit" value=" Add User ">
   </form>
 <?php } ?>
</body>
</html>

Σημειώνουμε ότι στο παράδειγμα χρησιμοποιούμε τη βάση δεδομένων "lxf". Εσείς μπορείτε να την έχετε ονομάσει όπως αλλιώς θέλετε.

Με κλήση στο mysql_query() στην αρχή του script, εισάγουμε το νέο ζευγάρι στοιχείων (όνομα και κωδικό) στον πίνακα και στο πελάτη στέλνεται ένα μικρό μήνυμα επιβεβαίωσης. Αν τρέξουμε το script, μπορούμε να παρακολουθούμε τις αλλαγές που κάνει αυτό στον πίνακα userauth της βάσης lxf από το prompt της MySQL δίνοντας απλά την εντολή:

SELECT * FROM userauth;

Τώρα που οι χρήστες μπορούν δυναμικά να προστίθενται με το addauth.php, μπορούμε να τροποποιήσουμε το αρχικό σκριπτάκι auth.php για να ελέγχουμε τα στοιχεία που εισάγει κάθε επισκέπτης σε σχέση με αυτά που υπάρχουν στη ΒΔ. Γι' αυτό τροποποιούμε την γραμμή

if (($_SERVER[‘PHP_AUTH_USER’] == ‘lxf’) && ($_SERVER[‘PHP_AUTH_PW’] == ‘bestseller’)) {

ως εξής:

mysql_connect("localhost", "php", "alm65z");
mysql_select_db("lxf");
$result = mysql_query("SELECT ID FROM userauth WHERE Username = ‘{$_SERVER[‘PHP_AUTH_USER’]}’ AND Password = ‘{$_SERVER[‘PHP_AUTH_PW’]}’;");
if (mysql_num_rows($result)) { 
</yourmysqlpassword></yourmysqlusername>

Αντί να συγκρίνουμε τα εισαχθέντα από το χρήστη στοιχεία (όνομα και κωδικό) με δεδομένα που έχουμε μέσα στον κώδικα του script, ελέγχουμε τώρα αν υπάρχουν αυτά στον πίνακα userauth. Αν η συνάρτηση mysql_num_rows($result) επιστρέψει τουλάχιστον μία γραμμή του πίνακα, τότε υπάρχει τουλάχιστον ένα μέλος με τα στοιχεία που δόθηκαν και μπορούμε να επιτρέψουμε την πρόσβαση. Παρατηρήστε ότι είπαμε "τουλάχιστον ένα μέλος", γιατί προς το παρόν δεν έχουμε τρόπο να σταματάμε δύο διαφορετικούς χρήστες από το να χρησιμοποιούν το ίδιο όνομα. Αυτά την επόμενη φορά!

Αναδημοσίευση από το τεύχος 11 του Ελληνικού Linux Format

Φόρουμ
Tags
Δώσε αστέρια!

MO: 1 (ψήφοι: 1)

Σχόλια

Δημήτρη πολύ ωραία τα tutorial σου, μπράβο. Πιστεύω θα βοηθούσε πολύ να υπήρχε εγκαταστημένο στο site του linux format κάποιο module που να επιτρέπει το syntax highlighting (όπως το GeShi) ώστε να φαίνεται όμορφα ο κώδικας που γράφεται σε τέτοια tutorial, γιατί τώρα όποιος διαβάζει τα tutorial βγάζει τα μάτια του στον κώδικα...

Είδα τελικά ότι ενεργοποιήθηκε το GeShi module, αλλά ο κώδικας συνεχίζει να έχει πρόβλημα. Από ότι κατάλαβα εγώ που έχω επίσης το GeShi εγκαταστημενο σε ένα drupal site, δεν συνεργάζεται καλά με τους wysiwyg editors. Θα πρέπει στα post που γράφεται ο κώδικας, να γίνεται πρώτα disabled o rich-text editor

Μια χαρά φαίνεται νομίζω. Μένει τώρα να τσεκάρουμε για προβλήματα που δημιουργήθηκαν στα υπόλοιπα threads λόγω της αλλαγής...

--Stat rosa pristina nomine, nomina nuda tenemus

Ναι τώρα φαίνονται μια χαρά!

___________________________________________________________________

SCO-Unix

Καλησπέρα! Είμαι νέος στην PHP5 και ενώ έχω εγκαταστήσει Apache2 και MySQL στο OPENSuSe11, δε μπορώ να δω τα σκριπτάκια της PHP. Μήπως μπορεί κάποιος να με βοηθήσει? Ευχαριστώ