DBAL   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 141
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 10
lcom 1
cbo 5
dl 0
loc 141
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A buildStatements() 0 18 1
A __construct() 0 7 1
A storeMeasurement() 0 10 2
A incrementMeasurement() 0 14 3
A getMeasurements() 0 23 3
1
<?php
2
3
/*
4
 * This file is part of the PHPProm package.
5
 *
6
 * (c) Philip Lehmann-Böhm <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace PHPProm\Storage;
13
14
use \Doctrine\DBAL\Connection;
15
16
/**
17
 * Class DBAL
18
 * Storage implementation using Doctrine DBAL.
19
 * This way, MySQL and other databases are supported.
20
 * The used SQL is kept very simple so the queries should work
21
 * with most of the DBAL supported databases.
22
 *
23
 * A MySQL example of the expected table:
24
 * CREATE TABLE `phpprom` (
25
 *     `key` varchar(255) NOT NULL,
26
 *     `value` double NOT NULL,
27
 *     PRIMARY KEY (`key`)
28
 * ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
29
 * @package PHPProm\Storage
30
 *
31
 * A SQLite example of the expected table:
32
 * CREATE TABLE `phpprom` (
33
 *     `key`	TEXT NOT NULL UNIQUE,
34
 *     `value`	REAL NOT NULL,
35
 *     PRIMARY KEY(`key`)
36
 * );
37
 *
38
 * A PostgreSQL example of the expected table:
39
 * CREATE TABLE public.phpprom (
40
 *     key VARCHAR(255) PRIMARY KEY NOT NULL,
41
 *     value DOUBLE PRECISION NOT NULL
42
 * );
43
 * CREATE UNIQUE INDEX phpprom_key_uindex ON public.phpprom (key);
44
 */
45
class DBAL extends AbstractStorage {
46
47
    /**
48
     * @var Connection
49
     * The DBAL connection.
50
     */
51
    protected $connection;
52
53
    /**
54
     * @var string
55
     * The table to use.
56
     */
57
    protected $table;
58
59
    /**
60
     * @var \Doctrine\DBAL\Driver\Statement
61
     * The prepared statement to check whether there is already a value for a given key.
62
     */
63
    protected $statementKeyExists;
64
65
    /**
66
     * @var \Doctrine\DBAL\Driver\Statement
67
     * The prepared statement to insert a new key value pair.
68
     */
69
    protected $statementKeyInsert;
70
71
    /**
72
     * @var \Doctrine\DBAL\Driver\Statement
73
     * The prepared statement to update the value of an existing key.
74
     */
75
    protected $statementKeyUpdate;
76
77
    /**
78
     * @var \Doctrine\DBAL\Driver\Statement
79
     * The prepared statement to increment the value of the given key.
80
     */
81
    protected $statementKeyIncr;
82
83
    /**
84
     * @var string
85
     * the sign to use to quote identifiers in queries
86
     */
87
    protected $quote;
88
89
    /**
90
     * Builds the prepared statements.
91
     */
92
    protected function buildStatements() {
93
        $quote                    = $this->quote;
94
        $queryBuilder             = $this->connection->createQueryBuilder()
95
            ->select('COUNT('.$quote.'key'.$quote.') AS amount')->from($quote.$this->table.$quote)->where(''.$quote.'key'.$quote.' = ?');
96
        $this->statementKeyExists = $this->connection->prepare($queryBuilder->getSQL());
97
98
        $queryBuilder             = $this->connection->createQueryBuilder()
99
            ->insert($quote.$this->table.$quote)->setValue($quote.'value'.$quote, '?')->setValue(''.$quote.'key'.$quote.'', '?');
100
        $this->statementKeyInsert = $this->connection->prepare($queryBuilder->getSQL());
101
102
        $queryBuilder             = $this->connection->createQueryBuilder()
103
            ->update($quote.$this->table.$quote)->set($quote.'value'.$quote, '?')->where($quote.'key'.$quote.' = ?');
104
        $this->statementKeyUpdate = $this->connection->prepare($queryBuilder->getSQL());
105
106
        $queryBuilder           = $this->connection->createQueryBuilder()
107
            ->update($quote.$this->table.$quote)->set($quote.'value'.$quote, $quote.'value'.$quote.' + 1')->where($quote.'key'.$quote.' = ?');
108
        $this->statementKeyIncr = $this->connection->prepare($queryBuilder->getSQL());
109
    }
110
111
    /**
112
     * DBAL constructor.
113
     *
114
     * @param Connection $connection
115
     * the DBAL connection
116
     * @param string $table
117
     * the table to use
118
     */
119
    public function __construct(Connection $connection, $table = 'phpprom') {
120
        parent::__construct();
121
        $this->connection = $connection;
122
        $this->table      = $table;
123
        $this->quote      = $connection->getDatabasePlatform()->getIdentifierQuoteCharacter();
124
        $this->buildStatements();
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    public function storeMeasurement($metric, $key, $value) {
131
        $prefixedKey = $metric.':'.$key;
132
        $this->statementKeyExists->bindValue(1, $prefixedKey);
133
        $this->statementKeyExists->execute();
134
        $exists         = $this->statementKeyExists->fetch(\PDO::FETCH_ASSOC);
135
        $statementStore = $exists['amount'] > 0 ? $this->statementKeyUpdate : $this->statementKeyInsert;
136
        $statementStore->bindValue(1, $value);
137
        $statementStore->bindValue(2, $prefixedKey);
138
        $statementStore->execute();
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144
    public function incrementMeasurement($metric, $key) {
145
        $prefixedKey = $metric.':'.$key;
146
        $this->statementKeyExists->bindValue(1, $prefixedKey);
147
        $this->statementKeyExists->execute();
148
        $exists             = $this->statementKeyExists->fetch(\PDO::FETCH_ASSOC);
149
        $statementIncrement = $exists['amount'] > 0 ? $this->statementKeyIncr : $this->statementKeyInsert;
150
        if ($exists['amount'] > 0) {
151
            $statementIncrement->bindValue(1, $prefixedKey);
152
        } else {
153
            $statementIncrement->bindValue(1, 1);
154
            $statementIncrement->bindValue(2, $prefixedKey);
155
        }
156
        $statementIncrement->execute();
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162
    public function getMeasurements($metric, array $keys, $defaultValue = 'Nan') {
163
        $prefixedKeys = array_map(function($key) use ($metric) {
164
            return $metric.':'.$key;
165
        }, $keys);
166
        $quote        = $this->quote;
167
        $queryBuilder = $this->connection->createQueryBuilder();
168
        $queryBuilder
169
            ->select($quote.'key'.$quote, $quote.'value'.$quote)
170
            ->from($quote.$this->table.$quote)
171
            ->where($quote.'key'.$quote.' IN (?)')
172
            ->setParameter(1, $prefixedKeys, Connection::PARAM_STR_ARRAY)
173
        ;
174
        $measurements = [];
175
        foreach ($keys as $key) {
176
            $measurements[$key] = $defaultValue;
177
        }
178
        $rows = $queryBuilder->execute()->fetchAll(\PDO::FETCH_ASSOC);
179
        foreach ($rows as $row) {
180
            $unprefixedKey                = substr($row['key'], strlen($metric) + 1);
181
            $measurements[$unprefixedKey] = (float)$row['value'];
182
        }
183
        return $measurements;
184
    }
185
}
186