【问题标题】:PHP: Trouble with autoloadingPHP:自动加载问题
【发布时间】:2016-02-22 14:54:22
【问题描述】:

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

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

我现在想换成 OOP 方法,但我无法理解如何自动加载我的类。我已经浏览了许多相关页面,例如; PSR-4 Examplespl_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

作为一个示例,您可以帮助我理解这一点,我们将只讨论两个类 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() 的某些实现将这些类加载到另一个 .php 文件中,以便我可以使用它们来创建对象?例如在一个名为 'Testing.php'

  2. 的文件中
  3. 以前我会在我的文件夹根目录之外解析一个 .ini 文件以获取数据库连接详细信息,我应该使用我在类 DbConfig?

【问题讨论】:

  • spl_autoload_register(function ($class) { include 'classes/' . $class . '.class.php'; }); 1. 将此功能用于您必须始终加载的 .php 文件。 2. 类名和文件名要一致,否则自动加载将找不到类。示例 Testing.php 具有类名 testing。 :)
  • 一个措辞和结构很好的 php 问题 +1
  • 如果您想遵守 PSR-4,为什么不使用 composer 自动加载器?
  • @JustinHoward 我会去看看,但对我来说了解发生了什么很重要。
  • 在这个阶段,您似乎不需要 PSR-4 兼容性,因此只需按照@Desert_King 的建议使用自定义自动加载器。如果您确实想要正式的 PSR-4 自动加载器,请查看 Zero to PSR-4 in 60 seconds

标签: php apache oop autoload


【解决方案1】:

根据 PSR-4 标准,

完全限定的类名必须具有顶级命名空间名称,也称为“供应商命名空间”。

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

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

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

为了具体回答您的问题,(1)只需将函数放入autoload.php,并进行我上面提到的修改。并且 (2) 由于 ini 文件不是 PHP 类,只要您的代码知道如何找到它(无论是硬编码路径还是其他),它在哪里都没有关系。

【讨论】:

  • 我认为我的顶级命名空间名称是 Lib,因为那是我保存所有类的地方......这是否意味着“供应商命名空间”实际上并不代表文件夹本身?如果是这样,你让我明白了。
  • 啊。您的顶级命名空间可能是Lib,但这可能不是一个好的顶级命名空间(因为它太通用了)。无论如何,如果你想这样做,只需设置$prefix='Lib\\';。这个怎么样?使您的顶级命名空间AskMeOnce。将Lib\Config 替换为AskMeOnce\Config。创建您的基本目录C:\Apache\htdocs\Lib\AskMeOnce(添加 AskMeOnce 文件夹)。这样,您可以根据需要在 lib 文件夹中包含其他库。而“vendor namespace”代表的是“basedir”。它不会像命名空间的其余部分那样变成文件夹路径。
  • 这意味着我要做的就是在我需要使用类的页面上需要 autoloader.php(比如 Testing.php ) 将位于 C:\Apache\htdocs ?
  • 是的。确切地。在您想要使用课程的任何页面上,require 'autoloader.php'Foo\Testing.php 将位于 C:\Apache\htdocs\Testing.php 如果“供应商命名空间”是 Foo 并且 basedir 是 C:\Apache\htdocs。此外,用反引号 (`) 将内容括起来,使其看起来像代码。
【解决方案2】:

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

创建一个名为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 来处理依赖项/类的自动加载,并且还允许使用 PRS-4 命名空间。

接下来,您的 Database 类将存储在 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 脚本,该脚本负责自动包含类。

<?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 数据库连接作为依赖项传递给Database 类作为示例。这称为Dependency Injection

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

<?php

namespace Acme\Services;

class UserService
{
    ...
}

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

$userService = new \Acme\Services\UserService;

我还建议您查看Composer。如果您想使用来自Packagist 的公共包,这将非常有用,而且创建自己的包非常容易。也支持 PSR-* 自动加载(最常见的是 PSR4)。

【讨论】:

  • 除非他将所有课程移至名为classes 的文件夹中,否则这是行不通的,这可能不是他想要做的。
【解决方案3】:

注册一个自动加载函数:

在您使用其他 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);

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

【讨论】:

  • 这不适用于他上面的目录结构。并非所有类都在 htdocs 文件夹中。
  • 如果是这样,您可以尝试自动加载功能中的目录
猜你喜欢
  • 2018-07-11
  • 1970-01-01
  • 2012-03-21
  • 2019-07-12
  • 2015-06-11
  • 2014-08-20
  • 2020-06-17
  • 1970-01-01
  • 2013-06-01
相关资源
最近更新 更多