HEX
Server: Apache
System: Linux server2.voipitup.com.au 4.18.0-553.109.1.lve.el8.x86_64 #1 SMP Thu Mar 5 20:23:46 UTC 2026 x86_64
User: posscale (1027)
PHP: 8.2.30
Disabled: exec,passthru,shell_exec,system
Upload Files
File: /home/posscale/subdomains/xibo/lib/Storage/PdoStorageService.php
<?php
/*
 * Xibo - Digital Signage - http://www.xibo.org.uk
 * Copyright (C) 2006-2015 Daniel Garner
 *
 * This file (PDOConnect.php) is part of Xibo.
 *
 * Xibo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version. 
 *
 * Xibo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Xibo.  If not, see <http://www.gnu.org/licenses/>.
 */

namespace Xibo\Storage;

use Xibo\Exception\DeadlockException;
use Xibo\Service\ConfigService;
use Xibo\Service\LogService;

/**
 * Class PDOConnect
 * Manages global connection state and the creation of connections
 * @package Xibo\Storage
 */
class PdoStorageService implements StorageServiceInterface
{
    /**
     * @var \PDO[] The connection
     */
	private $conn = [];

    /** @var array Statistics */
    private static $stats = [];

    /** @var  string */
    private static $_version;

    /**
     * Logger
     * @var LogService
     */
    private $log;

    /**
     * PDOConnect constructor.
     * @param LogService $logger
     */
	public function __construct($logger = null)
    {
        $this->log = $logger;
    }

    /** @inheritdoc */
    public function setConnection($name = 'default')
    {
        // Create a new connection
        $this->conn[$name] = PdoStorageService::newConnection();
        return $this;
    }

    /** @inheritdoc */
    public function close($name = null)
    {
        if ($name === null && isset($this->conn[$name])) {
            $this->conn[$name] = null;
            unset($this->conn[$name]);
        } else {
            $this->conn = [];
        }
    }

    /**
     * Create a DSN from the host/db name
     * @param string $host
     * @param string[Optional] $name
     * @return string
     */
    private static function createDsn($host, $name = null)
    {
        if (strstr($host, ':')) {
            $hostParts = explode(':', $host);
            $dsn = 'mysql:host=' . $hostParts[0] . ';port=' . $hostParts[1] . ';';
        }
        else {
            $dsn = 'mysql:host=' . $host . ';';
        }

        if ($name != null)
            $dsn .= 'dbname=' . $name . ';';

        return $dsn;
    }

    /**
     * Open a new connection using the stored details
     * @return \PDO
     */
	public static function newConnection()
    {
        $dsn = PdoStorageService::createDsn(ConfigService::$dbConfig['host'], ConfigService::$dbConfig['name']);

		// Open the connection and set the error mode
		$conn = new \PDO($dsn, ConfigService::$dbConfig['user'], ConfigService::$dbConfig['password']);
		$conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);

		$conn->query("SET NAMES 'utf8'");

		return $conn;
	}

    /**
     * Open a connection with the specified details
     * @param string $host
     * @param string $user
     * @param string $pass
     * @param string[Optional] $name
     * @return \PDO
     */
	public function connect($host, $user, $pass, $name = null)
    {
        if (!isset($this->conn['default']))
		    $this->close('default');

        $dsn = PdoStorageService::createDsn($host, $name);

        // Open the connection and set the error mode
		$this->conn['default'] = new \PDO($dsn, $user, $pass);
		$this->conn['default']->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
		$this->conn['default']->query("SET NAMES 'utf8'");

		return $this->conn['default'];
	}

    /** @inheritdoc */
    public function getConnection($name = 'default')
    {
        if (!isset($this->conn[$name]))
            $this->conn[$name] = PdoStorageService::newConnection();

        return $this->conn[$name];
    }

    /**
     * Check to see if the query returns records
     * @param string $sql
     * @param array[mixed] $params
     * @return bool
     */
    public function exists($sql, $params)
    {
        if ($this->log != null)
            $this->log->sql($sql, $params);

        $sth = $this->getConnection()->prepare($sql);
        $sth->execute($params);

        $this->incrementStat('default', 'exists');

        if ($sth->fetch())
            return true;
        else
            return false;
    }
    
    /**
     * Run Insert SQL
     * @param string $sql
     * @param array $params
     * @param \PDO[Optional] $dbh
     * @return int
     * @throws \PDOException
     */
    public function insert($sql, $params)
	{
        if ($this->log != null)
            $this->log->sql($sql, $params);

        if (!$this->getConnection()->inTransaction())
            $this->getConnection()->beginTransaction();

        $sth = $this->getConnection()->prepare($sql);

        $sth->execute($params);

        $this->incrementStat('default', 'insert');

        return intval($this->getConnection()->lastInsertId());
    }

	/** @inheritdoc */
	public function update($sql, $params)
	{
        if ($this->log != null)
            $this->log->sql($sql, $params);

        if (!$this->getConnection()->inTransaction())
            $this->getConnection()->beginTransaction();

        $sth = $this->getConnection()->prepare($sql);

        $sth->execute($params);

        $rows = $sth->rowCount();

        $this->incrementStat('default', 'update');

        return $rows;
	}

	/**
	 * Run Select SQL
	 * @param $sql
	 * @param $params
	 * @return array
	 * @throws \PDOException
	 */
	public function select($sql, $params)
	{
        if ($this->log != null)
            $this->log->sql($sql, $params);

        $sth = $this->getConnection()->prepare($sql);

        $sth->execute($params);

        $this->incrementStat('default', 'select');

        return $sth->fetchAll(\PDO::FETCH_ASSOC);
	}

	/** @inheritdoc */
	public function isolated($sql, $params)
    {
        // Should we log?
        if ($this->log != null)
            $this->log->sql($sql, $params);

        $sth = $this->getConnection('isolated')->prepare($sql);

        $sth->execute($params);

        $this->incrementStat('isolated', 'update');
    }

    /** @inheritdoc */
    public function updateWithDeadlockLoop($sql, $params, $connection = null)
    {
        // Should we log?
        if ($this->log != null)
            $this->log->sql($sql, $params);

        if ($connection === null)
            $connection = 'isolated';

        // Prepare the statement
        $statement = $this->getConnection($connection)->prepare($sql);

        // Deadlock protect this statement
        $success = false;
        $retries = 2;
        do {
            try {
                $this->incrementStat($connection, 'update');
                $statement->execute($params);
                // Successful
                $success = true;

            } catch (\PDOException $PDOException) {
                $errorCode = isset($PDOException->errorInfo[1]) ? $PDOException->errorInfo[1] : $PDOException->getCode();

                if ($errorCode != 1213 && $errorCode != 1205)
                    throw $PDOException;
            }

            if ($success)
                break;

            // Sleep a bit, give the DB time to breathe
            $queryHash = substr($sql, 0, 15) . '... [' . md5($sql . json_encode($params)) . ']';
            $this->log->debug('Retrying query after a short nap, try: ' . (3 - $retries) . '. Query Hash: ' . $queryHash);
            usleep(10000);

        } while ($retries--);

        if (!$success)
            throw new DeadlockException(__('Failed to write to database after %d retries. Please try again later.', $retries));
    }

    /** @inheritdoc */
    public function commitIfNecessary($name = 'default')
    {
        if ($this->getConnection($name)->inTransaction()) {
            $this->incrementStat($name, 'commit');
            $this->getConnection()->commit();
        }
    }

    /**
     * Set the TimeZone for this connection
     * @param string $timeZone e.g. -8:00
     * @param string $connection
     */
    public function setTimeZone($timeZone, $connection = 'default')
    {
        $this->getConnection($connection)->query('SET time_zone = \'' . $timeZone . '\';');

        $this->incrementStat($connection, 'utility');
	}

    /**
     * PDO stats
     * @return array
     */
    public function stats()
    {
        return self::$stats;
    }

    /** @inheritdoc */
    public function incrementStat($connection, $key)
    {
        $currentCount = (isset(self::$stats[$connection][$key])) ? self::$stats[$connection][$key] : 0;
        self::$stats[$connection][$key] = $currentCount + 1;
    }

    /**
     * Statically increment stats
     * @param $connection
     * @param $key
     */
    public static function incrementStatStatic($connection, $key)
    {
        $currentCount = (isset(self::$stats[$connection][$key])) ? self::$stats[$connection][$key] : 0;
        self::$stats[$connection][$key] = $currentCount + 1;
    }

    /**
     * @inheritdoc
     */
    public function getVersion()
    {
        if (self::$_version === null) {

            $results = $this->select('SELECT version() AS v', []);

            if (count($results) <= 0)
                return null;

            self::$_version = explode('-', $results[0]['v'])[0];
        }

        return self::$_version;
    }
}