From a146ab686f9b55593fc7ed680876247280a0fa94 Mon Sep 17 00:00:00 2001 From: Michal Stefanak Date: Sat, 13 Jul 2024 12:22:00 +0200 Subject: [PATCH 1/2] added mixpanel analytics --- README.md | 6 +++++ composer.json | 1 + src/Bolt.php | 47 ++++++++++++++++++++++++++++++++++++++ src/helpers/FileCache.php | 27 +++++++++++++--------- src/protocol/AProtocol.php | 13 ++++++++++- 5 files changed, 82 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 7e6da67..967461a 100644 --- a/README.md +++ b/README.md @@ -255,6 +255,12 @@ environments for security reasons._ Server state is not reported by server but it is evaluated by received response. You can access current state through property `$protocol->serverState`. This property is updated with every call `getResponse(s)`. +## :bar_chart: Analytics + +Bolt does collect anonymous analytics data. These data are stored offline (as files in temp directory) and submitted once a day. You can opt out with environment variable `BOLT_ANALYTICS_OPTOUT`. + +Analytics data are public and available at [Mixpanel](https://eu.mixpanel.com/p/7ttVKqvjdqJtGCjLCFgdeC). + ## :pushpin: More solutions If you need simple class to cover basic functionality you can diff --git a/composer.json b/composer.json index 6940565..f4a7f02 100644 --- a/composer.json +++ b/composer.json @@ -10,6 +10,7 @@ "require": { "php": "^8.1", "ext-mbstring": "*", + "ext-curl": "*", "psr/simple-cache": "^3.0" }, "require-dev": { diff --git a/src/Bolt.php b/src/Bolt.php index 48f1c3d..2e3c82e 100644 --- a/src/Bolt.php +++ b/src/Bolt.php @@ -26,9 +26,56 @@ final class Bolt public function __construct(private IConnection $connection) { + if (!file_exists(getcwd() . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR)) { + mkdir(getcwd() . DIRECTORY_SEPARATOR . 'temp'); + } + if (!getenv('BOLT_ANALYTICS_OPTOUT')) { + $this->track(); + } $this->setProtocolVersions(5.4, 5, 4.4); } + private function track(): void + { + foreach (glob(getcwd() . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . 'queries.*.cnt') as $file) { + $time = intval(explode('.', basename($file))[1]); + if ($time < strtotime('today')) { + $count = file_get_contents($file); + unlink($file); + + $curl = curl_init(); + curl_setopt_array($curl, [ + CURLOPT_URL => 'https://api-eu.mixpanel.com/import?strict=0&project_id=3355308', + CURLOPT_RETURNTRANSFER => true, + CURLOPT_ENCODING => '', + CURLOPT_MAXREDIRS => 10, + CURLOPT_TIMEOUT => $this->connection->getTimeout(), + CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, + CURLOPT_CUSTOMREQUEST => 'POST', + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_POSTFIELDS => json_encode([ + [ + 'properties' => [ + '$insert_id' => $file, + 'distinct_id' => sha1(implode('', [php_uname(), disk_total_space('.'), filectime('/'), phpversion()])), + 'amount' => $count, + 'time' => $time + ], + 'event' => 'queries' + ] + ]), + CURLOPT_HTTPHEADER => [ + 'Content-Type: application/json', + 'accept: application/json', + 'authorization: Basic MDJhYjRiOWE2YTM4MThmNWFlZDEzYjNiMmE5M2MxNzQ6', + ], + ]); + curl_exec($curl); + curl_close($curl); + } + } + } + /** * Connect via Connection, execute handshake on it, create and return protocol version class * @throws BoltException diff --git a/src/helpers/FileCache.php b/src/helpers/FileCache.php index 57b77f3..5cfcb54 100644 --- a/src/helpers/FileCache.php +++ b/src/helpers/FileCache.php @@ -13,17 +13,22 @@ */ class FileCache implements CacheInterface { + private string $tempDir; + public function __construct() { - if (!file_exists(__DIR__ . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR)) - mkdir(__DIR__ . DIRECTORY_SEPARATOR . 'temp'); + $this->tempDir = getcwd() . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR; + + if (!file_exists($this->tempDir)) { + mkdir(rtrim($this->tempDir, DIRECTORY_SEPARATOR)); + } // clean old - foreach (scandir(__DIR__ . DIRECTORY_SEPARATOR . 'temp') as $file) { + foreach (scandir($this->tempDir . 'temp') as $file) { if ($file == '.' || $file == '..') continue; - if (filemtime(__DIR__ . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $file) < strtotime('-1 hour')) - unlink(__DIR__ . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $file); + if (filemtime($this->tempDir . $file) < strtotime('-1 hour')) + unlink($this->tempDir . $file); } } @@ -37,7 +42,7 @@ public function __construct() */ public function get(string $key, mixed $default = null): mixed { - return $this->has($key) ? file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $key) : $default; + return $this->has($key) ? file_get_contents($this->tempDir . $key) : $default; } /** @@ -53,7 +58,7 @@ public function get(string $key, mixed $default = null): mixed */ public function set(string $key, mixed $value, \DateInterval|int|null $ttl = null): bool { - return is_int(file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $key, $value)); + return is_int(file_put_contents($this->tempDir . $key, $value)); } /** @@ -65,7 +70,7 @@ public function set(string $key, mixed $value, \DateInterval|int|null $ttl = nul */ public function delete(string $key): bool { - return $this->has($key) && unlink(__DIR__ . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $key); + return $this->has($key) && unlink($this->tempDir . $key); } /** @@ -73,10 +78,10 @@ public function delete(string $key): bool */ public function clear(): bool { - foreach (scandir(__DIR__ . DIRECTORY_SEPARATOR . 'temp') as $file) { + foreach (scandir(rtrim($this->tempDir, DIRECTORY_SEPARATOR)) as $file) { if ($file == '.' || $file == '..') continue; - unlink(__DIR__ . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $file); + unlink($this->tempDir . $file); } return true; } @@ -143,6 +148,6 @@ public function deleteMultiple(iterable $keys): bool */ public function has(string $key): bool { - return file_exists(__DIR__ . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . $key); + return file_exists($this->tempDir . $key); } } diff --git a/src/protocol/AProtocol.php b/src/protocol/AProtocol.php index bb37276..aca2660 100644 --- a/src/protocol/AProtocol.php +++ b/src/protocol/AProtocol.php @@ -63,8 +63,19 @@ public function __construct( */ protected function write(iterable $generator): void { - foreach ($generator as $buffer) + if (!getenv('BOLT_ANALYTICS_OPTOUT')) { + $this->track(); + } + foreach ($generator as $buffer) { $this->connection->write($buffer); + } + } + + private function track(): void + { + $file = getcwd() . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR . 'queries.' . strtotime('today') . '.cnt'; + $count = file_exists($file) ? intval(file_get_contents($file)) : 0; + file_put_contents($file, $count + 1); } /** From 1d27d7c3233cdcca81025c36db8a3a68ad004d9d Mon Sep 17 00:00:00 2001 From: Michal Stefanak Date: Sat, 13 Jul 2024 12:28:30 +0200 Subject: [PATCH 2/2] added directory create into protocol tests --- tests/protocol/ATest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/protocol/ATest.php b/tests/protocol/ATest.php index 1f371cf..a0c87af 100644 --- a/tests/protocol/ATest.php +++ b/tests/protocol/ATest.php @@ -100,6 +100,10 @@ public function readCallback(int $length = 2048): string */ protected function setUp(): void { + if (!file_exists(getcwd() . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR)) { + mkdir(getcwd() . DIRECTORY_SEPARATOR . 'temp'); + } + self::$readBuffer = ''; self::$readArray = []; self::$writeIndex = 0;