2

I have a class extending mysqli. It follows a singleton pattern so I have a static method to retrieve the shared instance of the class.

After calling self::$instance = new self I suppose self::$instance should be FALSE or NULL if __construct could not make the connection, but it is not.

The __construct function triggers a WARNING: Warning: mysqli::mysqli(): (HY000/2002): Can't connect to local MySQL server through socket etc.

But self::$instance is created as an instance of BaseDatos class.

How should I detect a failure in the connection and return FALSE on the factory method?

class BaseDatos extends mysqli {

    //singleton, instancia compartida
    private static $instance = null;    
    private $user = "root";
    private $password = "root";
    private $db = "agendaeventos";
    private $dbHost = "localhost";

    public static function getInstance() {
        if (!self::$instance instanceof self) {
            self::$instance = new self;
        }
        if (self::$instance) {
            return self::$instance;
        } else {
            return FALSE; //This is never called even when the connection is not created
        }
    }

    private function __construct() {
        parent::__construct($this->dbHost, $this->user, $this->password, $this->db);
        if (!mysqli_connect_errno()) {
            parent::set_charset('utf8');
        }
    }

}
Charles Sprayberry
  • 7,741
  • 3
  • 41
  • 50
David Casillas
  • 1,801
  • 1
  • 29
  • 57
  • Create the mysqli instance in your bootstrap and pass it to the object instances that use it. You dont need a Database Singleton. See [Who needs Singletons](http://stackoverflow.com/questions/4595964/who-needs-singletons/4596323#4596323) – Gordon Jan 30 '12 at 22:30
  • 1
    @Gordon, nice comment but starts another discussion away from my question. – David Casillas Jan 30 '12 at 22:35
  • you cannot have singletons without discussing their validity imo. And my suggestion solves your problem. – Gordon Jan 31 '12 at 09:12

2 Answers2

2

A constructor has to create an instance, i.e. it can't return false or null.

You can stop the instanciation by throwing an exception in your constructor. For example, if you can't make the connection to the database, throw an exception and then catch it in getInstance() to return false.

public static function getInstance() {
    if (self::$instance === null) {
        try {
            self::$instance = new self();
        } catch(Exception $e) {
            self::$instance = false;
        }
    }
    return self::$instance
}
private function __construct() {
    parent::__construct($this->dbHost, $this->user, $this->password, $this->db);
    if (!mysqli_connect_errno()) {
        parent::set_charset('utf8');
    } else {
         throw new Exception("Database connection");
    }
}

However, I recommend you don't return false in getInstance(), but instead just throw the exception and catch it where the class is used.

Matthieu Napoli
  • 48,448
  • 45
  • 173
  • 261
  • +1 If a constructor throws an exception, no instance is created... so make sure it's caught in your calling code – Mark Baker Jan 30 '12 at 22:25
  • In the end, I just let the constructor return the instance and check the value of connect_errno. Makes the code simpler and avoids using an exception for something not really considered an exception. I'm not really happy with this PHP pattern, but better stick with it. – David Casillas Feb 04 '12 at 10:51
  • @DavidCasillas erm this is exactly the case where you should use an exception: you can't connect to your database. (personnaly the only thing I'm not happy with is that errors/warnings exists: everything should throw an exception IMO, so you don't have to handle different types of failures) – Matthieu Napoli Feb 06 '12 at 08:54
  • @Mathieu I have learned to use exceptions when something that should not happen happens, so there is a bug in code (get a wrong type of param). Everything that should happen (like a data base not accessible) is not an exception. Take a look at SPL Exception types and you will see that falling to connect into a database does not fall into any category. All of them cover issues because of bugs in code. – David Casillas Feb 07 '12 at 16:41
  • @Mathieu I'm with you and I'm not happy with handling different exceptions and erros, but maybe just because I have not find a good practice to deal with them. Seems they are the day to go. – David Casillas Feb 07 '12 at 16:46
0

You should change your getInstance function to look like this

public static function getInstance() {
    try {
      if (!self::$instance instanceof self) {
        self::$instance = new self;
      }
      return self::$instance;
    }
    catch {
        return false;
    }
}
Adrian Serafin
  • 7,665
  • 5
  • 46
  • 67
  • As it was said, there is no exception thrown in the given code, just a standard PHP warning, so you won't `catch` anything. – Matthieu Napoli Feb 06 '12 at 08:52
  • Ok, didn't notice. IMHO being unable to connect to database is a pretty big issue, one might think perfect place for throwing an exception ;) – Adrian Serafin Feb 06 '12 at 10:13