Advertisement
miksir

PDO with reconnect

Aug 17th, 2012
504
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 6.77 KB | None | 0 0
  1. <?php
  2. /**
  3.  * PDO adapter with keep-alive and auto-reconnect
  4.  * On SIGHUP if reconnect fail - keep old connection on
  5.  */
  6. class DbPDOAdapter implements DbAdapterInterface,ObserverInterface
  7. {
  8.     /** @var PDO */
  9.     protected $_db;
  10.     protected $_log;
  11.  
  12.     protected $config;
  13.     protected $options = array(
  14.         PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
  15.     );
  16.  
  17.     protected $ping_time = 600;
  18.  
  19.     /**
  20.      * @param LoggerInterface $log
  21.      * @param SugarDbData $config
  22.      * @param PosixEvent $posix
  23.      */
  24.     public function __construct(LoggerInterface $log, SugarDbData $config, PosixEvent $posix) {
  25.         $this->_log = $log;
  26.  
  27.         $this->config = $config;
  28.  
  29.         $this->create_pdo_object();
  30.         $this->setup_keepalive();
  31.  
  32.         $posix->addObserver($this, 'SIGHUP');
  33.     }
  34.  
  35.     /**
  36.      * @param string $method
  37.      * @param array $args
  38.      * @return mixed
  39.      */
  40.     protected function call_pdo($method, $args) {
  41.         $this->setup_keepalive();
  42.         return PDOMethodCaller::call_pdo($this, null, $this->_db, $method, $args);
  43.     }
  44.  
  45.     /**
  46.      * @param bool $reconfig
  47.      * @throws DbException
  48.      * @return PDO
  49.      */
  50.     public function create_pdo_object($reconfig = false) {
  51.         try {
  52.             $pdo = new PDO(
  53.                 $this->config->dsn,
  54.                 $this->config->user,
  55.                 $this->config->password,
  56.                 $this->options
  57.             );
  58.  
  59.             $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  60.             $this->log("MySQL connected {$this->config->dsn}", 'DEBUG');
  61.  
  62.         } catch (PDOException $e) {
  63.             $this->log($e->getCode() . ' ' . $e->getMessage(), 'ERROR');
  64.  
  65.             if (!$reconfig) {
  66.                 throw new DbException($e->getMessage());
  67.             } else {
  68.                 $this->log("Reconfig fail, keep old connection", 'DEBUG');
  69.                 return $this->_db;
  70.             }
  71.         }
  72.  
  73.         $this->config->clearChanged();
  74.         return $this->_db = $pdo;
  75.     }
  76.  
  77.     /**
  78.      * @param $sql
  79.      * @throws DbException
  80.      * @return DbPDOStatementAdapter
  81.      */
  82.     public function prepare($sql)
  83.     {
  84.         return new DbPDOStatementAdapter($this->call_pdo('prepare', array($sql)), $this);
  85.     }
  86.  
  87.     /**
  88.      * @throws DbException
  89.      * @return array
  90.      */
  91.     public function errorInfo()
  92.     {
  93.         return $this->call_pdo('errorInfo', array());
  94.     }
  95.  
  96.     public function ping() {
  97.         $this->call_pdo("query", array("SELECT 1"));
  98.         $this->log("Ping? Pong!", 'DEBUG');
  99.     }
  100.  
  101.     /**
  102.      * @param string $message
  103.      * @param string $level
  104.      */
  105.     public function log($message, $level='INFO') {
  106.         $this->_log->log(get_class().': '.$message, $level);
  107.     }
  108.  
  109.     /**
  110.      * prepare for call alarm signal after timeout
  111.      */
  112.     protected function setup_keepalive() {
  113.         if (function_exists('pcntl_alarm')) {
  114.             pcntl_alarm($this->ping_time);
  115.         }
  116.     }
  117.  
  118.     public function notify(ObservableInterface $source, $eventType, &$params = null)
  119.     {
  120.         if ($source instanceof PosixEvent) {
  121.             if ($eventType == 'SIGALRM')
  122.                 $this->ping();
  123.             elseif ($eventType == 'SIGHUP') {
  124.                 if ($this->config->isChanged())
  125.                     $this->create_pdo_object(true);
  126.             }
  127.         }
  128.     }
  129. }
  130.  
  131.  
  132. class DbPDOStatementAdapter implements DbStatementAdapterInterface {
  133.     protected $_st;
  134.     protected $_dba;
  135.  
  136.     /**
  137.      * @param PDOStatement $st
  138.      * @param DbPDOAdapter $db
  139.      */
  140.     public function __construct(PDOStatement $st, DbPDOAdapter $db) {
  141.         $this->_st = $st;
  142.         $this->_dba = $db;
  143.     }
  144.  
  145.     /**
  146.      * @param PDOStatement $st
  147.      */
  148.     public function setPDOStatement(PDOStatement $st) {
  149.         $this->_st = $st;
  150.     }
  151.  
  152.     /**
  153.      * @param string $method
  154.      * @param array $args
  155.      * @return mixed
  156.      */
  157.     protected function call_pdo_st($method, $args) {
  158.         return PDOMethodCaller::call_pdo($this->_dba, $this, $this->_st, $method, $args);
  159.     }
  160.  
  161.     /**
  162.      * @param array $params
  163.      * @throws DbException
  164.      * @return bool
  165.      */
  166.     public function execute($params = null)
  167.     {
  168.         return $this->call_pdo_st('execute', array($params));
  169.     }
  170.  
  171.     /**
  172.      * @throws DbException
  173.      * @return array
  174.      */
  175.     public function errorInfo()
  176.     {
  177.         return $this->call_pdo_st('errorInfo', array());
  178.     }
  179.  
  180.     /**
  181.      * @throws DbException
  182.      * @return mixed
  183.      */
  184.     public function fetch()
  185.     {
  186.         return $this->call_pdo_st('fetch', array(PDO::FETCH_ASSOC));
  187.     }
  188.  
  189.     /**
  190.      * @throws DbException
  191.      * @return mixed
  192.      */
  193.     public function fetchAll()
  194.     {
  195.         return $this->call_pdo_st('fetchAll', array(PDO::FETCH_ASSOC));
  196.     }
  197. }
  198.  
  199. class PDOMethodCaller {
  200.     /**
  201.      * @static
  202.      *
  203.      * @param DbPDOAdapter                $adapter_object
  204.      * @param DbPDOStatementAdapter|null  $statement_object
  205.      * @param PDO|PDOStatement                 $callable_object
  206.      * @param string                           $name
  207.      * @param array                            $arguments
  208.      *
  209.      * @throws DbException
  210.      * @return mixed
  211.      */
  212.     static public function call_pdo($adapter_object, $statement_object, $callable_object, $name, $arguments)
  213.     {
  214.         try {
  215.  
  216.             return call_user_func_array(array($callable_object, $name), $arguments);
  217.  
  218.         } catch (PDOException $e) {
  219.             if ($e->getCode() != 'HY000') {
  220.                 $adapter_object->log($e->getCode() . ' ' . $e->getMessage(), 'ERROR');
  221.                 throw new DbException($e->getMessage());
  222.             }
  223.  
  224.             $adapter_object->log($e->getCode() . ' ' . $e->getMessage() . '. Reconnecting...', 'ERROR');
  225.             // lets try to reconnect
  226.  
  227.             try {
  228.  
  229.                 $newpdo = $adapter_object->create_pdo_object();
  230.  
  231.             } catch (DbException $f) {
  232.                 $adapter_object->log($f->getMessage(), 'ERROR');
  233.                 throw new DbException($e->getMessage());
  234.             }
  235.  
  236.             try {
  237.  
  238.                 if ($callable_object instanceof PDOStatement) {
  239.                     $callable_object = $newpdo->prepare($callable_object->queryString);
  240.                     $statement_object->setPDOStatement($callable_object);
  241.                 } else {
  242.                     $callable_object = $newpdo;
  243.                 }
  244.  
  245.                 return call_user_func_array(array($callable_object, $name), $arguments);
  246.  
  247.             } catch (PDOException $f) {
  248.                 $adapter_object->log($f->getCode() . ' ' . $f->getMessage(), 'ERROR');
  249.                 throw new DbException($f->getMessage());
  250.             }
  251.  
  252.         }
  253.     }
  254. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement