Dar vieno analitiko svetainė

Petras Kudaras

Perliška vartotojų sistema

Skaičiausi čia neseniai apie Perlo modulį Class::DBI ir šiandieną sumaniau jį šiek tiek išbandyti. Šis modulis yra duomenų bazės objektinė abstrakcija, ir viską daro tokiame aukštame lygyje (t.y. taip toli nuo pačios duomenų bazės), kad norint ja naudotis net nereikia mokėti SQL (OK, OK, SQL visada pravartu mokėti, bet labai paprastiems dalykams gali to ir neprireikti). Taigi išbandymui sugalvojau pasirašyti vartotojų prisijungimo sistemą.

Class::DBI priverčia viską apgalvoti ir išdėlioti objektiškai, į atskirus modulius, tad atsiranda krūvos mažų failiukų, kuriuos reikia dėti kažkur į atskirą lib direktoriją. Bet pirma pradėkim nuo SQL lentelės:

CREATE TABLE `vartotojai` (
  `vartotojoid` int(11) NOT NULL auto_increment,
  `login` varchar(40) NOT NULL default '',
  `pass` varchar(32) NOT NULL default '',
  `status` int(11) NOT NULL default '0',
  `vartotojaskada` timestamp(14) NOT NULL,
   PRIMARY KEY  (`vartotojoid`),
   UNIQUE KEY `login` (`login`)
) TYPE=MyISAM PACK_KEYS=0 AUTO_INCREMENT=1;

Tiesa, login yra unikalus, tad realiai užtektų tik jį padaryt pirminiu raktu, bet man kažkodėl patinka turėti ir skaitinį vartotojo ID. Nevisai dar sugalvojau kodėl.

Pradedam apsirašinėti duomenų bazę. Byloje lib/Bilekas/DBI.pm saugom duomenų bazės prisijungimo duomenis:

package Bilekas::DBI;
use base 'Class::DBI';
Bilekas::DBI->set_db('Main', 'dbi:mysql:bilekas', "root", "");
1;

Apsirašome klasę Bilekas::Vartotojas ir išsaugom byloje lib/Bilekas/Vartotojas.pm:

package Bilekas::Vartotojas;
use base 'Bilekas::DBI';
Bilekas::Vartotojas->table('vartotojai');
Bilekas::Vartotojas->columns(All => qw/vartotojoid login pass status vartotojaskada/);
1;

Bilekas::Vartotojas modulyje užtenka tik nurodyti kurią SQL lentelę naudoti, bei kokie yra tos lentelės stulpeliai (jeigu būtų ir daugiau lentelių tai reikėtų nurodyti ir ryšius tarp jų)

Galvojant apie ateitį, reikėtų pasirašyti ir vieną pagrindinį modulį Bilekas.pm, kuris būtų atsakingas už bendrą visai svetainei kodą (reikiamų modulių užkrovimą, CGI objekto sukūrimą, sesijas ir pan). Štai ir Bilekas.pm:

package Bilekas;
use strict;
use CGI;
use CGI::Session;

BEGIN {
        use Exporter ();
	our (@ISA, @EXPORT);
        @ISA = qw/Exporter/;
        @EXPORT = qw/$q $s/;
}

our $q = new CGI;
our $s = new CGI::Session("driver:File", $q, {Directory => '/tmp'});

END {
	$s->close;
}
1;

Šiame pakete yra šiek tiek magijos, kurią vertėtų paaiškinti. Pačioje pradžioje yra užkraunami moduliai CGI ir CGI::Session. Toliau eina BEGIN blokas, kuriuo iš paketo Bilekas vardų srities (namespace) eksportuojami kintamieji $q ir $s – tai leidžia vėliau bet kurioje byloje parašius use Bilekas; iš karto naudotis jau sukurtais ir užpildytais kintamaisiais $q ir $s (viename iš jų yra CGI objektas, o kitame – sesijos objektas).

Kitomis dvi eilutėmis yra sukuriami minėtieji eksportuojami kintamieji, o po to seka END blokas, kuri yra vykdomas paskutiniu programos gyvavimo metu. Jo pareiga yra teisingai įrašyti visus sesijos duomenis į bylą, kad jie neliktų laikinojoje atmintinėje (tą daro $s->close;)

Kadangi turime šitus modulius, galima pradėti jais naudotis. Pirma parašome index.pl, kuris nesant užsiregistravusio vartotojo rodys formą, kurios pagalba galima prisijungti prie sistemos:

#!/usr/bin/perl -w
use strict;
use lib './lib';

# Mūsų parašytieji moduliai
use Bilekas;
use Bilekas::Vartotojas;

# Siunčiame HTTP antraštes (su sausainiuku su sesijos informacija)
print $s->header;

# Jeigu yra nustatytas sesijos kintamasis, pranešantis apie klaidą, tai ją išspausdiname
if($s->param("bilekas_error")) {
        print "<h1>!!! " . $s->param("bilekas_error") . " !!!</h1>";
	# Išspausdinę klaidos pranešimą ištriname šį sesijos kintamąjį, kad
	# nespausdintume klaidos keletą kartų
        $s->clear(['bilekas_error']);
}

# Jeigu yra nustatytas sesijos kintamasis 'user', reiškia vartotojas prisijungęs
if($s->param('user')) {
        print "Esi isilogines kaip UID <em>", $s->param('user')->{vartotojoid}, "</em>";
} else {
	# Vartotojas neprisijungęs, rodome prisijungimo formą
        print <<EOHTML;
        <form action="login.pl" method="post">
        <input type="text" name="user"><br>
        <input type="password" name="pass"><br>
        <input type="submit">
        </form>
EOHTML
}

Beliko parašyti patį prisijungimo kodą login.pl:

#!/usr/bin/perl -w
use strict;
use lib './lib';

use Bilekas;
use Bilekas::Vartotojas;
use Digest::MD5 qw/md5_hex/;

my $location = $q->referer || 'index.pl';

if ($q->param('user') && $q->param('pass')) {
        my $user = Bilekas::Vartotojas->retrieve(login => $q->param('user'),
                                                 pass  => md5_hex($q->param('pass')));
	if ($user) {
		$s->param('user', $user);
		$s->expire(user => "+15m");
	} else {
		$s->param('bilekas_error', "Blogas login/pass");
        }
}

print $s->header(-location => $location);