Completed
Pull Request — develop (#5)
by Oyebanji Jacob
02:13
created

DatabaseConnection::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
c 4
b 1
f 0
dl 0
loc 10
rs 9.4285
cc 1
eloc 6
nc 1
nop 1
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
        
51
        $this->loadEnv(); // load the environment variables
52
        $neededValues = array('DRIVER', 'HOSTNAME','USERNAME','PASSWORD','DBNAME','PORT'); 
53
        //Extract needed environment variables from the $_ENV global array
54
        $this->config = array_intersect_key($_ENV, array_flip($neededValues)); 
55
        $dsn = $dbConnStringFactory->createDatabaseSourceString($this->config);
56
        $this->databaseConnection = $this->createConnection($dsn);
57
    }
58
59
    /**
60
     * Get the instance of the class.
61
     *
62
     * @return Pyjac\ORM\DatabaseConnection
63
     */
64
    public static function getInstance()
65
    {
66
        if (self::$instance === null) {
67
            self::$instance = new self(new DatabaseConnectionStringFactory());
68
        }
69
70
        return self::$instance;
71
    }
72
73
    /**
74
     * Create a new PDO connection.
75
     *
76
     * @param string $dsn
77
     *
78
     * @return \PDO
79
     */
80
    public function createConnection($dsn)
81
    {
82
        $username = $this->config['USERNAME'];
83
84
        $password = $this->config['PASSWORD'];
85
86
        try {
87
            $pdo = new PDO($dsn, $username, $password, $this->options);
88
        } catch (\Exception $e) {
89
            $pdo = $this->tryAgainIfCausedByLostConnection(
90
                $e, $dsn, $username, $password, $this->options
91
            );
92
        }
93
94
        return $pdo;
95
    }
96
97
    /**
98
     * Get the default PDO connection options.
99
     *
100
     * @return array
101
     */
102
    public function getDefaultOptions()
103
    {
104
        return $this->options;
105
    }
106
107
    /**
108
     * Set the default PDO connection options.
109
     *
110
     * @param array $options
111
     *
112
     * @return void
113
     */
114
    public function setDefaultOptions(array $options)
115
    {
116
        $this->options = $options;
117
    }
118
119
    /**
120
     * Handle a exception that occurred during connect execution.
121
     *
122
     * @param \Exception $e
123
     * @param string     $dsn
124
     * @param string     $username
125
     * @param string     $password
126
     * @param array      $options
127
     *
128
     * @throws \Exception
129
     *
130
     * @return \PDO
131
     */
132
    protected function tryAgainIfCausedByLostConnection(\Exception $e, $dsn, $username, $password, $options)
133
    {
134
        if ($this->causedByLostConnection($e)) {
135
            return new PDO($dsn, $username, $password, $options);
136
        }
137
138
        throw $e;
139
    }
140
141
    /**
142
     * Determine if the given exception was caused by a lost connection.
143
     *
144
     * @param \Exception $e
145
     *
146
     * @return bool
147
     */
148
    protected function causedByLostConnection(\Exception $e)
149
    {
150
        $message = $e->getMessage();
151
152
        return Helpers::contains($message, [
153
            'server has gone away',
154
            'no connection to the server',
155
            'Lost connection',
156
            'is dead or not enabled',
157
            'Error while sending',
158
            'decryption failed or bad record mac',
159
            'SSL connection has been closed unexpectedly',
160
            'Deadlock found when trying to get lock',
161
        ]);
162
    }
163
164
     /**
165
      * Load needed configuration values from the .env file using Dotenv.
166
      * 
167
      * @return void
168
      */
169
     public function loadEnv()
170
     {
171
        if (!getenv('APP_ENV')) {
172
            $dotenv = new \Dotenv\Dotenv(__DIR__.'/../../../');
173
            $dotenv->overload();
174
         }
175
     }
176
}
177