$pdo returns 实施路由器后方法中的 NULL

$pdo returns NULL from a method after implementing a router

我花了几个小时试图解决这个问题。 我的网站运行良好,但我需要更改一些难看的 URL。

我尝试实现一个路由器 (AltoRouter),经过一些更改后一切正常,直到出现此错误:

Fatal error: Uncaught Error: Call to a member function prepare() on null in foldername line X

我有一个基本的 MVC,模型如下所示:

基本上$pdo在这个方法中是空的: (还有那个 class 中的每一个方法)

class UsersModel {
    public function confirmedUser($username) {
        global $pdo;

        $sql = "SELECT * FROM users WHERE username = ? AND confirmed_at IS NOT NULL";
        $request = $pdo->prepare($sql); // Fatal error here
        $request->execute([$username]);
        return $request->fetch();
    }
}

控制器看起来像这样:

<?php
require_once "../php/connect.php";
require_once "../models/UsersModel.class.php";

$usersModel = new UsersModel();

if(array_key_exists('register', $_POST) && !empty($_POST)) {
    $username = $_POST['username'];
    $email = $_POST['email'];
    $password = $_POST['password'];
    $passwordConfirm = $_POST['passwordConfirm'];

    $usersModel->checkName($username);
    $usersModel->checkEmail($email);
    $usersModel->checkPassword($password, $passwordConfirm);
}

我的连接文件:

<?php

const DBNAME = "imparfait";
const DBUSER = "root";
const DBPASS = "";

$pdo = new PDO('mysql:host=localhost;dbname='.DBNAME.';charset=utf8', DBUSER, DBPASS);

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

路由器看起来像这样: (有点凌乱的自动取款机)

$router = new AltoRouter();

// map routes
$router->map('GET', '/', 'home', 'Home');
$router->map('GET', '/contact', 'contact', 'Contact');
$router->map('GET', '/about', 'about', 'About');
$router->map('GET', '/[*:slug]/[i:id]', 'article', 'Article');

$router->map('GET', '/login', function() {
    require './controller/users/login.php';
}, 'Login');
$router->map( 'POST', '/login', function() {
    require './controller/users/login.php';
});

$router->map('GET', '/forgot-password', function() {
    require './controller/users/forgotPassword.php';
}, 'Forgot');

$router->map( 'POST', '/forgot-password', function() {
    require './controller/users/forgotPassword.php';
});

$router->map('GET', '/logout', 'logout', 'Logout');

$router->map('GET', '/register', function() {
    require './controller/users/register.php';
}, 'Register');
$router->map( 'POST', '/register', function() {
    require './controller/users/register.php';
});

$router->map('GET', '/account', function() {
    require './controller/users/account.php';
}, 'Account');

$match = $router->match();

// call closure or throw 404 status
if(is_array($match)) {
    require './views/inc/header.phtml';

    if (is_callable( $match['target'])) {
        call_user_func_array( $match['target'], $match['params'] );
    } else {
        $params = $match['params'];
        require "./controller/{$match['target']}.php";
    }

    require './views/inc/footer.phtml';
} else {
    // no route was matched
    header( $_SERVER["SERVER_PROTOCOL"] . ' 404 Not Found');
    require './views/404.php';
}

我查看了堆栈溢出,但每个 post 都与数据库对象相关。

我不明白在实施路由器后什么会影响 $pdo 的值。 我需要创建一个 class 来解决这个问题吗?

感谢任何帮助。

@AbraCadaver 已经向您解释了原因,您遇到指定问题的原因。所以,在这方面,功劳都归于他。

请注意,在 MVC 应用程序的正确实现中,数据库连接仅由 model layer 的组件使用。

因此,考虑到您当前的设计,正确的解决方案是将 $pdo 实例传递给每个 model 在匹配路由的范围内创建的对象 目标.

因此,UsersModel 应该接收 $pdo 作为依赖项:

class UsersModel {

    /**
     * Database connection.
     * 
     * @var \PDO
     */
    private $connection;

    /**
     * @param PDO $connection Database connection.
     */
    public function __construct(\PDO $connection) {
        $this->connection = $connection;
    }

    public function confirmedUser($username) {
        // ...Please, no global variables anymore!...

        $sql = 'SELECT * 
                FROM users 
                WHERE 
                    username = :username AND 
                    confirmed_at IS NOT NULL';

        $statement = $this->connection->prepare($sql);

        $statement->execute([
            ':username' => $username,
        ]);

        $data = $statement->fetch(PDO::FETCH_ASSOC);

        /*
         * As I recall, PDOStatement::fetch returns FALSE when no records are found - instead of 
         * an empty array, like PDOStatement::fetch returns. But, in terms of PDO, a returned 
         * FALSE means "failure" and triggers an exception.
         * So I handle this "special" situation here.
         */
        return ($data === false) ? [] : $data;
    }
}

控制器(如path-to/controller/users/register.php)然后必须将$pdo对象传递给新创建的UsersModel实例(参见Dependency injection):

<?php
require_once '../php/connect.php';
require_once '../models/UsersModel.class.php';

$usersModel = new UsersModel($pdo);

if(/*...*/) {
    //...

    $usersModel->confirmedUser($username);
}

建议:在包含创建数据库连接的文件时始终使用 require 而不是 require_once

require "../php/connect.php";