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

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

ただいまの
回答率

87.37%

パーフェクトPHP ミニブログ作成でのエラーについて

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 4,920
退会済みユーザー

退会済みユーザー

前提・実現したいこと

パーフェクトPHPのミニブログの写経をしています。
下記のエラーが発生しており、自分でもなんども該当の部分を見比べてみたのですが、解決できません。
よろしくお願いします。

発生している問題・エラーメッセージ

Fatal error: Uncaught Error: Call to a member function prepare() on bool in C:\xampp\htdocs\php-blog.localhost\core\DbRepository.php:19 Stack trace: #0 C:\xampp\htdocs\php-blog.localhost\core\DbRepository.php(32): DbRepository->execute('select a.*, u.u...', Array) #1 C:\xampp\htdocs\php-blog.localhost\models\StatusRepository.php(22): DbRepository->fetchAll('select a.*, u.u...', Array) #2 C:\xampp\htdocs\php-blog.localhost\controllers\StatusController.php(8): StatusRepository->fetchAllPersonalArchivesByUserId(NULL) #3 C:\xampp\htdocs\php-blog.localhost\core\Controller.php(41): StatusController->indexAction(Array) #4 C:\xampp\htdocs\php-blog.localhost\core\Application.php(123): Controller->run('index', Array) #5 C:\xampp\htdocs\php-blog.localhost\core\Application.php(103): Application->runAction('status', 'index', Array) #6 C:\xampp\htdocs\php-blog.localhost\web\index_dev.php(7): Application->run() #7 {main} thrown in C:\xampp\htdocs\php-blog.localhost\core\DbRepository.php on line 19

該当のソースコード

DbRepository.php

<?php

abstract class DbRepository
{
    protected $con;

    public function __construct($con)
    {
        $this->setConnection($con);
    }

    public function setConnection($con)
    {
        $this->con = $con;
    }

    public function execute($sql, $params = array())
    {
        $stmt = $this->con->prepare($sql); //19行目です
        $stmt->execute($params);

        return $stmt;
    }

    public function fetch($sql, $params = array())
    {
        return $this->execute($sql, $params)->fetch(PDO::FETCH_ASSOC);
    }

    public function fetchAll($sql, $params = array())
    {
        return $this->execute($sql, $params)->fetchAll(PDO::FETCH_ASSOC);
    }
}

UserRepository.php

<?php

class UserRepository extends DbRepository
{
    public function insert($user_name, $password)
    {
        $password = $this->hashPassword($password);
        $now = new DateTime();

        $sql = "insert into user(user_name, password, created_at) values(:user_name, :password, :created_at)";
        $stmt = $this->execute($sql, array(
            ':user_name'    => $user_name,
            ':password'     => $password,
            ':created_at'   => $now->format('Y-m-d H:i:s'),
        ));
    }

    public function hashPassword($password)
    {
        return sha1($password . 'SecretKey');
    }

    public function fetchByUserName($user_name)
    {
        $sql = "select * from user where user_name = :user_name";

        return $this->fetch($sql, array(':user_name' => $user_name));
    }

    public function isUniqueUserName($user_name)
    {
        $sql = "select count(id) as count from user where user_name = :user_name";

        $row = $this->fetch($sql, array(':user_name' => $user_name));
        if($row['count'] === '0'){
            return true;
        }

        return false;
    }
}

StatusRepository.php

<?php

class StatusRepository extends DbRepository
{
    public function insert($user_id, $body)
    {
        $now = new DateTime();

        $sql = "insert into status(user_id, body, created_at) values(:user_id, :body, :created_at)";

        $stmt = $this->execute($sql, array(
            ':user_id'      => $user_id,
            ':body'         => $body,
            ':created_at'   => $now->format('Y-m-d H:i:s'),
        ));
    }

    public function fetchAllPersonalArchivesByUserId($user_id)
    {
        $sql = "select a.*, u.user_name from status a left join user u on a.user_id = u.id where u.id = :user_id order by a.created_at desc";

        return $this->fetchAll($sql, array(':user_id' => $user_id));
    }
}

試したこと

本に書かれている通りのコードを写経しています。

補足情報(FW/ツールのバージョンなど)

php 7.3.8
Xampp v.3.2.4

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • m.ts10806

    2019/08/10 19:20 編集

    DbRepositoryを継承しているクラスとクラスを呼び出しているコードをご提示ください。
    また、PHP7.3ではパーフェクトPHPが前提としているバージョンと大きく違います(書籍の発行は版によりますが5年以上前では)
    せめてPHP7対応の書籍を利用されたほうが良いですし、PHPマニュアルは必ず活用してください。

    キャンセル

  • 退会済みユーザー

    退会済みユーザー

    2019/08/10 20:40

    UserRepositoryとStatusRepositoryのコードを追加させていただきました。
    確かにこの本は古い本といえば、古い本なので今のバージョンとの食い違いなどがあるかもしれません。
    今回は知り合いにPHPの勉強におすすめということでお勧めされ、やってみた次第です。

    キャンセル

回答 4

checkベストアンサー

+4

『回答』『参考サイトの紹介』『NetBeans デバッグ方法』

回答です。

PhpBlogApplication.php (MiniBlogApplication.php)
誤 protected function cofigure()
正 protected function configure()

既に、指摘されていますが、DB への接続ミスです。

NetBeans IDE デバッグ機能 を使って解決しました。


参考サイトの紹介です。

パーフェクトPHP デバッグ (処理の流れ | ユーザ登録機能作成)
パーフェクトPHP デバッグ (チェックポイント)
パーフェクトPHP デバッグ (その他) (具体的な 環境設定手順)
パーフェクトPHP デバッグ (NetBeans デバッグ方法) (具体的な デバッグ例)
 
パーフェクトPHP をデバッグしました (学習方法)
パーフェクトPHP をデバッグしました (やる気のある初心者)

パーフェクトPHP をデバッグしました (学習方法) や
パーフェクトPHP をデバッグしました (やる気のある初心者) に
書いてありますが、「写経」はおすすめしません。

 
【パーフェクトPHP デバッグ 動作環境】

  • XAMPP 5.6.31
  • NetBeans 8.2

学習環境なので XAMPP 7.x.x (PHP 7.x.x) にする必要はありません。
XAMPP 7.x.x でも動作しますが、Xdebug の準備が必要です。
XAMPP 7.1.14 と NetBeans IDE 8.2 の不具合
 
サンプルコードは、フレームワークの 使い方 ではなく、
フレームワークとオブジェクト指向の 考え方 について書かれているので
バージョンは、関係ありません。


NetBeans デバッグ方法

【エラーの原因】

NetBeans IDE デバッグ機能 を使った デバッグ です。

注意点

  • 公式サイト サンプルコード(mini-blog.zip) を使用 (不具合 2ヶ所修正)
  • デバッグ中の変数の変更は、メモリ上です (ファイルは元のまま)
  • index.php ではなく index_dev.php を使用します

サンプルコードの仕様 と orangefarmerさんの仕様 との違い (ホームページ表示とログイン)
 
サンプルコードの仕様

  • ホームページは、ログイン後に表示されます

  • ホームページ (エラー ログ #5 Application->runAction('status', 'index', Array))

orangefarmerさんの仕様

  • 未ログインで、ホームページ表示が実行されます (今回は DB への接続ミス で 処理は中断)

  • エラー ログ #2 StatusRepository->fetchAllPersonalArchivesByUserId(NULL) 本来は、NULLの部分に ログインユーザ ($user['id']) の 値 がセットされます

  • ログインする 仕様の場合、ホームページ表示前に userテーブル使用で DB への接続ミス が発生します

デバッグ対策 下記 NetBeans ブレークポイント Session 49 参照
orangefarmerさんの仕様 に合わせるため、強引に 未ログイン状態 可 にしています

参考

エラー ログ

  • #5 Application->runAction('status', 'index', Array))
  • #2 StatusRepository->fetchAllPersonalArchivesByUserId(NULL)
#6 C:\xampp\htdocs\php-blog.localhost\web\index_dev.php(7):                Application->run()
#5 C:\xampp\htdocs\php-blog.localhost\core\Application.php(103):           Application->runAction('status', 'index', Array)
#4 C:\xampp\htdocs\php-blog.localhost\core\Application.php(123):           Controller->run('index', Array)
#3 C:\xampp\htdocs\php-blog.localhost\core\Controller.php(41):             StatusController->indexAction(Array)
#2 C:\xampp\htdocs\php-blog.localhost\controllers\StatusController.php(8): StatusRepository->fetchAllPersonalArchivesByUserId(NULL)
#1 C:\xampp\htdocs\php-blog.localhost\models\StatusRepository.php(22):     DbRepository->fetchAll('select a.*, u.u...', Array)
#0 C:\xampp\htdocs\php-blog.localhost\core\DbRepository.php(32):           DbRepository->execute('select a.*, u.u...', Array)

Fatal error: Call to a member function prepare() on bool

 
NetBeans ブレークポイント (3ヶ所設定 *) と 処理順序

PHP 内容 次のアクション
Application 184* $controller("status"), $action("index") 続行 (F5)
Session 103* isAuthenticated ログインチェック ステップ・イン (F7) 2回
Session 49   $default true に変更 (上記 仕様) 続行 (F5)
DbRepository 41* $this->con false に変更 続行 (F5)

注 Session 49 は、ブレークポイントではありません
NetBeans デバッグ方法 参照

原因

デバッグを実行すると、最後に エラーが「再現」 されます
 
「正しい」データベースへの接続設定 (PDO インスタンス) の 
$this->con を「壊しています」
 
(DbRepository 41* $this->con false に変更)
 
エラーの原因は、DB への接続ミスです

Application.php

   ...
173    public function run()
174    {
175        try {
176            $params = $this->router->resolve($this->request->getPathInfo());
177            if ($params === false) {
178                throw new HttpNotFoundException('No route found for ' . $this->request->getPathInfo());
179            }
180
181            $controller = $params['controller'];
182            $action = $params['action'];
183
184*           $this->runAction($controller, $action, $params);    /* ブレークポイント */
185        } catch (HttpNotFoundException $e) {
186            $this->render404Page($e);
187        } catch (UnauthorizedActionException $e) {
188            list($controller, $action) = $this->login_action;
189            $this->runAction($controller, $action);
190        }
191
   ...

 
Session.php

   ...
43    public function get($name, $default = null)
44    {
45        if (isset($_SESSION[$name])) {
46            return $_SESSION[$name];
47        }
48
49        return $default;    /* $default true に変更 */
50    }
   ...
101    public function isAuthenticated()
102    {
103*       return $this->get('_authenticated', false);    /* ブレークポイント */
104    }
   ...

 
DbRepository.php

   ...
39    public function execute($sql, $params = array())
40    {
41*       $stmt = $this->con->prepare($sql);    /* ブレークポイント $this->con false に変更 */
42        $stmt->execute($params);
43
44        return $stmt;
45    }
   ...

【コードを調べる】

DB への接続 のスタートである」"new PDO" を検索 検索ツール(Devas)

ファイル メソッド
DbManager.php  public function connect($name, $params)

 
"connect(" を検索 検索ツール(Devas)

ファイル メソッド
MiniBlogApplication.php protected function configure()
DbManager.php public function connect($name, $params)    同上

 
"configure(" を検索 検索ツール(Devas)

ファイル メソッド
MiniBlogApplication.php protected function configure()    同上
Application.php public function __construct($debug = false)
Application.php protected function configure()

 
DbManager.php

   ...
20    public function connect($name, $params)
21    {
22        $params = array_merge(array(
23            'dsn'      => null,
24            'user'     => '',
25            'password' => '',
26            'options'  => array(),
27        ), $params);
28
29        $con = new PDO(    /* "new PDO" を検索 */
30            $params['dsn'],
31            $params['user'],
32            $params['password'],
33            $params['options']
34        );
35
36        $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
37
38        $this->connections[$name] = $con;
39    }
   ...

 
MiniBlogApplication.php

   ...
37    protected function configure()
38    {
39        $this->db_manager->connect('master', array(
40            'dsn'      => 'mysql:dbname=mini_blog;host=localhost;charset=utf8',
41            'user'     => 'root',
42            'password' => '',
43        ));
44    }
   ...

 
Application.php

   ...
21    public function __construct($debug = false)
22    {
23        $this->setDebugMode($debug);
24        $this->initialize();
25        $this->configure();
26    }
   ...
60    protected function configure()
61    {
62    }
   ...

 
上記 3ファイル の メソッド を調べる
3ファイルのうち、orangefarmer さんから コード提示されているのは、
MiniBlogApplication.php のみ

MiniBlogApplication.php にエラーが無い場合
DbManager.php と Application.php のコード提示をしてもらう予定
...
(MiniBlogApplication.php にエラーがあったので、コードの提示は必要無くなった)


【修正箇所が分かる & NetBeans IDE デバッグ機能】

差分ツール (WinMerge) で
MiniBlogApplication.php (サンプルコード) と
MiniBlogApplication.php (orangefarmer さん) を
比較する

MiniBlogApplication.phpの比較
画像 MiniBlogApplication.phpの比較 (クリックすると、拡大表示します)

正 protected function configure()
誤 protected function cofigure()

 
今回のエラーを解決しても、別の不具合が出ます

  • ホームページのデータ件数 ゼロ
  • エラー ログ #2 StatusRepository->fetchAllPersonalArchivesByUserId(NULL)
  • ログイン機能のデバッグ (or 実装)
  • 写経 未完成 (ルーティング registerRoutes 参照) 画像 MiniBlogApplication.phpの比較 ↑

すべての写経のエラーを解決しても、フレームワークの内部とオブジェクト指向は理解できません

「NetBeans IDE デバッグ機能」 使って「フレームワークの内部」と「オブジェクト指向」を理解しましょう (ダウンロードした サンプルコード 使用)

NetBeans IDE デバッグ機能 には、2通りの使い方があります

  • バグを見つける (本来のデバッグ)

  • コードを追う (コードの理解) 

コードの理解のための NetBeans IDE デバッグ機能 は、バグを見つけるためではなく
サンプルコード の「処理の流れ」と「変数の値」を調べるために 使います
 
どんなツールも初めは大変ですが、すぐに慣れます
特に、初心者の方 には効果が大きいです 
デバッグ例

詳細は、参考サイトのリンク をご覧ください


【エラーの理由】

子クラスのメソッド名 (cofigure) が親クラスのメソッド名 (configure) と違うために
オーバライドができず、データベースへの接続設定 が未定義 のためです。

class MiniBlogApplication extends Application ← extends (継承)
{
    ... 省略
}

MiniBlogApplication の cofigure メソッド
    protected function cofigure() ← オーバライドできない (configure でないため)
    {  ↓ 以下 データベースへの接続設定
        $this->db_manager->connect('master', array(
            'dsn'      => 'mysql:dbname=mini_blog;host=localhost;charset=utf8',
            'user'     => 'root',
            'password' => '',
        ));
    }

Application の  の configure メソッド
エラーが無ければ MiniBlogApplication の データベースへの接続設定 が Application に反映されます
    protected function configure() ← configure
    {
    }

Application のコンストラクタ
    public function __construct($debug = false)
    {
        $this->setDebugMode($debug);
        $this->initialize();
        $this->configure(); ←  のメソッドのまま configure (データベースへの接続設定 が 未定義)
    }

上記エラーで、フレームワークとオブジェクト指向の「考え方」が良く分かります

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/01/13 09:33

    デバッグ時の操作は、少しづつ慣れていけば良いと思います。

    「何か面倒だな」と思った時は、楽な方法を探してください。
    効率が変わります。

    キャンセル

  • 2020/02/02 13:44 編集

    --- url変更 ---

    パーフェクトPHP デバッグ (処理の流れ (1/5) | ユーザ登録機能作成) 全5パターン
    https://perfectphp-debug.netlify.com/debug-02-1.html

    パーフェクトPHP デバッグ (チェックポイント)
    https://perfectphp-debug.netlify.com/debug-01.html

    パーフェクトPHP デバッグ (その他) 環境設定等
    https://perfectphp-debug.netlify.com/debug-03.html

    パーフェクトPHP デバッグ (NetBeans デバッグ方法)
    https://perfectphp-debug.netlify.com/debug-04.html

    キャンセル

  • 2020/03/17 14:57 編集

    --- url追加 ---
    【動画 (YouTube)】

    パーフェクトPHP をデバッグしました (デバッグ例 基本編) オートロード
    https://youtu.be/F-QJCXH2Xm0

    パーフェクトPHP をデバッグしました (デバッグ例 応用編) CSRF対策
    https://youtu.be/htohdTY26D0

    パーフェクトPHP をデバッグしました (デバッグ例 応用編) アウトプットバッファリング
    https://youtu.be/U58DL8_iAvI

    パーフェクトPHP をデバッグしました (デバッグ例 応用編) DbManagerクラス
    https://youtu.be/najYeNBB1xM

    キャンセル

+2

写経したとのことですが、正誤表で該当箇所に間違いはないですか?
http://gihyo.jp/book/2010/978-4-7741-4437-5/support#supportApology

あとソースコードも配布されているようなので少し見てみましたが、MiniBlogApplication.phpにmysqlの接続設定が書かれていますが、環境に合わせてありますか?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/13 12:29

    すみませんが、どうやってもうまくいかないので諦めて読むだけにしようと思います。これまで書いたコードが水の泡になってしまうのですが、読むだけでもいいかなと思うのでそうしようと思った次第です。
    ここまで対応してくださったということもあり、こちらをBAとさせていただきクローズしたいと思います。

    キャンセル

  • 2019/08/14 16:47 編集

    nullbot さんのコメントを無駄にしたくないので、コメントしましたが、
    同様な内容を回答しましたので、削除しました。

    キャンセル

  • 2019/08/16 15:18

    どうやら自分のコードが間違っていたみたいです。perfectPHPさんの回答をBAにさせていただきました。

    キャンセル

+1

質問からは少し離れますが、こういうの読むと良いですよ。
PHP7 で堅牢なコードを書く - 例外処理、表明プログラミング、契約による設計 / PHP Conference 2016

public function __construct($con)

この辺とかナントカしたくなると思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/12 22:35

    回答ありがとうございます。
    基本は本を写経しているだけなので、詳しいことがよくわからず引用されてらっしゃるエントリについても難しくて理解しがたいものでした。

    キャンセル

  • 2019/12/28 10:30

    質問から、かなり離れています。
    エラーは、解決しません。

    何が目的ですか?

    キャンセル

  • 2019/12/28 11:44

    フレームワークなら Error を正しく検出する前提を整えるといいって提案だけど?

    キャンセル

0

$this->con が bool型で
boolにはprepareなんてメソッドないですよ

って感じのエラーです。書いてあるままですが。
conが何か不明ですが、雰囲気的にDBの接続に失敗してるんじゃないですかね

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/10 20:54

    このあたりかな?

    C:\xampp\htdocs\php-blog.localhost\controllers\StatusController.php(8)

    キャンセル

  • 2019/08/12 22:43

    回答ありがとうございます。おっしゃっておられて個所を見てみても本と変わっておらず、依然としてエラーは出たままになっていしまいます。

    キャンセル

  • 2019/08/12 22:47

    うーん、十中八九dbに繋げてないだけかと思うんですが
    他の所でdbにアクセスには成功しているんでしょうか?

    キャンセル

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

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

関連した質問

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