更新该类的最佳方法是在它旁边创建一个新的,恕我直言。这将允许您在不破坏现有代码的情况下逐步更改代码(或至少以更可控的方式)。
您可以通过首先从现有类中提取接口然后实现实现相同接口的新类来做到这一点。
使用 PHP Storm 这样的 IDE 可以最轻松地完成此操作,但这不是必需的,您也可以手动完成。
首先,从 class 中提取接口db
,例如DbInterface
(在 PHPStorm 中右键单击类名“db”,然后选择 Refactor -> Extract -> Interface):
interface DbInterface {
/**
* @param $link
* @return mixed
*/
function close($link);
/**
* @param string $query
* @return mixed
*/
function query($query);
function connect();
}
我还添加了一些缺少的 docblock 注释。我们还让db
类来实现接口:
class db implements DbInterface {
现在再次测试您的代码,一切都应该运行没有任何问题。
这个界面已经显示了你的一些缺陷,值得注意的是,我们以后不会错过它们:
- 该类的对象配置有一些代码,这些代码通过公共成员分配数据库配置和凭据,因此这里有一些代码(在您的问题中不可见)。随着数据库客户端库的更改,需要配置类的方式也可能会发生变化。配置代码也应该被封装以便它可以被替换(例如移动到类或配置对象中)。
- close 方法需要来自外部的数据库链接。由于类本身控制连接,因此该链接实际上应该是内部信息。
此时,您可以决定如何继续。我建议您首先从 2.) 开始,然后重构代码以将该$link
变量拉入,而不是将其外部化到类中。但是,也可以进行外部化,它只需要一个新类型,然后将其作为新接口引入:
interface DbLinkInterface {
/**
* @return mixed
*/
public function getLink();
/**
* @return DbInterface
*/
public function getDatabase();
public function close();
}
然后这个接口由一个新类实现,该类主要封装有关链接的详细信息:
class DbLink implements DbLinkInterface
{
private $link;
/**
* @var DbInterface
*/
private $db;
public function __construct($link, DbInterface $db) {
$this->link = $link;
$this->db = $db;
}
/**
* @return mixed
*/
public function getLink() {
return $this->link;
}
/**
* @return DbInterface
*/
public function getDatabase() {
return $this->db;
}
public function close() {
$this->db->close($this);
}
}
当你现在测试它时,你的代码应该仍然可以完美运行。在每个小步骤之后进行测试。
下一步是将这个类引入现有类,以便可以将数据库链接作为具体类型传递。这一步有点危险,因为取决于$link
您在其他地方的代码中如何使用它,因此可能需要进行更多更改才能正常运行,因此您需要进行测试。例如,您可以替换
mysql_something($link);
至少与
mysql_something($link->getLink());
但是,您实际上必须从那里删除该功能。因此,更改类型$link
实际上会使所有这些地方都可见。将方法添加到DbInterface
then 和/或 toDbLinkInterface
以便您可以更流畅地执行操作:
$link->something();
但这在很大程度上取决于您的代码以及您在重构方面的表现如何(请参阅链接的重复问题,如果您认为不值得通过抽象进行分支,则它建议以不同的方式扫描所有文件并使用兼容的 API 自动进行替换)。
首先,将DbLinkInterface
引入DbInterface
:
interface DbInterface {
/**
* @param DbLinkInterface $link
* @return void
*/
function close(DbLinkInterface $link);
...
这将使现有代码失败,因为接口不满足,db
因此至少需要进行以下更改:
class db implements DbInterface {
...
function close(DbLinkInterface $link) {
if ($link->getDatabase() instanceof $this) {
return mysql_close($link->getLink());
}
throw new InvalidArgumentException('Invalid Link given. Can only close link of my own type.');
}
}
现在可以再次加载代码,但是在将链接传递到close
. db
因此,在返回该链接时,该类需要进行另一项更改:
class db implements DbInterface {
...
function connect() {
$link = mysql_connect($this->host . ':' . $this->port, $this->login, $this->pass);
return new DbLink($link, $this);
}
...
当您仅使用链接来关闭数据库连接时(看起来有点像),您的整个代码应该再次工作。测试一下看看。
因此,现在终于将旧类抽象为两个接口,这将允许您更轻松地替换它。这意味着您将添加额外的代码,以便旧应用程序仍然可以使用旧代码运行,并且您还可以使用新代码进行测试。您需要做的就是在使用它的位置进行更改,通常是单个对象实例(变量)。
因此,现在您介绍使用新数据库客户端 API 的类:
class db_mysqli extends db implements DbInterface {
/**
* @var mysqli
*/
private $db;
function connect() {
$this->db = new mysqli($this->host, $this->login, $this->pass, $this->data_base, $this->port);
return new DbLink($this->db, $this);
}
function query($query) {
return $this->db->query($query);
}
function close(DbLinkInterface $link) {
if ($link->getDatabase() instanceof $this) {
return $link->getLink()->close();
}
throw new InvalidArgumentException('Invalid Link given. Can only close link of my own type.');
}
}
您现在可以互换地使用这个类而不是另一个类,因此出于测试目的并且您不需要在整个应用程序中使用它,或者您可以逐个脚本或类似地移动。
如本例所示,将链接外部化确实会引入需要首先封装的差异。
因为您不更改数据库服务器,所以可以保留旧代码的配置和某些部分。
将您的代码置于版本控制之下,以便您可以轻松地逐步执行此操作,并在您引入错误和/或此处概述的概念之一不适合您的情况下恢复。
我希望这是有帮助的。并记住做一些小步骤,并考虑如何在不改变旧代码的情况下引入新代码。例如,就像我在这里使用新课程而不是更改旧课程一样。
为了使这个过程更加健壮,您还可以引入断言,请参阅我应该在我的 PHP 代码中使用断言吗?了解更多信息。