0

I have the following code within a PDO database class

/**
 * Class DB
 */
class DB{
    /**
     * @var DB The one and only instance of this class;
     */
    private static $theOneAndOnly = null;
    /**
     * @var array
     */
    private static $transactionalOptions = array( PDO::ATTR_AUTOCOMMIT => false, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC );
    /**
     * @var array
     */
    private static $nonTransactionalOptions = array( PDO::ATTR_AUTOCOMMIT => true, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC );
    /**
     * @var PDO
     */
    private $transactionalDatabase = null;
    /**
     * @var PDO
     */
    private $nonTransactionalDatabase = null;
    /**
     * @var bool
     */
    private $isConnected = false;

    /**
     * Initializes the connections to the database. One connection for transaction based operations and one connection
     * for non-transactional operations;
     */
    private function __construct(){
        if($this->isConnected() !== true){
            //connect
            if($this->connect() !== true){
                //connection failed
                exit( 'An internal error occurred. Please contact Keyfort support:' );
            }
        }
    }

    /**
     * @return bool
     */
    public function isConnected(){
        return $this->isConnected;
    }

    /**
     * @return bool
     */
    public function connect(){
        try{
            $this->transactionalDatabase = new PDO(Config::DB_TYPE . ':host=' . Config::DB_HOST . ';dbname=' . Config::$DB_NAME, Config::$DB_USER, Config::$DB_PASS, self::$transactionalOptions);
            $this->nonTransactionalDatabase = new PDO(Config::DB_TYPE . ':host=' . Config::DB_HOST . ';dbname=' . Config::$DB_NAME, Config::$DB_USER, Config::$DB_PASS, self::$nonTransactionalOptions);

            $this->isConnected = true;

            return true;
        } catch(PDOException $exception){
            Log::error('Initializing the database connections failed:' . $exception->getTraceAsString());
            die();
            //return false;
        }
    }

    /**
     * @return DB
     */
    public static function &getInstance(){
        if(self::$theOneAndOnly === null){
            self::$theOneAndOnly = new self();
        }

        return self::$theOneAndOnly;
    }

    public static function ParamMultiple($key){
        return self::Param($key) . ', ';
    }

    public static function Param($key){
        return ':' . $key;
    }

    /**
     * Close all connections;
     */
    public function __destruct(){
        $this->disconnect();
    }

    /**
     * @return void
     */
    public function disconnect(){
        $this->transactionalDatabase = null;
        $this->nonTransactionalDatabase = null;
        $this->isConnected = false;
    }

    /**
     * @return PDO
     */
    public function &getTransactional(){
        if($this->isConnected() !== true){
            $this->connect();
        }

        return $this->transactionalDatabase;
    }

    /**
     * @return PDO
     */
    public function &getBasic(){
        if($this->isConnected() !== true){
            $this->connect();
        }

        return $this->nonTransactionalDatabase;
    }
}

It gets called via DB::getInstance();

The first time it gets called, it does not "die" if it errors.

I eventually get

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) in C:\path\to\class\db.php on line 64

Line 64 is the $this->transactionalDatabase line.

It does log the exception in my error log (as in the catch).

I'm drawing a blank as to why its not "die"ing, I've tried alot of things, and I'm wondering if it could be starting 2 PDOs in the try?

[edit] I'm running through the code with the wrong database user password currently. So I know what the error is caused by (it works ok when i put the correct password in), I just don't understand why it isn't processing the die() in the catch.

Use Fragment:

public static function GetByUsername($username){
    $db = DB::getInstance();
    $dbh = $db->getBasic();

    $sql = 'SELECT * FROM ' . self::TABLE_NAME . ' WHERE ' . self::KEY_USERNAME . ' = ' . DB::Param(self::KEY_USERNAME);

    $statement = $dbh->prepare($sql);

    $statement->bindValue(DB::Param(self::KEY_USERNAME), $username);

    if($statement->execute() === true){
        $rawUser = $statement->fetch();
        if($rawUser === null){
            return null;
        } else {
            return self::CreateFromSelectResult($rawUser);
        }
    } else {
        Log::error("Fetching user by username failed:" . print_r($dbh->errorInfo(), true));

        return null;
    }
}
user1842842
  • 95
  • 1
  • 2
  • 14
  • 1
    show us the fragment where you instantiate objects of this class. – Alex Oct 27 '15 at 16:16
  • i updated the question to include the class – user1842842 Oct 27 '15 at 16:22
  • 1
    I did ask for the object initialization and use fragment. not for full class description. something where you do `$DB = new DB;` – Alex Oct 27 '15 at 16:25
  • Sorry i should of been clearer. I added that "It gets called via DB::getInstance();" under the large part of code. I then added the class to explain what happens upon getInstance() – user1842842 Oct 27 '15 at 16:28
  • 1
    *It gets called via DB::getInstance();" under the large part of code.* that exactly what I need to help you. post the fragment of the page where you do those calls which brings the problem. – Alex Oct 27 '15 at 16:30
  • I posted an example of it's use, it gets called in a lot of places (because its meant to be singleton.). At the moment its erroring because im entering the wrong database user password, i just dont get why its not doing the "die()" and is carrying on – user1842842 Oct 27 '15 at 16:31
  • 1
    `because its meant to be singleton` I am not sure that you have a singleton here. – Alex Oct 27 '15 at 16:33
  • Oh, why? Its meant to be stored in " $theOneAndOnly " and the getInstance is meant to return " $theOneAndOnly" if its not null – user1842842 Oct 27 '15 at 16:35
  • 1
    http://stackoverflow.com/a/203359/4421474 – Alex Oct 27 '15 at 16:41
  • thank you, should that help with it not doing the die() ? if i make it a singleton properly – user1842842 Oct 27 '15 at 16:42
  • 1
    I guess you should `public static function &getInstance(){ static $theOneAndOnly = null; if($theOneAndOnly === null){ $theOneAndOnly = new self(); } return $theOneAndOnly; }` – Alex Oct 27 '15 at 16:42
  • 1
    My guess is not about `die()` but about `Allowed memory size of 134217728 bytes exhausted` to fix – Alex Oct 27 '15 at 16:44
  • Ok, i changed as you said, but i get the same `Allowed memory size of 134217728 bytes exhausted` – user1842842 Oct 27 '15 at 16:46
  • I figured out my issue ... my first line of the catch `Log::Error('');' .... thats logged in the database, so it was infinitely looping through attempting to connect every time it tried to log the error. Thank you so much for your help. – user1842842 Oct 28 '15 at 09:29

3 Answers3

1

Try using either transactionalDatabase or nonTransactionalDatabase, it should solve the problem.

Marcin
  • 1,488
  • 1
  • 13
  • 27
  • do you mean changing the try to only include one or the other? – user1842842 Oct 27 '15 at 16:23
  • I figured out my issue ... my first line of the catch `Log::Error('');' .... thats logged in the database, so it was infinitely looping through attempting to connect every time it tried to log the error. Thank you for your help. – user1842842 Oct 28 '15 at 09:28
1

Make sure your /tmp folder has space to cache queries.

Richard Merchant
  • 983
  • 12
  • 10
  • This error is caused through wrong password, im trying to figure out why it doesnt stop when it can't get in the database. It's a website reliant on the database so in a perfect scenario i can get it to stop and display an error to the user – user1842842 Oct 27 '15 at 16:24
  • 1
    Sorry, what i'm mainly focusing on is the fatal error, this must be happening before the script is killed. As a test, try increasing the memory say ini_set('memory_limit','16M'); Try 16,24,32.... This should be placed on the executing script. Again, i suppect that your server is running out of memory before it can finish processing the script. – Richard Merchant Oct 27 '15 at 17:35
  • In my PHP ini its set to `; Maximum amount of memory a script may consume (128MB) ; http://php.net/memory-limit memory_limit=128M` already, I'll have a go at increasing it – user1842842 Oct 28 '15 at 09:05
  • At `memory_limit=1024M` i get 574 new entries in the error log. At `2048` I get 761. I think I have an issue somewhere that it just keeps trying. – user1842842 Oct 28 '15 at 09:11
  • I figured out my issue ... my first line of the catch `Log::Error('');' .... thats logged in the database, so it was infinitely looping through attempting to connect every time it tried to log the error. Thank you so much for your help. – user1842842 Oct 28 '15 at 09:28
0

The issue was within "Log::error();"

Log:error() also tries to use the database.

So the issue was an infinite loop that started before the die() line was reached.

user1842842
  • 95
  • 1
  • 2
  • 14