Completed
Push — master ( 13aa9d...17fd6c )
by Jan-Petter
03:10
created

Client::checkQueue()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 20
rs 9.4285
cc 3
eloc 13
nc 3
nop 0
1
<?php
2
/**
3
 * vipnytt/RobotsTxtParser
4
 *
5
 * @link https://github.com/VIPnytt/RobotsTxtParser
6
 * @license https://github.com/VIPnytt/RobotsTxtParser/blob/master/LICENSE The MIT License (MIT)
7
 */
8
9
namespace vipnytt\RobotsTxtParser\Client\Delay\MySQL;
10
11
use PDO;
12
use vipnytt\RobotsTxtParser\Client\Delay\ClientInterface;
13
use vipnytt\RobotsTxtParser\Exceptions\DatabaseException;
14
15
/**
16
 * Class Client
17
 *
18
 * @see https://github.com/VIPnytt/RobotsTxtParser/blob/master/docs/methods/DelayClient.md for documentation
19
 * @package vipnytt\RobotsTxtParser\Client\Delay\MySQL
20
 */
21
class Client implements ClientInterface
22
{
23
    /**
24
     * Database handler
25
     * @var PDO
26
     */
27
    private $pdo;
28
29
    /**
30
     * Base uri
31
     * @var string
32
     */
33
    private $base;
34
35
    /**
36
     * User-agent
37
     * @var string
38
     */
39
    private $userAgent;
40
41
    /**
42
     * Delay
43
     * @var float|int
44
     */
45
    private $delay;
46
47
    /**
48
     * Client constructor.
49
     *
50
     * @param PDO $pdo
51
     * @param string $baseUri
52
     * @param string $userAgent
53
     * @param float|int $delay
54
     * @throws DatabaseException
55
     */
56
    public function __construct(PDO $pdo, $baseUri, $userAgent, $delay)
57
    {
58
        $this->pdo = $pdo;
59
        $this->base = $baseUri;
60
        $this->userAgent = $userAgent;
61
        $this->delay = round($delay, 6, PHP_ROUND_HALF_UP);
62
    }
63
64
    /**
65
     * Queue
66
     *
67
     * @return float|int
68
     */
69
    public function checkQueue()
70
    {
71
        if ($this->delay == 0) {
72
            return 0;
73
        }
74
        $query = $this->pdo->prepare(<<<SQL
75
SELECT GREATEST(0, (delayUntil / 1000000) - UNIX_TIMESTAMP(CURTIME(6))) AS sec
76
FROM robotstxt__delay0
77
WHERE base = :base AND userAgent = :userAgent;
78
SQL
79
        );
80
        $query->bindParam(':base', $this->base, PDO::PARAM_STR);
81
        $query->bindParam(':userAgent', $this->userAgent, PDO::PARAM_STR);
82
        $query->execute();
83
        if ($query->rowCount() > 0) {
84
            $row = $query->fetch(PDO::FETCH_ASSOC);
85
            return $row['sec'];
86
        }
87
        return 0;
88
    }
89
90
    /**
91
     * Reset queue
92
     *
93
     * @param float|int|null $delay
94
     * @return bool
95
     */
96
    public function reset($delay = null)
97
    {
98
        if ($delay === null) {
99
            $query = $this->pdo->prepare(<<<SQL
100
DELETE FROM robotstxt__delay0
101
WHERE base = :base AND userAgent = :useragent;
102
SQL
103
            );
104
            $query->bindParam(':base', $this->base, PDO::PARAM_INT);
105
            $query->bindParam(':useragent', $this->userAgent, PDO::PARAM_STR);
106
            return $query->execute();
107
        }
108
        $query = $this->pdo->prepare(<<<SQL
109
INSERT INTO robotstxt__delay0 (base, userAgent, delayUntil, lastDelay)
110
VALUES (:base, :useragent, (UNIX_TIMESTAMP(CURTIME(6)) + :delay) * 1000000, :delay * 1000000)
111
ON DUPLICATE KEY UPDATE
112
  delayUntil = (UNIX_TIMESTAMP(CURTIME(6)) + :delay) * 1000000,
113
  lastDelay  = :delay * 1000000;
114
SQL
115
        );
116
        $query->bindParam(':base', $this->base, PDO::PARAM_INT);
117
        $query->bindParam(':useragent', $this->userAgent, PDO::PARAM_STR);
118
        $query->bindParam(':delay', $delay, PDO::PARAM_INT | PDO::PARAM_STR);
119
        return $query->execute();
120
    }
121
122
    /**
123
     * Sleep
124
     *
125
     * @return float|int
126
     */
127
    public function sleep()
128
    {
129
        $start = microtime(true);
130
        $until = $this->getTimeSleepUntil();
131
        if (microtime(true) > $until) {
132
            return 0;
133
        }
134
        try {
135
            time_sleep_until($until);
136
        } catch (\Exception $warning) {
137
            // Timestamp already in the past
138
        }
139
        return microtime(true) - $start;
140
    }
141
142
    /**
143
     * Timestamp with milliseconds
144
     *
145
     * @return float|int
146
     * @throws DatabaseException
147
     */
148
    public function getTimeSleepUntil()
149
    {
150
        if ($this->delay == 0) {
151
            return 0;
152
        }
153
        $query = $this->pdo->prepare(<<<SQL
154
SELECT
155
  delayUntil,
156
  UNIX_TIMESTAMP()
157
FROM robotstxt__delay0
158
WHERE base = :base AND userAgent = :userAgent;
159
SQL
160
        );
161
        $query->bindParam(':base', $this->base, PDO::PARAM_STR);
162
        $query->bindParam(':userAgent', $this->userAgent, PDO::PARAM_STR);
163
        $query->execute();
164
        $this->increment();
165
        if ($query->rowCount() > 0) {
166
            $row = $query->fetch(PDO::FETCH_ASSOC);
167
            if (abs(time() - $row['UNIX_TIMESTAMP()']) > 10) {
168
                throw new DatabaseException('`PHP server` and `SQL server` timestamps are out of sync. Please fix!');
169
            }
170
            return $row['delayUntil'] / 1000000;
171
        }
172
        return 0;
173
    }
174
175
    /**
176
     * Set new delayUntil timestamp
177
     *
178
     * @return bool
179
     */
180
    private function increment()
181
    {
182
        $query = $this->pdo->prepare(<<<SQL
183
INSERT INTO robotstxt__delay0 (base, userAgent, delayUntil, lastDelay)
184
VALUES (:base, :userAgent, (UNIX_TIMESTAMP(CURTIME(6)) + :delay) * 1000000, :delay * 1000000)
185
ON DUPLICATE KEY UPDATE
186
  delayUntil = GREATEST((UNIX_TIMESTAMP(CURTIME(6)) + :delay) * 1000000, delayUntil + (:delay * 1000000)),
187
  lastDelay = :delay * 1000000;
188
SQL
189
        );
190
        $query->bindParam(':base', $this->base, PDO::PARAM_STR);
191
        $query->bindParam(':userAgent', $this->userAgent, PDO::PARAM_STR);
192
        $query->bindParam(':delay', $this->delay, PDO::PARAM_INT | PDO::PARAM_STR);
193
        return $query->execute();
194
    }
195
}
196