The best way to update that class is to create a new one next to it IMHO. That will allow you to change the code step by step without destroying the existing one (or at least in a more controlled manner).
You can do that by first extracting the interface out of the existing class and then implementing the new class implementing the same interface.
You do this most easily with an IDE like PHP Storm, but this is no requirement, you can also do it all by hand.
First of all you extract the interface out of class db
, for example as DbInterface
(In PHPStorm right-click on the classname "db" and then select Refactor -> Extract -> Interface):
interface DbInterface {
/**
* @param $link
* @return mixed
*/
function close($link);
/**
* @param string $query
* @return mixed
*/
function query($query);
function connect();
}
I also added some docblock comments which were missing. Also we make the db
class to implement the interface:
class db implements DbInterface {
Now test again your code, everything should run without any problems.
This interface already shows some flaws you've got which is worth to note so we don't miss them later:
- An object of that class is configured with some code that assigns database configuration and credentials via public members, so there is some code elsewehere (and not visible in your question). With the change of the database client library the way how the class needs to be configured might change as well. The configuration code should be encapsulated as well so that it can be replaced (e.g. moved into the class or into a configuration object).
- The close method requires a database link from the outside. As the class itself controls the connection, that link actually should be an internal information.
At this point you can decide how you want to continue. I would suggest you to start with 2.) first and refactor the code to pull that $link
variable in, instead of externalising it to the class. However, externalising can be done as well, it just requires a new type then which is introduced as a new interface:
interface DbLinkInterface {
/**
* @return mixed
*/
public function getLink();
/**
* @return DbInterface
*/
public function getDatabase();
public function close();
}
And this interface then is implemented by a new class which is mainly encapsulating the details about the link:
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);
}
}
Your code should still run flawless when you test it now. Test after each little step.
The next step then is to introduce this class into the existing class so that a database link can be passed around as a concrete type. This step is somewhat dangerous because depending on how you use that $link
in your code at other places, so things might need more changes to behave as usual, so you need to test. You might for example replace
mysql_something($link);
with at least
mysql_something($link->getLink());
However you actually must remove that function from there. So changing the type of $link
actually makes all those places visible. Add methods to the DbInterface
then and/or to the DbLinkInterface
so that you can do things more fluently:
$link->something();
But that depends a lot on your code and how well you're with refactoring (see the linked duplicate question it suggests a different way scanning all files and do replacements automatically with a compatible API if you think it's not worth to branch by abstraction).
First of all, the DbLinkInterface
is introduced into the DbInterface
:
interface DbInterface {
/**
* @param DbLinkInterface $link
* @return void
*/
function close(DbLinkInterface $link);
...
This will make the existing code to fail because the interface is not satisfied in db
which then needs at least this change:
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.');
}
}
Now the code can be loaded again, however you get a fatal error at the place where you pass the link into close
. therefore the class db
needs another change when returning that link:
class db implements DbInterface {
...
function connect() {
$link = mysql_connect($this->host . ':' . $this->port, $this->login, $this->pass);
return new DbLink($link, $this);
}
...
When you used the link only to close the database connection (as it looks like a bit), your whole code should work again. Test it to see so.
So now finally the old class has been abstracted into two interfaces which will allow you to replace it more easily. That means you will add additional code so that the old application can still run with the old code and you can also test with the new code. All you need to do is to change at the location where you use it, which normally is a single object instance (variable).
So now you introduce the class that is using the new database client 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.');
}
}
You can now use this class instead the other one interchangeably, so for testing purposes and that you don't need to use it in your whole application or you can move on script by script or similar.
As this example shows, externalizing the link does introduce differences that need to be encapsulated first.
Because you don't change the database server, the configuration and some part of your old code could be kept.
Put your code under version control so you can easily do this step by step and also revert in case you introduced a mistake and/or one of the concepts outlined here does not work for you.
I hope this is helpful. And keep in mind to do small steps and think about how you can introduce new code without changing the old one everwhere. E.g. like I did here with a new class instead of changing the old class.
To make this process more robust you can also introduce assertions, see Should I be using assert in my PHP code? for more information about that.