Beste Phpers,

Ik ben pas begonnen met OOP en probeer nu een login + session systeem te maken.
De bedoeling is dat een gebruiker in kan loggen er dan een cookie gemaakt word met als inhoud een hash.
Deze hash word ook opgeslagen in de database en ook word het user object opgeslagen in de database.
Verder wil in de cookie ook weer opslaan in een session met session_set_save_handler()

Nu was mijn vraag aan jullie ben ik goed op weg? Wat zijn mijn verbeter punten tot nu toe? Waar moet ik rekening mee houden? Wat moet ik anders doen?

LoginAction.class.php

<?php
/**
 * LoginAction klasse
 *
 * @author Tom Swinkels
 * @version v1.0 last edit on 26-02-2012
 */
class LoginAction
{
    private $_userMapper;
    private $_sessionMapper;

    public function __construct(UserMapper $userMapper, $sessionMapper)
    {
        $this->_userMapper = $userMapper;
        $this->_sessionMapper = $sessionMapper;
    }
    public function execute(Request $request)
    {
        $errors = array();
        if(isset($request->postData['submit']))
        {
            /** Form are submit **/
            $user = $this->_userMapper->login($request->postData['user_name'], $request->postData['user_password']);
            if($user != null)
            {
                /** Make new session **/
                $session = new Session('user', $user);
                $this->_sessionMapper->save($session);
                $this->_sessionMapper->saveCookie($session);

                /** Redirect user to an page **/
                //header("location:" . SITE_PATH);
                return $user;
            }
            else
            {
                $errors[] = 'Login gegevens kloppen niet';

                return $errors[0];
            }
        }
    }
}
?>


Session.class.php


<?php
/**
 * Session klasse
 *
 * @author Tom Swinkels
 * @version v1.0 last edit on 26-02-2012
 */
class Session
{
    private $_name;
    private $_data;
    private $_timestamp;
    private $_hash;

    public function __construct($name, $data)
    {
        $this->generateHash();

        $this->_name = $name;
        $this->_data = $data;
        $this->_timestamp = time();
    }
    public function generateHash()
    {
        $this->_hash = hash("sha512", time() . uniqid() . session_id());
    }
    public function getData()
    {
        return array('name'         => $this->_name,
                     'hash'         => $this->_hash,
                     'data'         => $this->_data,
                     'timestamp'    => $this->_timestamp);
    }
}
?>


SessionMapper.class.php



<?php
/**
 * SessionMapper klasse
 *
 * @author Tom Swinkels
 * @version v1.0 last edit on 26-02-2012
 */
class SessionMapper
{
    private $_db;
    private $_cookieData;

    const COOKIE_NAME = 'SESSION_ID';

    public function __construct(PDO $db, array $cookieData)
    {
        $this->_db = $db;
        $this->_cookieData = $cookieData;
    }
    public function getSession($name)
    {
        $resultWebs = $this->_db->prepare("SELECT
                                                 webs_id,
                                                 webs_name,
                                                 webs_data
                                           FROM
                                                 web_session
                                           WHERE
                                                 webs_hash = ?
                                           AND
                                                 webs_name = ?
                                          ");

        $resultWebs->execute(array($this->getSessionId(),
                                   $name));
        $listWebs = $resultWebs->fetch(PDO::FETCH_ASSOC);

        $session = new Session($name, unserialize($listWebs['webs_data']));
        return $session;
    }
    public function getSessionId()
    {
        return $this->_cookieData[self::COOKIE_NAME];
    }
    public function save(Session $session)
    {
        /** Get the session data **/
        $dataArray = $session->getData();

        $resultUsrs = $this->_db->prepare("INSERT INTO
                                                      web_session
                                           SET
                                                      webs_date_add = NOW(),
                                                      webs_name = ?,
                                                      webs_hash = ?,
                                                      webs_data = ?,
                                                      webs_timestamp = ?
                                          ");
        $resultUsrs->execute(array($dataArray['name'],
                                   $dataArray['hash'],
                                   serialize($dataArray['data']),
                                   $dataArray['timestamp']));
    }
    public function saveCookie(Session $session)
    {
        /** Get the session data **/
        $dataArray = $session->getData();

        setcookie(self::COOKIE_NAME, $dataArray['hash'], strtotime("+20 years"));
    }
}
?>
Bedankt voor de reacties hier heb ik zeker iets aan!
Een adres klasse gaat er ook zeker komen.

Verder maak ik liever gebruik van losse get's en set's i.v.m de magic methode.

Verder een login hoort bij de userMapper omdat hij de gegevens dan de user ophaalt, eigenlijk zou de naam van die methode moeten zijn getUserByNameAndPassword().
Omdat Tom graag wilt dat ik mijn mening vertel over de reactie van Mark:

Overigens denk ik dat het voor een deel ook gewoon is wat je zelf prettig vind werken. Iedereen zou de abstracties iets anders en verder / minder ver doorvoeren.

Dit ben ik met je eens. De Storage Interface is misschien een stapje te ver voor alleen een inlog systeem. Maar als ik een hele site ga maken in OOP is het handig dat ik al van dit soort verschillende storage objecten heb en er zo eentje bij kan bouwen.

Het belangrijkste bij het programmeren is duidelijke naamgeving van methoden, variabelen en klassen.

Dit is inderdaad het belangrijkste. Maar gebruik jij dan type prefixen in namen? Zodat je kan zien wat er gereturned word en welk type een parameter moet zijn? Voor dat soort dingen is de PHPdoc heel handig. Je hebt snel overzicht wat er in moet en wat je terug krijgt.
Bij getter en setters zou ik geen PHPdoc gebruiken, dat weet je zo wel. Maar bij de wat uitgebreidere methods is het altijd handig om een kort overzicht te zien.
Verder vind ik de " Er wordt echter zelden meer gekeken naar de PHPDoc die erboven staat, wat deze outdated en daardoor juist heel erg verwarrend maakt." mening een beetje vreemd. Hoe weet je dat er zelden naar de PHPdoc wordt gekeken? Als ik iets verander verander ik namelijk ook meteen de PHPdoc erboven. Ik denk dat het vooral aan de persoon ligt of dit wel of niet wordt gedaan, maar als je PHPdoc gebruikt moet je het ook goed gebruiken...

Interessant, een discussie :)


Dit ben ik met je eens. De Storage Interface is misschien een stapje te ver voor alleen een inlog systeem. Maar als ik een hele site ga maken in OOP is het handig dat ik al van dit soort verschillende storage objecten heb en er zo eentje bij kan bouwen.

Hangt er geheel vanaf hoe je alles opgebouwd hebt. Ik gebruik zelf het domain model pattern. Waarbij ik repositories gebruik waaruit je de domein modellen kan opvragen en in kan toevoegen. De repository bepaalt zelf vervolgens doormiddel van een ORM hoe en waar de object opgeslagen worden en waar ze vandaan komen. Zo heb ik ook nooit meer te maken met de relationele structuur van de database. Daarnaast is mijn domein model op die manier volledig losgekoppeld van de infrastructuur. Het domein model weet niet eens dat het opgeslagen kan worden.


zien.
Verder vind ik de " Er wordt echter zelden meer gekeken naar de PHPDoc die erboven staat, wat deze outdated en daardoor juist heel erg verwarrend maakt." mening een beetje vreemd.

Dat is geen mening, maar gewoon wat ik in de praktijk tegen kom als ik code van andere moet aanpassen of moet samenwerken met andere. Het schrijven van PHPDoc kost tijd en vaak is die tijd er niet. Zeker niet als er een set met bugs gefixt moeten worden. De fixes en features worden snel toegevoegd en de PHPDoc blijft staan. Helaas is niet iedereen zo netjes daarmee dus.

Daarnaast heb ik nog nooit een methode gezien waarbij de naam goed was en PHPDoc nog enige toegevoegde waarde kon hebben. Natuurlijk zijn er wel methoden waar PHPDoc kan helpen, maar meestal zijn er dan één van de volgende dingen mis:

- De methode heeft naam die niet goed beschrijft wat de methode doet
- De methode doet meer dan één ding en zou opgesplitst moeten worden

Ik heb de afgelopen vijf jaar geen PHPDoc of Javadoc of Doxygen geschreven en het ook geen moment gemist. En de mensen die bij mij in het team zaten heb ook nog nooit horen klagen dat ze misten. Ook niet voor het opgeven van typen. Ik gebruik namelijk voor klassen altijd typehinting en bij primitieve typen geven de parameter namen en methode namen al duidelijk aan wat er voor type verwacht wordt. Voor Java en C++ is dit verder helemaal zinloos, want bij die talen geef je gewoon in je methode interface al de typen aan.

Zie ook mijn artikel Commentaar wanneer is het nuttig? over deze kwestie. Veel mensen noemen commentaar (en dus ook PHPDoc) in hun code ten onrechte documentatie. Maar commentaar en documentatie zijn twee compleet verschillende dingen. Documentatie ben ik wel een heel groot voorstander van.
Ik heb weer aan update gedaan, de cookie word nu opgeslagen en werkt.

Moet ik nu om te bepalen wie er ingelogd is de functie getSession oproepen? Hoe kan ik deze functie beter beveiligen en waar zouden jullie op beveilingen enkel IP?

<?php
/**
 * SessionMapper klasse
 *
 * @author Tom Swinkels
 * @version v1.0 last edit on 27-02-2012
 */
class SessionMapper
{
    private $_db;

    const COOKIE_NAME = 'SESSION_ID';

    public function __construct(PDO $db)
    {
        $this->_db = $db;
    }
    public function getSession($name, $request)
    {
        $resultWebs = $this->_db->prepare("SELECT
                                                 webs_id,
                                                 webs_name,
                                                 webs_data
                                           FROM
                                                 web_session
                                           WHERE
                                                 webs_hash = ?
                                           AND
                                                 webs_name = ?
                                          ");

        $resultWebs->execute(array($this->getSessionId($request),
                                   $name));
        $listWebs = $resultWebs->fetch(PDO::FETCH_ASSOC);

        $session = new Session($name, unserialize($listWebs['webs_data']));
        return $session;
    }
    public function getSessionId(Request $request)
    {
        return $request->getCookie[self::COOKIE_NAME];
    }
    public function save(Session $session)
    {
        $resultUsrs = $this->_db->prepare("INSERT INTO
                                                      web_session
                                           SET
                                                      webs_date_add = NOW(),
                                                      webs_name = ?,
                                                      webs_hash = ?,
                                                      webs_data = ?,
                                                      webs_timestamp = ?
                                          ");
        $resultUsrs->execute(array($session->getName(),
                                   $session->getHash(),
                                   serialize($session->getData()),
                                   $session->getTimestamp()));

        $this->saveCookie($session->getHash());
    }
    private function saveCookie($hash)
    {
        setcookie(self::COOKIE_NAME, $hash, strtotime("+20 years"));
    }
}
?>


Moet het ophalen van iemand die is ingelogd op de volgende manier?

$logindata = $sessionMapper->getSession('user', $request);
//Check login?
if($logindata->data)
{
    echo 'Ingelogd als ' . $logindata->data->name;
}


Verder krijg ik een error als er een gebruiker naar de pagina gaat waar nog geen session voor gemaakt is, hoe kan ik dat oplossen?

public function getSessionId(Request $request)
{
    return $request->getCookie[self::COOKIE_NAME];
}

Reageren