Bonjour,
Voila je rencontre un petit problème avec mon code.
J'ai visionné et mis en pratique la partie MVC de la formation PHP orientée objet.
J'ai donc le code suivant
UsersController.php
<?php
namespace App\Controller;
use Core\Auth\DBAuth;
use Core\HTML\BootstrapForm;
use \App;
class UsersController extends AppController {
public function login(){
$errors = false;
if(!empty($_POST)){
$auth = new DBAuth(App::getInstance()->getDb());
if($auth->login($_POST['username'], $_POST['password'])){
header('Location: index.php?p=admin.posts.index');
} else {
$errors = true;
}
}
$form = new BootstrapForm($_POST);
$this->render('users.login', compact('form', 'errors'));
}
}
DBAuth.php
<?php
namespace Core\Auth;
use Core\Database\Database;
class DBAuth {
private $db;
public function __construct(Database $db){
$this->db = $db;
}
public function getUserId(){
if($this->logged()){
return $_SESSION['auth'];
}
return false;
}
/**
* @param $username
* @param $password
* @return boolean
*/
public function login($username, $password){
$user = $this->db->prepare('SELECT * FROM users WHERE username = ?', [$username], null, true);
var_dump(sha1($password));
if($user){
if($user->password === sha1($password)){
$_SESSION['auth'] = $user->id;
return true;
}
}
return false;
}
public function logged(){
return isset($_SESSION['auth']);
}
}
App.php
<?php
use Core\Config;
use Core\Database\MysqlDatabase;
class App{
public $title = "Mon super site";
private $db_instance;
private static $_instance;
public static function getInstance(){
if(is_null(self::$_instance)){
self::$_instance = new App();
}
return self::$_instance;
}
public static function load(){
session_start();
require ROOT . '/app/Autoloader.php';
App\Autoloader::register();
require ROOT . '/core/Autoloader.php';
Core\Autoloader::register();
}
public function getTable($name){
$class_name = '\\App\\Table\\' . ucfirst($name) . 'Table';
return new $class_name($this->getDb());
}
public function getDb(){
$config = Config::getInstance(ROOT . '/config/config.php');
if(is_null($this->db_instance)){
$this->db_instance = new MysqlDatabase($config->get('db_name'), $config->get('db_user'), $config->get('db_pass'), $config->get('db_host'));
}
return $this->db_instance;
}
}
On a donc une page de connexion accessible à l'url suivante: index.php?p=users.login
En tappant le bon login + mot de passe, cette page doit nous rediriger vers : index.php?p=admin.posts.index
Quand j'appuis sur le bouton se connecter, je veux donc être redigirer sur la page admin.
Malheureusement, j'obtiens un message d'erreur. Que je peux voir uniquement en faisant afficher le code source de la page, mais ça c'est une histoire de template, c'est pas très important. Voilà le message:
<b>Warning</b>: Cannot modify header information - headers already sent by (output started at /Applications/MAMP/htdocs/Model View Controller/core/Auth/DBAuth.php:28) in <b>/Applications/MAMP/htdocs/Model View Controller/app/Controller/UsersController.php</b> on line <b>16</b><br />
Donc, oui je me suis renseigné sur internet, j'ai d'ailleurs déjà eu cette erreur, y a un article qui explique bien comment la résoudre: http://darklg.me/2008/10/php-erreur-cannot-modify-header-information/
Mais n'étant pas encore très familier avec le modèle MVC, je me retrouve un peu perdu, je ne vois vraiment pas où se situe le problème dans mon code. Le plus intéressant c'est que quand j'essaye d'accéder à l'url "index.php?p=admin.posts.index" directement avec le navigateur une fois après avoir essayer de me loger, il n'y a pas de problème, j'arrive bien sur la page souhaiter.
Pour ceux qui sont premium, le code que j'ai est exactement le même que celui qu'on peut télécharger à la partie MVC dans la partie orientée objet, lien ici: https://www.grafikart.fr/formations/programmation-objet-php/mvc-model-view-controller
Merci d'avance à tout ceux qui prendront le temps de lire mon post et d'essayer de m'aider, si je ne suis pas claire sur certains points, où qu'il manque des informations ou des détails, n'hésitez pas à me le demander.
Euh, je suis d'un autre avis :)
Même si il n'a pas tord sur le fait qu'il faille couper ton script après ton header() car le script continu à s'éxecuter, le soucis vient sans le moindre doute du "var_dump" présent dans la méthode login qui renvoi du contenu au navigateur avant que tu fasses ta redirection, ce qui provoque cette erreur.
Salut,
Ton erreur est simple et se situe dans le UsersController.php
: tu fais un header('Location: ...')
, sans couper l'execution du reste de la méthode, ce qui fait que tu es redirigé, puis que tu essayes de rendre la page users.login
. Il te faut impérativement couper l'execution de ton script avec un exit;
ou un die();
après l'appel de la fonction header()
.
Est-ce que c'est assez clair ? Si tu as des questions ou des choses que tu ne comprends pas, tu peux demander, nous sommes là pour ça :)
Une petite remarque concernant la fonction header
, une bonne pratique serait de te faire une méthode protégée redirect()
dans le AppController
dans lequel tu aurais le header
puis le exit
:
AppController.php
protected function redirect (string $url, bool $permanently = true): void
{
if ($permanently) {
header("HTTP/1.0 301 Moved Permanently");
}
header("Location: " . $url);
exit;
}
/* la valeur par défaut de $path est à adapter suivant tes besoins et ton code */
protected function redirectPath (string $path = 'home', bool $permanently = true): void
{
return $this->redirect('index.php?p=' . $path, $permanently);
}
UserController.php
class UsersController extends AppController
{
public function login()
{
$errors = false;
if (!empty($_POST)) {
$auth = new DBAuth(App::getInstance()->getDb());
if ($auth->login($_POST['username'], $_POST['password'])) {
return $this->redirectPath('admin.posts.index');
} else {
$errors = true;
}
}
$form = new BootstrapForm($_POST);
$this->render('users.login', compact('form', 'errors'));
}
}
Tu peux également rajouter un HTTP status dynamique en t'inspirant de cet exemple : http://php.net/manual/fr/function.http-response-code.php#107261
je précise que je n'ai pas testé le code ci-dessus, à toi de l'adapter si besoin est ;)
En espérant avoir pu t'aider :)
@Kenor le var_dump()
ne fait qu'afficher les informations sur la page (celles-la même qu'il voit lorsqu'il inspecte le code source). Je pense plutôt que c'est lié au header()
. Cela étant dit, ça ne mange pas de pain de virer le var_dump()
afin de s'assurer qu'il ne pose pas plus de soucis qu'autre chose :)
moi je vote pour le var_dump.
une réponse HTTP envoi d'abord les headers et ensuite le body. (dans la meme requette hein mais une fois que les headers sont pret a etre envoyé, il ne faut plus y toucher si le buffer de la réponse du body commence a se remplir.)
headers already sent
ca veux dire qu'une partie du contenu a déjà été envoyé au client. et que dans ce cas, le serveur ne peux pas envoyer a nouveau un header. d'où le message d'erreur...
mais il est aussi vrai qu'un header doit avoir un exit / die juste après...
le mieux, ce serai de tester et de nous dire.
en virant ton var_dump, ca fonctionne ?
en remettant ton var_dump et en mettant un exit; après ton header, ca marche ? et en virant le var_dump et en mettant un exit ?
@keulu A voir effectivement, il faut tester. Après je lui ai également donné une piste d'amélioration :)
@betaWeb Aucun soucis, tes optis sont les bienvenues :) c'est dailleurs comme ca que font les frameworks. une methode redirect qui fait un header('location:...'); exit; (comme ca, t'es sur de pas l'oublier...)
Merci à vous tous, effectivement le problème venait du var_dump.
Et oui, il faut couper l'éxécution du script après la redirection.
Vous êtes super !
J'ai pris en compte l'amélioration proposé avec la méthode protégée redirect() par @betaWeb
Merci d'avoir pris le temps de répondre !