GeoipDatabase   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 235
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 36
eloc 105
dl 0
loc 235
rs 9.52
c 2
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A flush() 0 15 6
A insert() 0 14 2
A __construct() 0 14 2
A rollback() 0 8 2
A showTables() 0 13 3
A fetch() 0 20 6
A beginTransaction() 0 4 2
A initialize() 0 21 2
A fetchAll() 0 12 4
A genDsn() 0 15 5
A commit() 0 4 2
1
<?php
2
3
namespace iriven\bin;
4
5
class GeoipDatabase
6
{
7
    const DS = DIRECTORY_SEPARATOR;
8
    /**
9
     * Database instance
10
     *
11
     * @var \PDO
12
     */
13
    private $oPDOInstance;
14
    /**
15
     * PDO transaction Counter
16
    *
17
    * @var integer
18
    */
19
    private $transactionCounter = 0;
20
    /**
21
     * Class Constructor
22
     *
23
     * @param string $database
24
     */
25
    public function __construct(string $database = null)
26
    {
27
        try {
28
            $aOptions = [
29
                    \PDO::ATTR_ERRMODE            => \PDO::ERRMODE_EXCEPTION,
30
                    \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
31
                    \PDO::ATTR_EMULATE_PREPARES   => false,
32
                ];
33
            $this->oPDOInstance = new \PDO($this->genDsn($database), null, null, $aOptions);
34
            $this->initialize();
35
        } catch (\Throwable $th) {
36
            trigger_error($th->getMessage(), E_USER_ERROR);
37
        }
38
        return $this->oPDOInstance;
39
    }
40
    /**
41
     * Create Database tables structure.
42
     *
43
     * @return GeoipDatabase
44
     */
45
    private function initialize()
46
    {
47
        $aCommands = [
48
            'CREATE TABLE IF NOT EXISTS `ipv4Range`(
49
                    `start` INT UNSIGNED ,
50
                    `end` INT UNSIGNED ,
51
                    `country` VARCHAR(2)
52
                )',
53
            'CREATE TABLE IF NOT EXISTS `ipv6Range`(
54
                    `start` BIGINT UNSIGNED ,
55
                    `end` BIGINT UNSIGNED ,
56
                    `country` VARCHAR(2)
57
                )',
58
            'CREATE UNIQUE INDEX IF NOT EXISTS `idx_ipv4Range` ON `ipv4Range`(`start`, `end`)',
59
            'CREATE UNIQUE INDEX IF NOT EXISTS `idx_ipv6Range` ON `ipv6Range`(`start`, `end`)'
60
            ];
61
        foreach ($aCommands as $command)
62
        {
63
            $this->oPDOInstance->query($command);
64
        }
65
        return $this;
66
    }
67
    /**
68
     * Generate PDO SQLite3 DSN
69
     *
70
     * @param string|null $database
71
     * @return string
72
     */
73
    private function genDsn(string $database = null)
74
    {
75
        $database || $database = 'Geoip.db.sqlite';
76
        try {
77
            $destination = rtrim(dirname(__DIR__), self::DS);
78
            $info = new \SplFileInfo($database);
79
            $dbName = $info->getFilename();
80
            $dbSuffix ='.sqlite';
81
            if (substr_compare(strtolower($dbName), $dbSuffix, -strlen($dbSuffix)) !== 0) { $dbName .= $dbSuffix ; }
82
        } catch (\Throwable $th) {
83
            trigger_error($th->getMessage(), E_USER_ERROR);
84
        }
85
        $destination .= self::DS.'data';
86
        if (!is_dir($destination)) { mkdir($destination, '0755', true); }
87
        return 'sqlite:'.realpath($destination).self::DS.$dbName;
88
    }
89
    /**
90
     * Get the table list in the database
91
     *
92
     * @return array
93
     */
94
    public function showTables()
95
    {
96
        try
97
        {
98
            $command = 'SELECT `name` FROM `sqlite_master` WHERE `type` = \'table\' ORDER BY name';
99
            $statement = $this->oPDOInstance->query($command);
100
            $tables = [];
101
            while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
102
                $tables[] = $row['name'];
103
            }
104
            return $tables;
105
        } catch (\PDOException $th) {
106
            trigger_error($th->getMessage(), E_USER_ERROR);
107
        }
108
    }
109
    /**
110
     * Retrieve Column(s) value from given table
111
     *
112
     * @param string $sTable
113
     * @param array $columns
114
     * @return array
115
     */
116
    public function fetchAll(string $sTable, array $columns = [])
117
    {
118
        !empty($columns) || $columns = '*';
119
        if (is_array($columns)) { $columns = implode('`, `', $columns); }
120
        try
121
        {
122
            $sCommand = 'SELECT `%s` from  `%s`';
123
            $statement = $this->oPDOInstance->prepare(sprintf($sCommand, $columns, $sTable));
124
            $statement->execute();
125
            return $statement->fetchAll();
126
        } catch (\PDOException $th) {
127
            trigger_error($th->getMessage(), E_USER_ERROR);
128
        }
129
    }
130
    /**
131
     * Return Country code from given IP address (converted to integer)
132
     *
133
     * @param integer $start
134
     * @param integer $ipVersion  (ip version)
135
     * @return string
136
     */
137
    public function fetch(int $start, int $ipVersion)
138
    {
139
        try
140
        {
141
            $sCommand  = 'SELECT `start`, `end`, `country` ';
142
            $sCommand .= 'FROM `ipv%dRange` ';
143
            $sCommand .= 'WHERE `start` <= :start ';
144
            $sCommand .= 'ORDER BY start DESC LIMIT 1';
145
            $statement = $this->oPDOInstance->prepare(sprintf($sCommand, $ipVersion));
146
            $statement->execute([':start' => $start ]);
147
            $row = $statement->fetch(\PDO::FETCH_OBJ);
148
            if (is_bool($row) && $row === false)
149
            {
150
                $row = new \stdClass();
151
                $row->end = 0;
152
            }
153
            if ($row->end < $start || !$row->country) { $row->country = 'ZZ'; }
154
            return $row->country;
155
        } catch (\PDOException $th) {
156
            trigger_error($th->getMessage(), E_USER_ERROR);
157
        }
158
    }
159
    /**
160
     * Empty a given list of database tables
161
     *
162
     * @param array $tablesList
163
     * @return void
164
     */
165
    public function flush(array $tablesList = [])
166
    {
167
        !empty($tablesList) || $tablesList = $this->showTables();
168
        is_array($tablesList) || $tablesList = [$tablesList];
169
        try
170
        {
171
            if (!empty($tablesList)):
172
                $sCommand = 'DELETE FROM `%s`';
173
                foreach ($tablesList as $sTable) {
174
                    $this->oPDOInstance->query(sprintf($sCommand, $sTable));
175
                }
176
                $this->oPDOInstance->query('VACUUM');
177
            endif;
178
        } catch (\PDOException $th) {
179
            trigger_error('Statement failed: '.$th->getMessage(), E_USER_ERROR);
180
        }
181
    }
182
    /**
183
     * Insert data into database
184
     *
185
     * @param integer $start
186
     * @param integer $end
187
     * @param integer $ipVersion
188
     * @param string $country
189
     * @return void
190
     */
191
    public function insert(int $start, int $end, int $ipVersion, string $country)
192
    {
193
        try
194
        {
195
            $sQuery = 'INSERT INTO `ipv%dRange` (`start`, `end`, `country`) values (:start, :end, :country)';
196
            $command = sprintf($sQuery, $ipVersion);
197
            $statement = $this->oPDOInstance->prepare($command);
198
            $statement->execute([
199
                ':start'   => $start,
200
                ':end'     => $end,
201
                ':country' => $country
202
            ]);
203
        } catch (\PDOException $th) {
204
            trigger_error('Statement failed: ' . $th->getMessage(), E_USER_ERROR);
205
        }
206
    }
207
    /**
208
     * Begin PDO transaction, turning off autocommit
209
     *
210
     * @return bool
211
     */
212
    public function beginTransaction()
213
    {
214
        if (!$this->transactionCounter++) {return $this->oPDOInstance->beginTransaction();}
215
        return $this->transactionCounter >= 0;
216
    }
217
    /**
218
     * Commit PDO transaction changes
219
     *
220
     * @return bool
221
     */
222
    public function commit()
223
    {
224
        if (!--$this->transactionCounter) {return $this->oPDOInstance->commit(); }
225
        return $this->transactionCounter >= 0;
226
    }
227
    /**
228
     * Rollback PDO transaction, Recognize mistake and roll back changes
229
     *
230
     * @return bool
231
     */
232
    public function rollback()
233
    {
234
        if ($this->transactionCounter >= 0) {
235
            $this->transactionCounter = 0;
236
            return $this->oPDOInstance->rollback();
237
        }
238
        $this->transactionCounter = 0;
239
        return false;
240
    }
241
}
242