Completed
Push — master ( 23ae93...9ef007 )
by Jan-Petter
02:52
created

Client::reset()   B

Complexity

Conditions 3
Paths 2

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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