Something wrong with Singleton class, no idea what

2019-07-20 06:34发布

问题:

I have never done any Singleton class before and now I figured that for this DB connection it will be a good idea to make one, but I have no clue why it is not working. I really would appreciate if someone would help me out with this one since I want to learn how OOP works...

Anyway, I fixed it with just updating my PHP to latest version, now $DBH = new static(); works fine, thanks people.

I tried to use $DBH = new static(); isntead of $DBH = new self(); but then I have this error:

Parse error: syntax error, unexpected T_STATIC, expecting T_STRING or T_VARIABLE or '$' in mSingleton.php on line 14

Error:

Fatal error: Cannot instantiate abstract class Singleton in mSingleton.php on line 14

Files: (mSingleton.php)

abstract class Singleton
{

    protected $DBH;

    public static function getInstance()
    {
        if ($DBH == null)
        {
            $DBH = new self();
        }

        return $DBH;
    }

}

(mDBAccess.php)

<?php
//mDBAccess.php
//Removed values ofc
$db_host = "";
$db_name = "";
$db_user = "";
$db_pass = "";

include "mSingleton.php";

class DBAccess extends Singleton
{
    protected $DBH;

    function __construct()
    {
        try
        {
        $this->DBH = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);
        $this->DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
        }
        catch (PDOException $e)
        {
            echo $e->getMessage();
        }
    }

    public static function getDBH()
    {
        return self::getInstance()->DBH;
    }
}

(mLog.php)

<?php
//mLog.php
include "mDBAccess.php";

class Log
{

    public static function Add($action)
    {
        try
        {
            $DBH = DBAccess::getDBH();

            //Getting user IP
            $ip = $_SERVER['REMOTE_ADDR'];

            //Getting time
            $time = date('Y-m-d');

            //Preparing our SQL Query
            $values = array($ip, $action, $time);
            $STH = $DBH->prepare("INSERT INTO log (ip, action, time)
                                  VALUES (?, ?, ?)");

            //Excecuting SQL Query
            $STH->execute($values);
        }
        catch (PDOException $e)
        {
            echo $e->getMessage();
        }
    }

}
//testing..
Log::Add("ddd");

回答1:

You need to write

$DBH = new static();

See: Late Static Binding



回答2:

My workaround for PHP 5 < 5.3 for abstract Singleton classes.

Taken from: http://code.google.com/p/phpraise/source/browse/trunk/phpraise/core/RaiseSingleton.php

/**
 * RaiseSingleton abstract class
 * Defines a Singleton class
 *
 * @author Sam Yong
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License v3
 * @abstract
 * @package Raise.Core
 */
abstract class RaiseSingleton extends RaiseObject {

    /**
     * Prevent creation of a new instance
     * Constructor not set to final to allow overrides in subclass
     */
    protected function __construct() {}

    /**
     * Prevent cloning of the Singleton instance
     * @final
     */
    final private function __clone() {}

    /**
     * Return the Singleton instance
     * @return object
     * @staticvar static $__instance
     */
    public static function getInstance() {
        static $__instance;
        $class = get_called_class();
        return $__instance ? $__instance : $__instance = new $class();
    }
}

Basically, after tripping off all the phpRaise necessities:

abstract class Singleton {

    protected function __construct() {}

    final private function __clone() {}

    public static function getInstance() {
        static $__instance;
        $class = get_called_class();
        return $__instance ? $__instance : $__instance = new $class();
    }
}


回答3:

self points there to the abstract class, and you can't instantiate an abstract class, you have to use new static() if you want to keep that organization.



回答4:

$DBH = new self(); is the same as $DBH = new Singleton(); which has been marked as abstract to prevent instantiation.

You should be using Late Static Binding to initiate the new obhect of the child class, try using new static().



回答5:

I will say upfront I am not proficient with PHP but there are a few things that stand out to me.

  • $DBH is redefined in DBAccess where it is first declared in Singleton.
  • $DBH in Singleton is not declared static, so how could $DBH = new static() work? Unless late binding with static converts $DBH into a static storage area.
  • If you convert $DBH in Singleton into a static variable, you'll have issues if anything else every derives from Singleton.

Unless someone wiser comes along and can make a Singleton class that can be utilized for any subclass of it your best bet is to put the logic directly into your DBAccess class. If someone can fix the semantic and syntax errors I'd appreciate it.

class DBAccess
{
    private static $_instance;

    protected $DBH;

    function __construct()
    {
        try
        {
            $this->DBH = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);
            $this->DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
        }
        catch (PDOException $e)
        {
            echo $e->getMessage();
        }
    }

    public static function getDBH()
    {
        if ($_instance == null)
            $_instance = new DBAccess();
        return $_instance->DBH;
    }
}