Cikklista lapozással PHP-ban és MySQL-ben
Közzétéve:
Az oldal cikklistája eddig egy hosszú, kézzel szerkesztett HTML fájl volt. Most átkerült MySQL-be, és kapott lapozást is. Leírom, hogyan működik, mert a lapozás az a feladat, amit mindenki megír egyszer, és elsőre szinte mindenki hibásan.
A tábla
CREATE TABLE cikkek (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
cim VARCHAR(200) NOT NULL,
szerzo VARCHAR(50) NOT NULL,
datum DATE NOT NULL,
bevezeto TEXT NOT NULL,
KEY (datum)
);
A lista
<?php
$perOldal = 10;
// 1. Oldalszám: ami a GET-ből jön, azt nem hisszük el
$oldal = isset($_GET['oldal']) ? intval($_GET['oldal']) : 1;
if ($oldal < 1) {
$oldal = 1;
}
$kezdet = ($oldal - 1) * $perOldal;
$db = mysql_connect('localhost', 'progz', 'jelszo') or die('Adatbázis hiba');
mysql_select_db('progz', $db);
// 2. Egy lekérdezés a listára és a teljes darabszámra
$sql = "SELECT SQL_CALC_FOUND_ROWS id, cim, szerzo, datum, bevezeto
FROM cikkek ORDER BY datum DESC
LIMIT $kezdet, $perOldal";
$eredmeny = mysql_query($sql, $db);
$osszes = mysql_result(mysql_query('SELECT FOUND_ROWS()', $db), 0);
$oldalakSzama = max(1, ceil($osszes / $perOldal));
// 3. Kiírás: minden szöveg htmlspecialchars-on át
while ($sor = mysql_fetch_assoc($eredmeny)) {
echo '<h3><a href="cikk.php?id=' . $sor['id'] . '">'
. htmlspecialchars($sor['cim']) . '</a></h3>';
echo '<p>' . $sor['datum'] . ' | ' . htmlspecialchars($sor['szerzo']) . '</p>';
echo '<p>' . htmlspecialchars($sor['bevezeto']) . '</p>';
}
// 4. Lapozósor
if ($oldal > 1) {
echo '<a href="?oldal=' . ($oldal - 1) . '"><<< Előző</a> ';
}
echo $oldal . '. oldal / ' . $oldalakSzama;
if ($oldal < $oldalakSzama) {
echo ' <a href="?oldal=' . ($oldal + 1) . '">Következő >>></a>';
}
?>
Miért pont így?
intval. Az intval('3; DROP TABLE cikkek') eredménye 3. Bármi kerül a címsorba, a lekérdezésbe csak egy szám jut. Mivel a $kezdet is ebből a számból jön, a LIMIT-be sem kerülhet szöveg.
SQL_CALC_FOUND_ROWS. A lapozósorhoz tudni kell, összesen hány cikk van. A naiv megoldás egy külön SELECT COUNT(*). A MySQL 4.0 óta viszont a SQL_CALC_FOUND_ROWS kulcsszóval a szerver a LIMIT ellenére is megszámolja a találatokat, és ezt a FOUND_ROWS() adja vissza.
htmlspecialchars. Egy < jel a cikk címében tönkreteszi az oldalt, egy <script> pedig ennél sokkal rosszabbat is tehet. A szabály egyszerű: ami adatbázisból jön, az kiírás előtt átmegy a htmlspecialchars-on.
Még két apróság
Ha a register_globals be van kapcsolva a tárhelyen, a $oldal változót kívülről is be lehetne állítani. A PHP 4.2 óta ez alapból ki van kapcsolva, de az olcsó tárhelyek egy részén még mindig be van. Ezért kezdi a fenti kód minden változó értékadását nulláról.
A jelszó pedig ne a listázó fájlban legyen, hanem egy külön config.php-ban, lehetőleg a webgyökéren kívül. Így ha egyszer rosszul áll be a szerver, és a PHP forráskód szövegként jelenik meg, a jelszó akkor sem kerül ki.