質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.37%

header関数がうまく動かない。。

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 163

score 29

1日中調べましたがどうにもならないのでお知恵をおかしください。

現在簡単なログインシステムを作っています。
header関数を使用して様々な場合にログインページへ遷移させています。
しかし1箇所だけ下記エラーがでます。
Notice: Trying to access array offset on value of type bool in ~ on line ~
Warning: Cannot modify header information - headers already sent by ~

header関数で遷移させているパターンは
直接このコードがかかれたページにアクセスした時、メアドとパスワードが間違っていた時等ですが、特定のheaderだけエラーが出ます。
エラーが出るのはアドレスを間違えた時です。アドレスの型を間違えた場合はキチンとheader関数は動きます。
アドレスが合っていてパスワードが間違っていた場合はエラーはでません。
メアド、パスワードに間違いがなければログインんができheader関数もキチンと動きます。
下記部分でエラーが出ています。

if($check === false || $check == '' || $check === NULL){
$_SESSION['E-message'] = "mistaken";
header("Location: login");
exit();

DBからpassを拾えてないのが問題なのでしょうか。。。XAMPPではキチンと動いています。
サーバーはロリポップでPHPのバージョンは7.4のLiteSpeedなるものです。
BOMもなしで保存しています。
exit();をdie('location header sent'); に変えたりheader("Location: フルパス");にしてもダメでした。。

下記ソースです。

<?php
session_set_cookie_params(0);
session_name('didyesclean');
session_start();

if($_SESSION['token'] == ""){
    header("Location: login");
    exit();
}

$token = filter_input(INPUT_POST, 'token');

if ($token != '' || $token != NULL) {
    if($token != $_SESSION['token']){
        $_SESSION['EM'] = "ut";
        header("Location: login");
        exit();
    }
}
function db_connect(){
    $dsn = 'mysql:dbname=sample;host=sample;charset=utf8';
    $user = 'sample';
    $password = 'sample';
    try{
        $dbh = new PDO($dsn, $user, $password);
        return $dbh;
    }catch (PDOException $e){
            print('Error:'.$e->getMessage());
            die();
    }
}
$dbh = db_connect();

$mail = filter_input(INPUT_POST, 'mail');
$pass = filter_input(INPUT_POST, 'password');

    if (!empty($mail)){
        if(!preg_match("/^([a-zA-Z0-9])+([a-zA-Z0-9\._-])*@([a-zA-Z0-9_-])+([a-zA-Z0-9\._-]+)+$/", $mail)){
            $_SESSION['EM'] = "im";
            header("Location: login");
            exit();
        }
    }

    $statement = $dbh->prepare("SELECT pass FROM users WHERE mail = :mail");
    $statement->bindValue(':mail', $mail, PDO::PARAM_STR);
    $statement -> execute();
    $result = $statement -> fetch(PDO::FETCH_ASSOC);
    $check = password_verify($pass, $result['pass']);

    if($check === false || $check == '' || $check === NULL){
        $_SESSION['EM'] = "mt";
        header("Location: login");
        exit();
    } elseif($check === true) {

    }
    header("Location: mypage");
    exit();
}else{
    header("Location: login");
    exit();
}
?>

どなたかご教授していただけますと幸いです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

$result = $statement -> fetch(PDO::FETCH_ASSOC);
$check = password_verify($pass, $result['pass']);

この部分ですが、リファレンスによるとPDOStatement::fetch()は失敗するとfalseを返します。
https://www.php.net/manual/ja/pdostatement.fetch.php
そして、失敗してbool型を受け取った$resultに対して、配列アクセスを試みています。
故に下記のNoticeが出ていると思われます。

Notice: Trying to access array offset on value of type bool in ~ on line ~

ここで、header関数は呼び出される前になんらかの出力が行われると機能しない仕様であることから、
エラー出力が先行してしまったので、ヘッダー出力ができずにリダイレクトに失敗しているということを下記のエラーメッセージが指しています。

Warning: Cannot modify header information - headers already sent by ~

PDOStatement::fetch()がなぜ失敗したか調査する必要もありますが、
ケースバイケースで以下のいずれかまたは複数の対応を実施すると良いかと思います。

  • 本来は失敗してはならないのであればそのように修正する
  • 失敗することも想定内の場合(false)を判定する処理を追加してエラーを吐かせないようにする
  • そもそもエラー出力させない(別途ログ出力させる)
  • PHPの設定でNoticeを吐かないようにする

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/09/20 05:30 編集

    とてもわかりやすいご説明ありがとうございます!!

    現在PHPのエラーを可視化できるよう.htaccessにphp_flag display_errors onの設定をしています。
    それのせいでNotice: Trying to access array offset on value of type bool in ~ on line ~
    が表示されheader関数は呼び出される前になんらかの出力が行われると機能しないに抵触してしまっていたんですね。。。

    >失敗することも想定内の場合(false)を判定する処理を追加してエラーを吐かせないようにする

    これは事前に入力されたアドレスがDBに存在するかCOUNTし「0」ならpassをSELECTするロジックへ進む前にheader関数でloginに飛ばせるので動き的には意図した動きになったのですが、このような処置でよろしいのでしょうか?

    キャンセル

  • 2021/09/20 10:36 編集

    > これは事前に入力されたアドレスがDBに存在するかCOUNTし「0」ならpassをSELECTするロジックへ進む前にheader関数でloginに飛ばせるので動き的には意図した動きになったのですが、このような処置でよろしいのでしょうか?

    今回のfetch()がfalseになることに関しては結果が0件だったことが原因だと思うので、そのやり方も一つの解だとは思います。
    ですが、逆にその他の要因は考える必要が無いのであれば、
    最初の通りいきなりSELECTして、そのfetch()結果がfalseならば0件と解釈して、ログインページへ戻すようにすればいいと思います。
    一般的にDBアクセスする回数は少ない方が良いので、なるべく減らす方向で最適化すると良いと思います。

    こういうユーザー入力の検証処理はどこまで細かく分別するかの粒度がシステムによって様々で無数に考えられるので、あえて言及しませんでしたが、
    提示されているコードのように同じ結果に行きつくところがパターンが複数ある場合はthrowとtry ~ catchを有効に使うと同じコードの繰り返しが減ってシンプルになります。

    キャンセル

  • 2021/09/20 13:03

    度々のご回答ありがとうございます!
    throwとtryについて調べてみましたが便利そうですね!
    今回のソース内ではどのように組み込めばいいのか100%の理解はできていませんが頑張って勉強します!

    キャンセル

+1

Notice: Trying to access array offset on value of type bool in ~ on line ~

というメッセージが、header関数実行前に出力されているからでしょう。
出力されないようにしましょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/09/20 05:30

    ご回答ありがとうございます!

    上の方の回答で理解できました!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.37%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る