-1

Working on an old website (PHP 3), I need to update its code, notably this DB connection class (mysql_db_query seems deprecated) :

class db {
var $host; 
var $port;
var $login;
var $pass;
var $data_base;

function db(){
    $this->host = "";
    $this->port = ""; 
    $this->login = "";
    $this->pass = "";
    $this->data_base = "";
}

function connect() {
    return mysql_connect($this->host.':'.$this->port, $this->login, $this->pass);
}

function query( $query ){
    if ( $query )
        return mysql_db_query($this->data_base,$query);
        return mysql_query($query);
    return 0;
}

function close( $link ){
    mysql_close($link);
}
}

Could anyone tell me how to update it?

Thank you by advance for your help.

guillaume
  • 21
  • 5
  • BTW only something is deprecated does not (yet) mean you need to change the code. It still works, it just warns you that sometime next you need to plan to refactor the code to make use of a new database client API. It's a warning, not a fatal error. The PHP manual has guides how to migrate from one PHP version to the other. For 3 to 4: http://www.helmholtz-muenchen.de/ibb/homepage/karsten.rodenacker/php_html/migration4.html For 4 to 5: http://php.net/manual/en/faq.migration5.php – hakre Sep 27 '13 at 09:44

3 Answers3

1

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:

  1. 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).
  2. 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.

Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
  • Thank you Hakre for your great help. Seems that I have pretty much work on this old website. My first tests are not really conclusive and I think I'm gonna restart from zero. I'll let you know. Thank you again. – guillaume Sep 30 '13 at 08:22
  • Well, take this answer as a *suggestion*. The first thing you should do is to put your code under version control. Then consider the next steps. Rewriting from scratch mostly never makes it, it's just that old code comes with a high maintenance cost and you can not easily escape that. The answer is only to show one thing: You can have new and old code together so that you don't destroy your working code while you introduce new one. The answer is not good in the part about the second interface. It's just that I only see a small fraction of your code which makes an answer always limited. – hakre Sep 30 '13 at 08:55
0

There's a lot of articles on the web about how to migrate, here's example on class in question:

class db {
var $host; 
var $port;
var $login;
var $pass;
var $data_base;

function db(){
    $this->host = "";
    $this->port = ""; 
    $this->login = "";
    $this->pass = "";
    $this->data_base = "";
}

function connect() {
    $mysqli = new mysqli($this->host, $this->login, $this->pass, $this->data_base, $this->port);

    if( $mysqli->connect_errno )
        return 0;
    else
        return $mysqli;
}

function query( $query ){
    if ( $query )
        return mysqli_query($query);
    return 0;
}

function close( $link ){
    mysqli_close($link);
}
}

There's a great source scanning tool for conversion mysql to mysqli here:
Converting to MySQLi

arma
  • 4,084
  • 10
  • 48
  • 63
-1

You need to read the documentation for mysqli - See http://php.net/manual/en/book.mysqli.php.

This library can also be used in a functional way. Most of the library calls to mysql_ library will have a similar one in mysqli_

Ed Heal
  • 59,252
  • 17
  • 87
  • 127
  • Please add that as a comment to the canonical question linked above, not here (if it does not exist there already). Thank you! – hakre Sep 27 '13 at 09:42