PHP:自动加载有问题

PHP: Trouble with autoloading

我正在创建一个网站,我使用 Apache 作为我的网络服务器。

我创建了一个没有使用 classes 的网站。我所有的功能都位于一个文件中,我只是在需要使用某些功能的每个页面上包含这个文件。

我现在想切换到 OOP 方法,但我无法理解如何自动加载我的 classes。我已经浏览了许多相关页面,例如; PSR-4 Example, spl_autoload_register(), Related Question 我似乎无法理解这个问题。

因此,由于我使用的是 Apache,我网站的根目录路径是 C:\Apache\htdocs.

我的目录如下所示;

+ htdocs
    + Lib
        + Base
            - User.php
            - Device.php
            - Company.php
            - Database.php
        + Services
            - UserService.php
            - DeviceService.php
            - CompanyServer.php
        + Config
            - DbConfig.php

举个例子,这样你就可以帮助我解决这个问题,我们将只讨论两个 classes DbConfigDatabase.

DbConfig.php(省略连接细节)

<?PHP
namespace Lib\Config;

class DbConfig
{
    protected $serverName;
    protected $userName;
    protected $password;
    protected $dbName;

   public function __construct()
    {
        $this->serverName = 'not my server name';
        $this->userName = 'not my username';
        $this->passCode = 'not my password';
        $this->dbName = 'not my database';
    }
}

Database.php(非常简单,直到我可以实际使用它——此时我将添加它的功能)

<?PHP
namespace Lib\Base;

use Lib\Config\DbConfig;

class Database extends DbConfig
{
    protected $connection;
    private $dataSet;
    private $sqlQuery;

    public function __construct()
    {
        parent::__construct();      
        $this->connection = null;
        $this->dataSet = null;
        $this->sqlQuery = null;
    }
}

所以..

  1. 如何使用 spl_autoload_register() 的某些实现将这些 class 加载到另一个 ].php 文件以便我可以使用它们来创建对象?例如在一个名为 'Testing.php'

  2. 的文件中
  3. 以前我会在我的文件夹的根目录之外解析一个 .ini 文件来获取数据库连接的详细信息,我应该用我的新方法这样做吗在 class DbConfig?

  4. 中包含连接详细信息

根据PSR-4标准,

The fully qualified class name MUST have a top-level namespace name, also known as a "vendor namespace".

您的 classes 似乎没有这个。要添加它,请将 namespace Lib\Config; 更改为例如 namespace AskMeOnce\Lib\Config;。您需要将此前缀添加到所有 classes.

然后,使用您引用的 PSR-4 Autoloader,将 $prefix = 'Foo\Bar\'; 更改为 $prefix = 'AskMeOnce\';,将 $base_dir = __DIR__ . '/src/'; 更改为 $base_dir = 'C:\Apache\htdocs';。将该函数放在一个名为 autoload.php 的文件中,并在任何需要知道如何自动加载的文件中要求它。

一旦你 require 那个自动加载器,PHP 就会知道在指定的基本目录中寻找任何以命名空间 AskMeOnce 开头的 class。从命名空间中删除该前缀后,命名空间代表路径的其余部分。例如,AskMeOnce\Lib\Config\DbConfig 将在 <basedir>/Lib/Config/DbConfig.php.

处查找

具体回答您的问题,(1) 只需将函数放在 autoload.php 中,并进行我上面提到的修改。并且 (2) 由于 ini 文件不是 PHP class,只要您的代码知道如何找到它(无论是硬编码路径还是否则)。

这是我拼凑的一个简单示例:

创建一个名为 autoloader.php 的文件。这个例子来自 PSR-4 Examples。将 Acme 更改为您的项目名称。

这将允许您使用命名空间,只要它们存储在 Lib/ 下并且您将命名空间添加到每个文件(正如您已经在做的那样)。

<?php
/**
 * An example of a project-specific implementation.
 * @param string $class The fully-qualified class name.
 * @return void
 */
spl_autoload_register(function ($class) {

    // project-specific namespace prefix
    $prefix = 'Acme\';

    // base directory for the namespace prefix
    $base_dir = __DIR__ . '/Lib/';

    // does the class use the namespace prefix?
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        // no, move to the next registered autoloader
        return;
    }

    // get the relative class name
    $relative_class = substr($class, $len);

    // replace the namespace prefix with the base directory, replace namespace
    // separators with directory separators in the relative class name, append
    // with .php
    $file = $base_dir . str_replace('\', '/', $relative_class) . '.php';
    print $file;
    // if the file exists, require it
    if (file_exists($file)) {
        require $file;
    }
});

这使用 spl_autoload_register 来处理 dependencies/classes 的自动加载,并且还允许使用 PRS-4 命名空间。

接下来,您的 Database class 将存储在 Lib/Base/Database.php.

<?php

namespace Acme\Base;

class Database
{
    private $db;

    public function __construct(\PDO $db)
    {
        $this->db = $db;
    }

    public function allUsers()
    {
        /*
         * Query to fetch all users from the database
         */

        return [
            [
                'username' => 'Bob',
                'role' => 'member'
            ],
            [
                'username' => 'Joe',
                'role' => 'admin'
            ]
        ];
    }
}

最后,您的索引页面包含 autoloader.php 脚本,它负责自动包含 classes。

<?php

require_once 'autoloader.php';

$config = require 'config.php';

try {
    $dbc = new PDO("mysql:dbname={$config['MYSQL']['DATABASE']};host={$config['MYSQL']['HOST']}",
                   $config['MYSQL']['USERNAME'], $config['MYSQL']['PASSWORD']);
} catch (PDOException $e) {
    die('Could not connect to database ' . $e->getMessage());
}

$db = new \Acme\Database($dbc);
print_r($db->allUsers());

最后,您询问了配置文件。我使用这样的配置文件:

<?php

return [
    'MYSQL' => [
        'USERNAME' => 'root',
        'PASSWORD' => '',
        'DATABASE' => 'test',
        'HOST' => 'localhost'
    ]
];

这允许您使用简单的方式包含配置文件:

$config = require 'config.php';

然后像这样访问:

$config['MYSQL']['USERNAME'];

我把\PDO数据库连接作为依赖传入Databaseclass作为例子。这叫做Dependency Injection.

另一个例子:Lib/Services/UserService.php:

<?php

namespace Acme\Services;

class UserService
{
    ...
}

您现在可以在代码中调用它(前提是包含自动加载器),如下所示:

$userService = new \Acme\Services\UserService;

我还建议查看 Composer. Super helpful if you want to use public packages from Packagist,创建您自己的非常容易。也支持 PSR-* 自动加载(PSR4 最常见)。

注册一个自动加载函数:

在您使用其他 php 库 类 之前,可以在任何文件中添加代码,例如在您的项目入口文件中。

defined('ROOT') or define('ROOT', 'C:/Apache/htdocs');
// define root directory
// or `defined('ROOT') or define('ROOT', __DIR__)` if
// the file is in the root directory

spl_autoload_register(function($name) {
    $file = ROOT . "/{$name}.php";
    if(!is_file($file)) {
        return false;
    } else {
        return include $file;
    }
}, false, true);

如果你的项目不够大,不建议使用命名空间,直接使用目录和文件名即可