joshmac 0 Light Poster

I have a singleton class that I am revamping and need a little help with. I want to use the following syntax for my queries without having to declare a global object. Below is my current code:

   /**
     * The db database object
     *
     * @access private
     * @var object
     */
    private $db;

    /**
     * MySQLi database object
     *
     * @access private
     * @var object
     */
    private static $instance;

    /**
     * Current result set
     *
     * @access private
     * @var object
     */
    private $result;

    /**
     * The last result (processed)
     *
     * @access private
     * @var array
     */
    private $last_result;

    /**
     * The number of rows from last result
     *
     * @access private
     * @var int
     */
    private $row_count;

    /**
     * Last error
     *
     * @access private
     * @var string
     */
    private $last_error;

    /**
     * PHP5 Constructor
     *
     * Making this function 'private' blocks this class from being directly created.
     *
     * @access private

     */
    private function __construct() { }

    /**
     * Creates and references the db object.
     *
     * @access public
     * @return object MySQLi database object
     */
    public static function instance() {
        if ( !self::$instance )
            self::$instance = new db();
        return self::$instance;
    }

    /**
     * Connect to the MySQL database.
     *
     * @param string $host MySQL hostname
     * @param string $user MySQL username
     * @param string $password MySQL password
     * @param string $name MySQL database name
     * @return bool True if successful, false on error.
     */
    public function connection($host, $user, $password, $name) {
        // Connect to the database
        $this->db = new mysqli($host, $user, $password, $name);
        // Check connection
        if ( mysqli_connect_errno() ) {
            $this->last_error = mysqli_connect_error();
            return false;
        }
        return true;
    }

    public function query($sql) {
        $this->result = $this->db->query($sql);
        return $this->result;
    }

So then, what would I need to change in my class so that I will not have to declare a global variable for other classes and functions to use like db::query->();? Thanks in advance for your help.

Dani AI

Generated

For : a minimal, safe way to allow static calls like db::query(...) without globals is to make the singleton return itself from a proper factory and add a static proxy that forwards unknown static calls to the instance. Also block cloning/unserializing so there’s truly one instance. Example additions (minimal and compatible with your existing non‑static methods):

public static function instance()
{
    if (null === self::$instance) {
        self::$instance = new self();
    }
    return self::$instance;
}

public static function __callStatic($name, $args)
{
    $obj = self::instance();
    if (!method_exists($obj, $name)) {
        throw new BadMethodCallException("Method {$name} does not exist");
    }
    return call_user_func_array([$obj, $name], $args);
}

private function __clone() { }

private function __wakeup() { throw new Exception("Cannot unserialize singleton"); }

With this in place, calls like db::query($sql) will be forwarded to the instance’s query() method, eliminating the need for a global variable. A few practical notes that complement the thread’s code:

  • Prefer new self() (not a hardcoded class name) so subclasses work correctly.
  • Use the mysqli OO properties for connection errors ($this->db->connect_errno / $this->db->connect_error) rather than the procedural helpers.
  • Set the charset ($this->db->set_charset('utf8mb4')) and consider enabling mysqli exceptions (mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)) to simplify error handling.
  • Avoid building SQL strings from user input; use prepared statements ($this->db->prepare() / bind) to prevent injection.
  • Be aware that singletons and static proxies reduce testability and hide dependencies; prefer injecting the DB object into classes when unit testing or in larger apps.

These changes keep the API convenience while improving safety and correctness.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.