DatabaseConnection   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 169
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 4

Importance

Changes 6
Bugs 2 Features 1
Metric Value
wmc 12
c 6
b 2
f 1
lcom 2
cbo 4
dl 0
loc 169
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getDefaultOptions() 0 4 1
A setDefaultOptions() 0 4 1
A __construct() 0 9 1
A getInstance() 0 8 2
A createConnection() 0 16 2
A tryAgainIfCausedByLostConnection() 0 8 2
A causedByLostConnection() 0 15 1
A loadEnv() 0 7 2
1
<?php
2
3
namespace Pyjac\ORM;
4
5
use PDO;
6
7
class DatabaseConnection implements DatabaseConnectionInterface
8
{
9
    /**
10
     * The instance of this class.
11
     *
12
     * @var Pyjac\ORM\DatabaseConnection.
13
     */
14
    private static $instance;
15
16
    /**
17
     * The PDO database connection in use.
18
     *
19
     * @var \PDO
20
     */
21
    public $databaseConnection;
22
23
    /**
24
     * The configuration values.
25
     *
26
     * @var array
27
     */
28
    private $config;
29
30
    /**
31
     * The default PDO connection options.
32
     *
33
     * @var array
34
     */
35
    protected $options = [
36
        PDO::ATTR_CASE              => PDO::CASE_NATURAL,
37
        PDO::ATTR_ERRMODE           => PDO::ERRMODE_EXCEPTION,
38
        PDO::ATTR_ORACLE_NULLS      => PDO::NULL_NATURAL,
39
        PDO::ATTR_STRINGIFY_FETCHES => false,
40
        PDO::ATTR_EMULATE_PREPARES  => false,
41
    ];
42
43
    /**
44
     * Create a new Database Connection.
45
     *
46
     * @param DatabaseConnectionStringFactoryInterface $dbConnStringFactory
47
     */
48
    public function __construct(DatabaseConnectionStringFactoryInterface $dbConnStringFactory)
49
    {
50
        $this->loadEnv(); // load the environment variables
51
        $neededValues = ['DRIVER', 'HOSTNAME', 'USERNAME', 'PASSWORD', 'DBNAME', 'PORT'];
52
        //Extract needed environment variables from the $_ENV global array
53
        $this->config = array_intersect_key($_SERVER, array_flip($neededValues));
54
        $dsn = $dbConnStringFactory->createDatabaseSourceString($this->config);
55
        $this->databaseConnection = $this->createConnection($dsn);
56
    }
57
58
    /**
59
     * Get the instance of the class.
60
     *
61
     * @return Pyjac\ORM\DatabaseConnection
62
     */
63
    public static function getInstance()
64
    {
65
        if (self::$instance === null) {
66
            self::$instance = new self(new DatabaseConnectionStringFactory());
67
        }
68
69
        return self::$instance;
70
    }
71
72
    /**
73
     * Create a new PDO connection.
74
     *
75
     * @param string $dsn
76
     *
77
     * @return \PDO
78
     */
79
    public function createConnection($dsn)
80
    {
81
        $username = $this->config['USERNAME'];
82
83
        $password = $this->config['PASSWORD'];
84
85
        try {
86
            $pdo = new PDO($dsn, $username, $password, $this->options);
87
        } catch (\Exception $e) {
88
            $pdo = $this->tryAgainIfCausedByLostConnection(
89
                $e, $dsn, $username, $password, $this->options
90
            );
91
        }
92
93
        return $pdo;
94
    }
95
96
    /**
97
     * Get the default PDO connection options.
98
     *
99
     * @return array
100
     */
101
    public function getDefaultOptions()
102
    {
103
        return $this->options;
104
    }
105
106
    /**
107
     * Set the default PDO connection options.
108
     *
109
     * @param array $options
110
     *
111
     * @return void
112
     */
113
    public function setDefaultOptions(array $options)
114
    {
115
        $this->options = $options;
116
    }
117
118
    /**
119
     * Handle a exception that occurred during connect execution.
120
     *
121
     * @param \Exception $e
122
     * @param string     $dsn
123
     * @param string     $username
124
     * @param string     $password
125
     * @param array      $options
126
     *
127
     * @throws \Exception
128
     *
129
     * @return \PDO
130
     */
131
    protected function tryAgainIfCausedByLostConnection(\Exception $e, $dsn, $username, $password, $options)
132
    {
133
        if ($this->causedByLostConnection($e)) {
134
            return new PDO($dsn, $username, $password, $options);
135
        }
136
137
        throw $e;
138
    }
139
140
    /**
141
     * Determine if the given exception was caused by a lost connection.
142
     *
143
     * @param \Exception $e
144
     *
145
     * @return bool
146
     */
147
    protected function causedByLostConnection(\Exception $e)
148
    {
149
        $message = $e->getMessage();
150
151
        return Helpers::contains($message, [
152
            'server has gone away',
153
            'no connection to the server',
154
            'Lost connection',
155
            'is dead or not enabled',
156
            'Error while sending',
157
            'decryption failed or bad record mac',
158
            'SSL connection has been closed unexpectedly',
159
            'Deadlock found when trying to get lock',
160
        ]);
161
    }
162
163
     /**
164
      * Load needed configuration values from the .env file using Dotenv.
165
      *
166
      * @return void
167
      */
168
     public function loadEnv()
169
     {
170
         if (!getenv('APP_ENV')) {
171
             $dotenv = new \Dotenv\Dotenv(__DIR__.'/../../../');
172
             $dotenv->overload();
173
         }
174
     }
175
}
176