Bonjour,

Voilà je vous explique je suis en train de mettre en place le module utilisateur. Et sur la class Auth j'ai une erreur que je ne m'explique pas. (nb: j'ai remplacé l'adresse mail par des * pour des raisons de confidentialité)

( ! ) Fatal error: Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 Erreur de syntaxe près de '@******.***' à la ligne 2 in E:\web\blog\src\user\Auth.php on line 42
( ! ) PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 Erreur de syntaxe près de '@******.***' à la ligne 2 in E:\web\blog\src\user\Auth.php on line 42
Call Stack
#   Time    Memory  Function    Location
1   0.0000  410536  {main}( )   ...\index.php:0
2   0.0005  440184  require_once( 'E:\web\blog\utils\user\login.php' )  ...\index.php:27
3   0.0012  490032  App\user\Auth->authUser( )  ...\login.php:13
4   0.0015  513832  execute ( ) ...\Auth.php:42

Quand je regarde le code je ne vois pas le problème et quand je commente la partie lié à l'update du last_login, pas de problème à me connecter.

Le code de la classe

<?php

namespace App\user;

use App\Crud;
use \PDO;

class Auth extends Crud{

    protected $table = 'users';
    protected $selector = ['email', 'pass', 'last_login'];

    public function authUser(array $datas) :bool
    {
        if(session_status() === PHP_SESSION_NONE):
            session_start();
        endif;

        $table = $this->table;
        $selector = $this->selector;
        date_default_timezone_set('Europe/Paris');
        $date = date("Y-m-d H:i:s");

        $email = $datas[0];
        $pass = $datas[1];

        $req = parent::getPDO()->prepare(
            "SELECT $selector[0], $selector[1], $selector[2] FROM $table
            WHERE email = :email AND pass = :pass");
        $req->execute(array(
            'email' => $email,
            'pass' => $pass
        ));
        $finduser = $req->fetch();

        if(!empty($finduser)){
            $_SESSION['user_connected'] = true;
            $req_update = parent::getPDO()->prepare(
                "UPDATE $table SET $selector[2] = :last_login
                WHERE email = $email");
            $req_update->bindParam('last_login', $date, PDO::PARAM_STR);
            $req_update->execute();
            return true;
        }else{
            return false;
        }
    }
}

Le code du PDO

<?php

namespace App;

use \PDO;
use \PDOException;

class Crud {

    protected $pdo;

    public function __construct()
    {
        try{
            $this->pdo = new PDO('mysql:host=blog;dbname=blog;port=3307;charset=utf8', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
            ]);
        }catch (PDOException $error){
            throw new PDOException($error->getMessage(), (int)$error->getCode());
        }
    }

    protected function getPDO()
    {
        if ($this->pdo instanceof PDO) {
            return $this->pdo;
       }
    }

    public function createElement(array $datas)
    {

    }

    protected function searchElement()
    {

    }

    protected function updateElement()
    {

    }

    protected function deleteElement()
    {

    }
}

Le code de la page PHP

<?php

use App\user\Auth;

if(!isset ($_POST['email']) && !isset ($_POST['pass'])){
    require_once '../templates/user/login.html.php';        
}else{

    $user_log = htmlentities($_POST['email']);
    $user_pass = htmlentities(hash('sha256', $_POST['pass']));    
    $user= [$user_log, $user_pass];
    $loginuser = new Auth();
    $user_login= $loginuser->authUser($user);

    if($user_login === true){    
        $route= $router->generate('home');
        header('Location:' .$route);
        exit;
    }else{
        require_once '../templates/user/login.html.php';
    };
};

Le code du formulaire

<div class="container">
    <?php if(isset($_POST['pass'])): ?>
        <div class="alert">
            <span class="closebtn" onclick="this.parentElement.style.display='none';">&times;</span> 
            Identifiant ou mot de passe incorrect.
        </div>
    <?php
    endif;
    ?>
    <div class="form-group">
        <h1>Se connecter:</h1>
        <form action="" method='POST'>
            <div class="required">
                <label for="email">Identifiant</label>
                <input type="email" name="email" placeholder="Votre email" value="<?= htmlentities($_POST['email'] ?? null)?>" required/>
            </div>
            <div class="required">
                <label for="pass">Mot de passe</label>
                <input type="password" name="pass" placeholder="Votre mot de passe" required/>
            </div>
            <div class="form-submit">
                <button class="btn-submit click-animation" type="submit" value="S'inscrire" >S'inscrire</button>
            </div>
        </form>
        <a href="passwordChange.php">Mot de passe oublié?</a>
    </div>
</div>

Je tiens à rappeler que tout fonctionne si j'enlève la partie update du last_login.

Merci par avance de votre aide.

6 réponses


Curvata
Réponse acceptée

Et si tu bindParam l'email ?

Au lieu de faire parent::getPDO() tu peux faire $this->getPDO()
Tu peux te créer une propriété entity = User::class et te créer un modèle afin de récupérer ton user sous forme d'objet avec PDO::FETCH_CLASS

Je te remercie pour l'aide Curvata, mais l'update se fait sur le last_login et non l'email. Je ne comprends pas pourquoi tu veux que je fasses ta dernière phrase, ni même à quoi ça servira.
Mon problème était bien plus simple que ça, j'avais oublié les quotes sur la variable $email

Tu te fais une classe qui va représenter ton user.

<?php

class user
{    
    private $email;  
    private $pass; 
    private $last_login;

    public function getEmail()
    {
        return $this->email;
    }

    public function getPass()
    {
        return $this->pass;
    }

    public function getLastLogin()
    {
        return $this->last_login;
    }

    public function setEmail(string $email)
    {
        $this->email = $email;
    }

    public function setPass(string $pass)
    {
        $this->pass = $pass;
    }

    public function setLastLogin(string $date)
    {
        $this->last_login = new DateTime($date);
    }
}

Dans ton CRUD tu rajoutes une propriété entity

Dans ta classe auth tu redéfini la propriété entity
entity = user::class

Quand tu veux récupérer des users tu fais
->fetchAll(PDO::FETCH_CLASS, $this-entity)

C'est plus sympa d'avoir un objet qu'un tableau..
$user->getName()
$user->getPass()

Par la suite, tu créeras des classes afin de représenter un article, un produit, une catégorie,... Tu auras cas changer la propriété
entity

Je te remercie Curvata tu viens de me donner ce qui me manquait, la logique derrière un CRUD. Donc je comprends la logique de faire un objet par module, les appels à la BDD sont simplifiés. La seule chose que je ne déchiffre pas, que je ne comprends pas c'est ce que fait exactement entity. J'imagine que c'est une variable, mais le user::class je ne trouve rien de tel dans la doc php.
Je vais me baser sur ce que tu m'as donné pour faire des tests.

constante spéciale
La constante spéciale ::class permet une résolution de nom de classe pleinement qualifié au moment de la compilation, cela est utile pour les classes dans un espace de nom.

Par exemple, depuis ta classe "auth" tu veux utiliser ta méthode "searchElement" de ta classe Crud, tu fais $auth->searchElement()
et dans celle-ci tu définis ton fetch (PDO::FETCH_CLASS, $this-entity).
Comme le entity de "auth" c'est "user::class" tu auras un objet pour modèle ta classe "User", si tu as par la suite une classe "PostCrud", tu n'auras cas redéfinir "entity" entity = post::class et faire ton $postCrud->searchElement() en ayant bien entendu changé également ta propriété "table".
Tu devrais te faire une classe "userCrud" avec tes deux méthodes "update" et "select" et dans "auth" tu fais ta logique.

Un truc dans le genre

class Auth
{
    private $userCrud;

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

    public function authUser(array $datas) :bool
    {
        if(session_status() === PHP_SESSION_NONE):
            session_start();
        endif;

        $user = $this->userCrud->findUser($datas)

        if(!empty($user)){
            $_SESSION['user_connected'] = true;
            $this->userCrud->update($user->getEmail());
            return true;
        }else{
            return false;
        }
    }
}

Je te remercie, je me creuse le cerveau, mais comme je ne suis pas sur un framework certaines choses ne fonctionne peut-être pas.
La propriété Entity n'existe pas sur PHP 7.2, du coup j'essaie de décortiquer la logique derrière ce que tu m'as écris. De ce que j'ai compris:

1 - Le user (ou articles ....) j'en fais des objets avec les getter et les setter
2 - Dans le crud j'initie les entités (donc objets)
3 - J'appelle ces objets soit dans les méthodes style updateElement ou searchElement ou dans des classes style Auth
4 - Je get ou set ces objets à partir de ses méthodes ou classes

Maintenant j'ai du mal à comprendres dans mon Crud comment un $user = user::class fera appelle à la classe User. Est-ce que ça veut dire que c'est équivalent à un $user= new User() ?
Je pense que j'ai du mal à definir ce qu'est entity étant donné qu'à part une référence à Symfony, j'ai rien trouvé.

Encore merci pour ton aide.