DbDriver   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 2
dl 0
loc 172
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A set_time_provider() 0 4 1
A get_time() 0 5 1
A get_lock() 0 21 3
A garbage_collect() 0 15 2
A release_lock() 0 8 1
A set_logger() 0 4 1
A log() 0 6 2
A build_lock_object() 0 14 1
A list_locks() 0 13 3
1
<?php
2
/**
3
 * Database driver
4
 *
5
 * @author    Matthias Gisder <[email protected]>
6
 * @copyright 2014 inGenerator Ltd
7
 * @licence   BSD
8
 */
9
10
namespace Ingenerator\RunSingle;
11
12
use \Ingenerator\RunSingle\PdoDatabaseObject;
13
use \Psr\Log\LoggerInterface;
14
15
class DbDriver implements LockDriver
16
{
17
18
    const DATE_FORMAT = 'd/m/Y H:i:s';
19
    /**
20
     * @var \Ingenerator\RunSingle\PdoDatabaseObject
21
     */
22
    protected $db_object;
23
24
    /**
25
     * @var callable
26
     */
27
    protected $timeProvider = 'time';
28
29
    /**
30
     * @var \Psr\Log\LoggerInterface
31
     */
32
    protected $logger;
33
34
    /**
35
     * @param \Ingenerator\RunSingle\PdoDatabaseObject $db_object
36
     */
37
    public function __construct(PdoDatabaseObject $db_object)
38
    {
39
        $this->db_object = $db_object;
40
    }
41
42
    /**
43
     * @param callable $provider
44
     */
45
    public function set_time_provider($provider)
46
    {
47
        $this->timeProvider = $provider;
48
    }
49
50
    /**
51
     * @return int
52
     */
53
    protected function get_time()
54
    {
55
        $time = \call_user_func($this->timeProvider);
56
        return $time;
57
    }
58
59
    /**
60
     * @param  string $task_name
61
     * @param  int    $timeout
62
     * @param string  $lock_holder
63
     *
64
     * @return false|integer
65
     * @throws \Exception
66
     * @throws \PDOException
67
     */
68
    public function get_lock($task_name, $timeout, $lock_holder)
69
    {
70
        $timestamp = $this->get_time();
71
72
        try {
73
            $this->db_object->execute('INSERT INTO '.$this->db_object->get_db_table_name()." VALUES(:task_name, :timestamp, :timeout, :lock_holder)", array(
74
                ':task_name'   => $task_name,
75
                ':timestamp'   => $timestamp,
76
                ':timeout'     => $timeout,
77
                ':lock_holder' => $lock_holder,
78
            ));
79
        } catch (\PDOException $e) {
80
            if (\substr($e->getMessage(), 0, 69) === 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry') {
81
                return FALSE;
82
            } else {
83
                throw $e;
84
            }
85
        }
86
87
        return $timestamp;
88
    }
89
90
    /**
91
     * @param string $task_name
92
     *
93
     * @return void
94
     */
95
    public function garbage_collect($task_name)
96
    {
97
        $result = $this->db_object->fetch_all('SELECT * FROM '.$this->db_object->get_db_table_name().' WHERE task_name = :task_name AND (lock_timestamp + timeout) < :current_timestamp', array(
98
            ':task_name'         => $task_name,
99
            ':current_timestamp' => $this->get_time()
100
        ));
101
        if (!$result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
102
            $this->log('debug', 'no stale locks found for task '.$task_name);
103
            return;
104
        }
105
106
        $lock = $this->build_lock_object($result[0]);
107
        $this->log('warning', \sprintf('stale locks found for lock:'.PHP_EOL.'    %s', $lock));
108
        $this->release_lock($result[0]['task_name'], $result[0]['lock_timestamp']);
109
    }
110
111
    /**
112
     * @param string $task_name
113
     * @param int    $lock_timestamp
114
     *
115
     * @return void
116
     */
117
    public function release_lock($task_name, $lock_timestamp)
118
    {
119
        $this->log('debug', 'releasing lock for task '.$task_name);
120
        $this->db_object->execute('DELETE FROM '.$this->db_object->get_db_table_name().' WHERE task_name = :task_name AND lock_timestamp = :lock_timestamp', array(
121
            ':task_name'      => $task_name,
122
            ':lock_timestamp' => $lock_timestamp
123
        ));
124
    }
125
126
    /**
127
     * @param LoggerInterface $logger
128
     */
129
    public function set_logger(LoggerInterface $logger)
130
    {
131
        $this->logger = $logger;
132
    }
133
134
    /**
135
     * Log only if logger is set.
136
     *
137
     * @param $level
138
     * @param $message
139
     */
140
    protected function log($level, $message)
141
    {
142
        if ($this->logger) {
143
            \call_user_func(array($this->logger, $level), $message);
144
        }
145
    }
146
147
    /**
148
     * @param array $result
149
     *
150
     * @return Lock
151
     */
152
    protected function build_lock_object($result)
153
    {
154
        $data = array(
155
            'task_name' => $result['task_name'],
156
            'lock_id' => $result['lock_timestamp'],
157
            'timeout' => $result['timeout'],
158
            'lock_holder' => $result['lock_holder'],
159
            'expires' => new \DateTime('@'.($result['lock_timestamp'] + $result['timeout'])),
160
            'locked_at' => new \DateTime('@'.($result['lock_timestamp'])),
161
        );
162
        $lock_obj = new Lock($data);
163
164
        return $lock_obj;
165
    }
166
167
    /**
168
     * Return a list of locks.
169
     *
170
     * @return array
171
     */
172
    public function list_locks()
173
    {
174
        $result = $this->db_object->fetch_all('SELECT * FROM '.$this->db_object->get_db_table_name(), array());
175
176
        $locks = array();
177
        if ($result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
178
            foreach ($result as $result_item) {
179
                $locks[] = $this->build_lock_object($result_item);
180
            }
181
        }
182
183
        return $locks;
184
    }
185
186
}
187