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 DbConfig 和 Database.
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;
}
}
所以..
如何使用 spl_autoload_register() 的某些实现将这些 class 加载到另一个 ].php 文件以便我可以使用它们来创建对象?例如在一个名为 'Testing.php'
的文件中
以前我会在我的文件夹的根目录之外解析一个 .ini 文件来获取数据库连接的详细信息,我应该用我的新方法这样做吗在 class DbConfig?
中包含连接详细信息
根据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
数据库连接作为依赖传入Database
class作为例子。这叫做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);
如果你的项目不够大,不建议使用命名空间,直接使用目录和文件名即可
我正在创建一个网站,我使用 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 DbConfig 和 Database.
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;
}
}
所以..
如何使用 spl_autoload_register() 的某些实现将这些 class 加载到另一个 ].php 文件以便我可以使用它们来创建对象?例如在一个名为 'Testing.php'
的文件中
以前我会在我的文件夹的根目录之外解析一个 .ini 文件来获取数据库连接的详细信息,我应该用我的新方法这样做吗在 class DbConfig?
中包含连接详细信息
根据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
数据库连接作为依赖传入Database
class作为例子。这叫做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);
如果你的项目不够大,不建议使用命名空间,直接使用目录和文件名即可