Enostaven MySQL wrapper za PHP

2 minute read

Opažam, da ima večina začetnikov težave z escapanjem narekovajev v MySQL poizvedbah, skrbi jih SQL injection (ko izvedo zanj), probleme z njim je imel tudi samooklicani “prvi slovenski internetni časopis”. Ponujam enostavno rešitev.

SQL wrapperjev za PHP obstaja malo morje, večina je glomaznih, tale podpira sicer samo MySQL, je pa zelo enostaven. Podobno zadevo uporabljam tudi v mojih projektih, s kar nekaj naprednimi možnostmi, ki pa bi precej zakomplicirale tole, pa jih bom rajši preskočil.

Najprej DB razred:

class DB {

    protected $connection;

    public function __construct(
            $dbHost,
            $dbUser,
            $dbPass,
            $dbDatabase
        ) {
        if ($this->connection = mysql_connect(
                $dbHost,
                $dbUser,
                $dbPass
            ))
            mysql_select_db(
                    $dbDatabase,
                    $this->connection
                );
        else
            die('Cannot connect!');
    }

    public function query(
            $sqlExpression,
            $paramList = NULL
        ) {
        $sqlExpression = preg_replace(
                '!\$([1-9][0-9]*)!e',
                '$this->getParam($paramList[\\1-1])',
                $sqlExpression
            );
        return new DB_RESULT(
                mysql_query(
                        $sqlExpression,
                        $this->connection
                    )
            );
    }

    protected function getParam(
            $value
        ) {
        if (is_numeric($value))
            $returnVal = $value;
        else
            $returnVal =
                '\''.
                mysql_real_escape_string(
                        $value,
                        $this->connection
                    ).
                '\'';
        return $returnVal;
    }

}

Nobenih hudih posebnosti, konstruktor razreda se priklopi na podatkovni strežnik, metoda query() pa izvede poizvedbo, parametre poizvedbi ji podamo kot array. V poizvedbi parametre označimo z znakom $ in zaporedno številko takoj za njim, recimo: $1, $2, $3, … Poglejmo primer:

$DB = new DB(
        'localhost', // database host
        'foo',       // database user
        'bar',       // database password
        'foobar'     // database name
    );

$result = $DB->query(
        'SELECT '.
                '`id`, `username`, `password` '.
            'FROM '.
                '`users` '.
            'WHERE '.
                '`username` = $1',
        array(
                'vini'
            );
    );

Kaj smo storili? Najprej smo se seveda priklopili na podatkovni strežnik, nato pa smo izvedli poizvedbo, v kateri smo uporabili en parameter, vrednost smo mu pa določili v prvem elementu arraya. Pozor, v PHPju je prvi element arraya v bistvu nulti element ($array[0]), tukaj sem izbral ljudem bolj berljivo metodo, da je prvi element tudi označen s številko 1.

Metoda query() je poskrbela tudi za escapanje narekovajev, seveda samo v parametrih.

Še implementacija razreda DB_RESULT:

class DB_RESULT {

    protected $result;

    public function __construct(
            $result
        ) {
        $this->result = $result;
    }

    public function fetch_result(
            $row,
            $col
        ) {
        return
            mysql_result(
                    $this->result,
                    $row,
                    $col
                );
    }

    public function fetch_assoc() {
        return
            mysql_fetch_assoc(
                    $this->result
                );
    }

    public function free() {
        mysql_free_result(
                $this->result
            );
    }
}

Spet zelo enostavno, objekt razreda DB_RESULT nam vrne metoda query() razreda DB. Implementira tri metode za manipulacijo z rezultatom MySQL poizvedbe. Metoda fetch_result() je ekvivalent funkcije mysql_result(), metoda fetch_assoc() je ekvivalent funkcije mysql_fetch_assoc(), metoda free() pa ekvivalent funkcije mysql_free(). Razlika je le v tem, da metodam ni potrebno podajati parametra z rezultatom poizvedbe, saj se le-ta hrani interno v objektu.

Primer, če kar nadaljujemo prejšnjega, ko že imamo rezultat poizvedbe po tabeli users iz katerega smo želeli izbrskati uporabnika z uporabniškim imenom “vini”:

if ($row = $result->fetch_assoc()) {
    echo
        'ID uporabnika '.
        $row['username'].
        ' je: '.
        $row['id'];
}
$result->free();

Pri uporabi tega razreda izklopite magic_quotes_gpc, saj bomo v nasprotnem primeru narekovaje dvojno escapali.

Ta dva razreda sta, kot sem že rekel, zelo enostavna in ne omogočata nekaterih stvari, ki bi jih mogoče skoraj morala, recimo lovljenja napak. Ampak, zakaj bi vam prinesel vse na pladnju? Malo napnite svoje male sive celice, kot bi rekel Hercule Poirot, pa implementirajte te zadeve sami. Seveda lahko rešitev podate v komentarjih, že čakam!

Updated: