Completed
Push — master ( 6eb58a...ff20ae )
by Jan-Petter
03:32
created

Client::sleep()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 3
eloc 9
nc 3
nop 0
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\SQLException;
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 SQLException
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
     * @return bool
89
     */
90
    public function reset()
91
    {
92
        $query = $this->pdo->prepare(<<<SQL
93
DELETE FROM robotstxt__delay0
94
WHERE base = :base AND userAgent = :useragent;
95
SQL
96
        );
97
        $query->bindParam(':base', $this->base, PDO::PARAM_INT);
98
        $query->bindParam(':useragent', $this->userAgent, PDO::PARAM_INT);
99
        return $query->execute();
100
    }
101
102
    /**
103
     * Sleep
104
     *
105
     * @return float|int
106
     */
107
    public function sleep()
108
    {
109
        $start = microtime(true);
110
        $until = $this->getTimeSleepUntil();
111
        if (microtime(true) > $until) {
112
            return 0;
113
        }
114
        try {
115
            time_sleep_until($until);
116
        } catch (\Exception $warning) {
117
            // Timestamp already in the past
118
        }
119
        return microtime(true) - $start;
120
    }
121
122
    /**
123
     * Timestamp with milliseconds
124
     *
125
     * @return float|int
126
     * @throws SQLException
127
     */
128
    public function getTimeSleepUntil()
129
    {
130
        if ($this->delay == 0) {
131
            return 0;
132
        }
133
        $query = $this->pdo->prepare(<<<SQL
134
SELECT
135
  delayUntil,
136
  UNIX_TIMESTAMP()
137
FROM robotstxt__delay0
138
WHERE base = :base AND userAgent = :userAgent;
139
SQL
140
        );
141
        $query->bindParam(':base', $this->base, PDO::PARAM_STR);
142
        $query->bindParam(':userAgent', $this->userAgent, PDO::PARAM_STR);
143
        $query->execute();
144
        $this->increment();
145
        if ($query->rowCount() > 0) {
146
            $row = $query->fetch(PDO::FETCH_ASSOC);
147
            if (abs(time() - $row['UNIX_TIMESTAMP()']) > 10) {
148
                throw new SQLException('`PHP server` and `SQL server` timestamps are out of sync. Please fix!');
149
            }
150
            return $row['delayUntil'] / 1000000;
151
        }
152
        return 0;
153
    }
154
155
    /**
156
     * Set new delayUntil timestamp
157
     *
158
     * @return bool
159
     */
160
    private function increment()
161
    {
162
        $query = $this->pdo->prepare(<<<SQL
163
INSERT INTO robotstxt__delay0 (base, userAgent, delayUntil, lastDelay)
164
VALUES (:base, :userAgent, (UNIX_TIMESTAMP(CURTIME(6)) + :delay) * 1000000, :delay * 1000000)
165
ON DUPLICATE KEY UPDATE
166
  delayUntil = GREATEST((UNIX_TIMESTAMP(CURTIME(6)) + :delay) * 1000000, delayUntil + (:delay * 1000000)),
167
  lastDelay = :delay * 1000000;
168
SQL
169
        );
170
        $query->bindParam(':base', $this->base, PDO::PARAM_STR);
171
        $query->bindParam(':userAgent', $this->userAgent, PDO::PARAM_STR);
172
        $query->bindParam(':delay', $this->delay, is_int($this->delay) ? PDO::PARAM_INT : PDO::PARAM_STR);
173
        return $query->execute();
174
    }
175
}
176