From d8fd5f6ca13d7d8bb8c9a262b402c1384a69c1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Leutg=C3=B6b?= Date: Thu, 7 Sep 2017 13:59:16 +0200 Subject: [PATCH 001/151] Fix timestamp implementation for microseconds Following changes and fixed issues are included: - Line 134: Wrong php method reference - Line 162: The cast to int is redundant as seconds are already casted in date() call at line 165 - Line 163: Casting microseconds part to int strips out leading zeros, resulting in converting a timestamp from `1504784759.0124` to `1504784759.124` as an example. - Line 614: The date format string misses the dot as separator between seconds and microseconds, resulting in datetime object far in the future when parsing in `send.php` (File consumer) --- lib/Segment/Client.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Segment/Client.php b/lib/Segment/Client.php index c421bfe..99c3bee 100644 --- a/lib/Segment/Client.php +++ b/lib/Segment/Client.php @@ -131,7 +131,7 @@ public function flush() { * Formats a timestamp by making sure it is set * and converting it to iso8601. * - * The timestamp can be time in seconds `time()` or `microseconds(true)`. + * The timestamp can be time in seconds `time()` or `microtime(true)`. * any other input is considered an error and the method will return a new date. * * Note: php's date() "u" format (for microseconds) has a bug in it @@ -159,9 +159,9 @@ private function formatTime($ts) { if (!isset($parts[1])) return date("c", (int)$parts[0]); // microtime(true) - $sec = (int)$parts[0]; - $usec = (int)$parts[1]; - $fmt = sprintf("Y-m-d\TH:i:s%sP", $usec); + $sec = $parts[0]; + $usec = $parts[1]; + $fmt = sprintf("Y-m-d\TH:i:s.%sP", $usec); return date($fmt, (int)$sec); } From 24cee46a4c33f49abd433c73d2a58cc5d9baf35c Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Mon, 22 Jan 2018 18:32:04 +0100 Subject: [PATCH 002/151] fix curl error handler --- lib/Segment/Consumer/LibCurl.php | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/Segment/Consumer/LibCurl.php b/lib/Segment/Consumer/LibCurl.php index 6bded9a..0da33a6 100644 --- a/lib/Segment/Consumer/LibCurl.php +++ b/lib/Segment/Consumer/LibCurl.php @@ -78,24 +78,32 @@ public function flushBatch($messages) { curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // retry failed requests just once to diminish impact on performance - $httpResponse = $this->executePost($ch); + $responseContent = curl_exec($ch); + + $err = curl_error($ch); + if ($err) { + $this->handleError(curl_errno($ch), $err); + return; + } + + $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); //close connection curl_close($ch); $elapsed_time = microtime(true) - $start_time; - if (200 != $httpResponse) { + if (200 != $responseCode) { // log error - $this->handleError($ch, $httpResponse); + $this->handleError($responseCode, $responseContent); - if (($httpResponse >= 500 && $httpResponse <= 600) || 429 == $httpResponse) { + if (($responseCode >= 500 && $responseCode <= 600) || 429 == $responseCode) { // If status code is greater than 500 and less than 600, it indicates server error // Error code 429 indicates rate limited. // Retry uploading in these cases. usleep($backoff * 1000); $backoff *= 2; - } elseif ($httpResponse >= 400) { + } elseif ($responseCode >= 400) { break; } } else { @@ -103,13 +111,6 @@ public function flushBatch($messages) { } } - return $httpResponse; - } - - public function executePost($ch) { - curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - - return $httpCode; + return $responseCode; } } From 98380a965aed8c94598e0bf8db178a6e298547b0 Mon Sep 17 00:00:00 2001 From: remyfrd Date: Tue, 20 Nov 2018 10:25:23 +0100 Subject: [PATCH 003/151] File permission option for file consumer --- lib/Segment/Consumer/File.php | 6 +++++- test/ConsumerFileTest.php | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/Segment/Consumer/File.php b/lib/Segment/Consumer/File.php index d59d71c..a0d385e 100644 --- a/lib/Segment/Consumer/File.php +++ b/lib/Segment/Consumer/File.php @@ -20,7 +20,11 @@ public function __construct($secret, $options = array()) { try { $this->file_handle = fopen($options["filename"], "a"); - chmod($options["filename"], 0777); + if (isset($options["filepermissions"])) { + chmod($options["filename"], $options["filepermissions"]); + } else { + chmod($options["filename"], 0777); + } } catch (Exception $e) { $this->handleError($e->getCode(), $e->getMessage()); } diff --git a/test/ConsumerFileTest.php b/test/ConsumerFileTest.php index 510ffaf..82ab848 100644 --- a/test/ConsumerFileTest.php +++ b/test/ConsumerFileTest.php @@ -123,6 +123,31 @@ public function testProductionProblems() $this->assertFalse($tracked); } + public function testFileSecurityCustom() { + $client = new Segment_Client( + "testsecret", + array( + "consumer" => "file", + "filename" => $this->filename, + "filepermissions" => 0700 + ) + ); + $tracked = $client->track(array("userId" => "some_user", "event" => "File PHP Event")); + $this->assertEquals(0700, (fileperms($this->filename) & 0777)); + } + + public function testFileSecurityDefaults() { + $client = new Segment_Client( + "testsecret", + array( + "consumer" => "file", + "filename" => $this->filename + ) + ); + $tracked = $client->track(array("userId" => "some_user", "event" => "File PHP Event")); + $this->assertEquals(0777, (fileperms($this->filename) & 0777)); + } + public function checkWritten($type) { exec("wc -l " . $this->filename, $output); From 6dec6ea60a44800c4aa903abd1a754a9a8ff46d4 Mon Sep 17 00:00:00 2001 From: William Grosset Date: Thu, 28 Feb 2019 20:42:17 -0800 Subject: [PATCH 004/151] Update README --- README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/README.md b/README.md index 946db82..8a9bb62 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,48 @@ analytics-php analytics-php is a php client for [Segment](https://segment.com) +
+ +

You can't fix what you can't measure

+
+ +Analytics helps you measure your users, product, and business. It unlocks insights into your app's funnel, core business metrics, and whether you have product-market fit. + +## How to get started +1. **Collect analytics data** from your app(s). + - The top 200 Segment companies collect data from 5+ source types (web, mobile, server, CRM, etc.). +2. **Send the data to analytics tools** (for example, Google Analytics, Amplitude, Mixpanel). + - Over 250+ Segment companies send data to eight categories of destinations such as analytics tools, warehouses, email marketing and remarketing systems, session recording, and more. +3. **Explore your data** by creating metrics (for example, new signups, retention cohorts, and revenue generation). + - The best Segment companies use retention cohorts to measure product market fit. Netflix has 70% paid retention after 12 months, 30% after 7 years. + +[Segment](https://segment.com) collects analytics data and allows you to send it to more than 250 apps (such as Google Analytics, Mixpanel, Optimizely, Facebook Ads, Slack, Sentry) just by flipping a switch. You only need one Segment code snippet, and you can turn integrations on and off at will, with no additional code. [Sign up with Segment today](https://app.segment.com/signup). + +### Why? +1. **Power all your analytics apps with the same data**. Instead of writing code to integrate all of your tools individually, send data to Segment, once. + +2. **Install tracking for the last time**. We're the last integration you'll ever need to write. You only need to instrument Segment once. Reduce all of your tracking code and advertising tags into a single set of API calls. + +3. **Send data from anywhere**. Send Segment data from any device, and we'll transform and send it on to any tool. + +4. **Query your data in SQL**. Slice, dice, and analyze your data in detail with Segment SQL. We'll transform and load your customer behavioral data directly from your apps into Amazon Redshift, Google BigQuery, or Postgres. Save weeks of engineering time by not having to invent your own data warehouse and ETL pipeline. + + For example, you can capture data on any app: + ```js + analytics.track('Order Completed', { price: 99.84 }) + ``` + Then, query the resulting data in SQL: + ```sql + select * from app.order_completed + order by price desc + ``` + +### 🚀 Startup Program +
+ +
+If you are part of a new startup (<$5M raised, <2 years since founding), we just launched a new startup program for you. You can get a Segment Team plan (up to $25,000 value in Segment credits) for free up to 2 years — apply here! + ## Documentation Documentation is available at [segment.com/docs/sources/server/php](https://segment.com/docs/sources/server/php/) From 0ae03d40566a903b9575096029e52c52b069bba5 Mon Sep 17 00:00:00 2001 From: Robert Baelde Date: Mon, 14 Oct 2019 13:43:56 +0200 Subject: [PATCH 005/151] Add possibility to use custom consumer --- .gitignore | 1 + lib/Segment/Client.php | 32 +++++++++++++++++++++++++------- test/ClientTest.php | 31 +++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 test/ClientTest.php diff --git a/.gitignore b/.gitignore index de6e5ad..373b920 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ composer.phar test/analytics.log /.vscode .phplint-cache +/.idea diff --git a/lib/Segment/Client.php b/lib/Segment/Client.php index 9ac6be5..422a9db 100644 --- a/lib/Segment/Client.php +++ b/lib/Segment/Client.php @@ -11,6 +11,13 @@ class Segment_Client { protected $consumer; + private const DEFAULT_CONSUMERS = array( + "socket" => "Segment_Consumer_Socket", + "file" => "Segment_Consumer_File", + "fork_curl" => "Segment_Consumer_ForkCurl", + "lib_curl" => "Segment_Consumer_LibCurl" + ); + /** * Create a new analytics object with your app's secret * key @@ -21,18 +28,21 @@ class Segment_Client { * */ public function __construct($secret, $options = array()) { - $consumers = array( - "socket" => "Segment_Consumer_Socket", - "file" => "Segment_Consumer_File", - "fork_curl" => "Segment_Consumer_ForkCurl", - "lib_curl" => "Segment_Consumer_LibCurl" - ); // Use our socket libcurl by default $consumer_type = isset($options["consumer"]) ? $options["consumer"] : "lib_curl"; - $Consumer = $consumers[$consumer_type]; + if (!array_key_exists($consumer_type, self::DEFAULT_CONSUMERS) && class_exists($consumer_type)) { + if (!is_subclass_of($consumer_type, Segment_Consumer::class)) { + throw new Exception('Consumers must extend the Segment_Consumer abstract class'); + } + // Try to resolve it by class name + $this->consumer = new $consumer_type($secret, $options); + return; + } + + $Consumer = self::DEFAULT_CONSUMERS[$consumer_type]; $this->consumer = new $Consumer($secret, $options); } @@ -131,6 +141,14 @@ public function flush() { return true; } + /** + * @return Segment_Consumer + */ + public function getConsumer() { + return $this->consumer; + } + + /** * Formats a timestamp by making sure it is set * and converting it to iso8601. diff --git a/test/ClientTest.php b/test/ClientTest.php new file mode 100644 index 0000000..754f4c5 --- /dev/null +++ b/test/ClientTest.php @@ -0,0 +1,31 @@ +assertInstanceOf(Segment_Consumer_LibCurl::class, $client->getConsumer()); + } + + /** @test */ + public function can_provide_the_consumer_configuration_as_string() + { + $client = new Segment_Client('foobar', [ + 'consumer' => 'fork_curl', + ]); + $this->assertInstanceOf(Segment_Consumer_ForkCurl::class, $client->getConsumer()); + } + + /** @test */ + public function can_provide_a_class_namespace_as_consumer_configuration() + { + $client = new Segment_Client('foobar', [ + 'consumer' => Segment_Consumer_ForkCurl::class, + ]); + $this->assertInstanceOf(Segment_Consumer_ForkCurl::class, $client->getConsumer()); + } +} From 3dfd25553bf429dbcd67492cf7e250cd6a0cf01e Mon Sep 17 00:00:00 2001 From: Robert Baelde Date: Mon, 14 Oct 2019 13:55:20 +0200 Subject: [PATCH 006/151] fix build for older php versions --- lib/Segment/Client.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/Segment/Client.php b/lib/Segment/Client.php index 422a9db..6b3e931 100644 --- a/lib/Segment/Client.php +++ b/lib/Segment/Client.php @@ -11,13 +11,6 @@ class Segment_Client { protected $consumer; - private const DEFAULT_CONSUMERS = array( - "socket" => "Segment_Consumer_Socket", - "file" => "Segment_Consumer_File", - "fork_curl" => "Segment_Consumer_ForkCurl", - "lib_curl" => "Segment_Consumer_LibCurl" - ); - /** * Create a new analytics object with your app's secret * key @@ -29,11 +22,17 @@ class Segment_Client { */ public function __construct($secret, $options = array()) { + $consumers = array( + "socket" => "Segment_Consumer_Socket", + "file" => "Segment_Consumer_File", + "fork_curl" => "Segment_Consumer_ForkCurl", + "lib_curl" => "Segment_Consumer_LibCurl" + ); // Use our socket libcurl by default $consumer_type = isset($options["consumer"]) ? $options["consumer"] : "lib_curl"; - if (!array_key_exists($consumer_type, self::DEFAULT_CONSUMERS) && class_exists($consumer_type)) { + if (!array_key_exists($consumer_type, $consumers) && class_exists($consumer_type)) { if (!is_subclass_of($consumer_type, Segment_Consumer::class)) { throw new Exception('Consumers must extend the Segment_Consumer abstract class'); } @@ -42,7 +41,7 @@ public function __construct($secret, $options = array()) { return; } - $Consumer = self::DEFAULT_CONSUMERS[$consumer_type]; + $Consumer = $consumers[$consumer_type]; $this->consumer = new $Consumer($secret, $options); } From fd87865999b3d5d6ac39eb481a0de1f610905177 Mon Sep 17 00:00:00 2001 From: Maciej Miszczyk Date: Thu, 23 Jul 2020 19:55:55 +0200 Subject: [PATCH 007/151] Escape all input arguments that end up in shell --- lib/Segment/Consumer/ForkCurl.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Segment/Consumer/ForkCurl.php b/lib/Segment/Consumer/ForkCurl.php index 8251570..6f73a00 100644 --- a/lib/Segment/Consumer/ForkCurl.php +++ b/lib/Segment/Consumer/ForkCurl.php @@ -33,7 +33,7 @@ public function flushBatch($messages) { // Escape for shell usage. $payload = escapeshellarg($payload); - $secret = $this->secret; + $secret = escapeshellarg($this->secret); $protocol = $this->ssl() ? "https://" : "http://"; if ($this->host) { From 13ce2a8792dc4ac8858a5c9c3b244cc4cf4562e5 Mon Sep 17 00:00:00 2001 From: Kelly Lu Date: Tue, 11 Aug 2020 16:22:48 -0700 Subject: [PATCH 008/151] Add .circleci/config.yml --- .circleci/config.yml | 58 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..ee76f23 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,58 @@ +# PHP CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-php/ for more details +# +version: 2 +jobs: + build: + docker: + # Specify the version you desire here + - image: circleci/php:7.1-node-browsers + + # Specify service dependencies here if necessary + # CircleCI maintains a library of pre-built images + # documented at https://circleci.com/docs/2.0/circleci-images/ + # Using the RAM variation mitigates I/O contention + # for database intensive operations. + # - image: circleci/mysql:5.7-ram + # + # - image: redis:2.8.19 + + steps: + - checkout + + - run: sudo apt update # PHP CircleCI 2.0 Configuration File# PHP CircleCI 2.0 Configuration File sudo apt install zlib1g-dev libsqlite3-dev + - run: sudo docker-php-ext-install zip + + # Download and cache dependencies + - restore_cache: + keys: + # "composer.lock" can be used if it is committed to the repo + - v1-dependencies-{{ checksum "composer.json" }} + # fallback to using the latest cache if no exact match is found + - v1-dependencies- + + - run: composer install -n --prefer-dist + + - save_cache: + key: v1-dependencies-{{ checksum "composer.json" }} + paths: + - ./vendor + - restore_cache: + keys: + - node-v1-{{ checksum "package.json" }} + - node-v1- + - run: yarn install + - save_cache: + key: node-v1-{{ checksum "package.json" }} + paths: + - node_modules + + # prepare the database + - run: touch storage/testing.sqlite + - run: php artisan migrate --env=testing --database=sqlite_testing --force + + # run tests with phpunit or codecept + #- run: ./vendor/bin/phpunit + - run: ./vendor/bin/codecept build + - run: ./vendor/bin/codecept run \ No newline at end of file From a70562c7b65e48ba3f8cb2b883a3bf6eb36bf8ad Mon Sep 17 00:00:00 2001 From: Kelly Lu Date: Thu, 10 Sep 2020 15:14:29 -0700 Subject: [PATCH 009/151] Fixed restore/save cache issue --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ee76f23..3eb080f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,11 +40,11 @@ jobs: - ./vendor - restore_cache: keys: - - node-v1-{{ checksum "package.json" }} + - node-v1-{{ checksum "composer.json" }} - node-v1- - run: yarn install - save_cache: - key: node-v1-{{ checksum "package.json" }} + key: node-v1-{{ checksum "composer.json" }} paths: - node_modules From 8984716b4665560f88950a1d2585771b59b176f6 Mon Sep 17 00:00:00 2001 From: Kelly Lu Date: Thu, 10 Sep 2020 15:23:44 -0700 Subject: [PATCH 010/151] Added sqlite to config --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3eb080f..60e5db7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,6 +22,7 @@ jobs: - checkout - run: sudo apt update # PHP CircleCI 2.0 Configuration File# PHP CircleCI 2.0 Configuration File sudo apt install zlib1g-dev libsqlite3-dev + - run: sudo apt install zlib1g-dev libsqlite3-dev - run: sudo docker-php-ext-install zip # Download and cache dependencies From 1d0feffe32c8a8a526dd9b530a657a96588cce07 Mon Sep 17 00:00:00 2001 From: Kelly Lu Date: Fri, 11 Sep 2020 11:33:52 -0700 Subject: [PATCH 011/151] Updated CircleCI config --- .circleci/config.yml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 60e5db7..c32a774 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -49,11 +49,19 @@ jobs: paths: - node_modules - # prepare the database - - run: touch storage/testing.sqlite - - run: php artisan migrate --env=testing --database=sqlite_testing --force - # run tests with phpunit or codecept + #- run: 'if [[ ! ${TRAVIS_PHP_VERSION:0:3} < "5.5" ]]; then composer require overtrue/phplint; composer require squizlabs/php_codesniffer; ./vendor/bin/phplint; ./vendor/bin/phpcs; fi' + #- run: 'phpunit' + - run: + name: 'Running unit tests' + command: './vendor/bin/phpunit test' + - run: + name: 'Running E2E tests' + command: '.buildscript/e2e.sh' + - run: + name: 'Code coverage' + command: 'bash <(curl -s https://codecov.io/bash)' + #- run: ./vendor/bin/phpunit - - run: ./vendor/bin/codecept build - - run: ./vendor/bin/codecept run \ No newline at end of file + #- run: ./vendor/bin/codecept build + #- run: ./vendor/bin/codecept run \ No newline at end of file From e1e2cbb2915583e375d55ba6be971988dca877d3 Mon Sep 17 00:00:00 2001 From: Kelly Lu Date: Fri, 11 Sep 2020 11:42:36 -0700 Subject: [PATCH 012/151] Code coverage after success --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index c32a774..4918d10 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -59,6 +59,7 @@ jobs: name: 'Running E2E tests' command: '.buildscript/e2e.sh' - run: + when: on_success name: 'Code coverage' command: 'bash <(curl -s https://codecov.io/bash)' From c7210fb08f8a8798ef31396e2a8b79f4546cadfb Mon Sep 17 00:00:00 2001 From: Kelly Lu Date: Fri, 11 Sep 2020 11:45:57 -0700 Subject: [PATCH 013/151] Removed TravisCI config --- .travis.yml | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index dd6bc10..0000000 --- a/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -language: php -dist: precise -php: - - 5.6 - - 5.5 - - 5.4 - - 5.3 - - hhvm - -matrix: - allow_failures: - - php: hhvm - -script: - - if [[ ! ${TRAVIS_PHP_VERSION:0:3} < "5.5" ]]; then composer require overtrue/phplint; composer require squizlabs/php_codesniffer; ./vendor/bin/phplint; ./vendor/bin/phpcs; fi - - phpunit - - .buildscript/e2e.sh - -after_success: - - bash <(curl -s https://codecov.io/bash) From 60da0f1890d265c774f07d23aabd3a287799d86a Mon Sep 17 00:00:00 2001 From: Kelly Lu Date: Wed, 30 Sep 2020 16:13:03 -0700 Subject: [PATCH 014/151] Added CODEOWNERS --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..ea28cc7 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Global users +@lubird @bsneed @pooyaj \ No newline at end of file From 8ae52e0ae88b309a0393ef5a8322a8305fc7d294 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Wed, 7 Oct 2020 11:09:04 -0500 Subject: [PATCH 015/151] Update circleci config to be more modular --- .circleci/config.yml | 81 ++++++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4918d10..973f377 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,33 +4,21 @@ # version: 2 jobs: - build: - docker: - # Specify the version you desire here - - image: circleci/php:7.1-node-browsers - # Specify service dependencies here if necessary - # CircleCI maintains a library of pre-built images - # documented at https://circleci.com/docs/2.0/circleci-images/ - # Using the RAM variation mitigates I/O contention - # for database intensive operations. - # - image: circleci/mysql:5.7-ram - # - # - image: redis:2.8.19 + multi-test: &multi-test + docker: + - image: node - steps: + steps: - checkout - - run: sudo apt update # PHP CircleCI 2.0 Configuration File# PHP CircleCI 2.0 Configuration File sudo apt install zlib1g-dev libsqlite3-dev - - run: sudo apt install zlib1g-dev libsqlite3-dev + - run: sudo apt update + - run: sudo apt install zlib1g-dev - run: sudo docker-php-ext-install zip - # Download and cache dependencies - restore_cache: keys: - # "composer.lock" can be used if it is committed to the repo - v1-dependencies-{{ checksum "composer.json" }} - # fallback to using the latest cache if no exact match is found - v1-dependencies- - run: composer install -n --prefer-dist @@ -49,20 +37,55 @@ jobs: paths: - node_modules - # run tests with phpunit or codecept - #- run: 'if [[ ! ${TRAVIS_PHP_VERSION:0:3} < "5.5" ]]; then composer require overtrue/phplint; composer require squizlabs/php_codesniffer; ./vendor/bin/phplint; ./vendor/bin/phpcs; fi' - #- run: 'phpunit' - - run: - name: 'Running unit tests' - command: './vendor/bin/phpunit test' - - run: + -run: + - e2e + - codecov + + + e2e: + steps: + - run: name: 'Running E2E tests' command: '.buildscript/e2e.sh' - - run: + codecov: + steps: + - run: when: on_success name: 'Code coverage' command: 'bash <(curl -s https://codecov.io/bash)' - #- run: ./vendor/bin/phpunit - #- run: ./vendor/bin/codecept build - #- run: ./vendor/bin/codecept run \ No newline at end of file + test-php5: + <<: *multi-test + docker: + - image: circleci/php:5-node-browsers + + test-php5.6: + <<: *multi-test + docker: + - image: circleci/php:5.6-node-browsers + + test-php7.1: + <<: *multi-test + docker: + - image: circleci/php:7.1-node-browsers + + test-php7.3: + <<: *multi-test + docker: + - image: circleci/php:7.3-node-browsers + + test-php7.4: + <<: *multi-test + docker: + - image: circleci/php:7.4-node-browsers + + +workflows: + version: 2 + multi-test: + jobs: + - test-php5 + - test-php5.6 + - test-php7.1 + - test-php7.3 + - test-php7.4 From b75c6971651ff8a037d6b09d3118acc24486d659 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Wed, 7 Oct 2020 12:22:55 -0500 Subject: [PATCH 016/151] Fix formatting and syntax errors after re-write --- .circleci/config.yml | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 973f377..26df9e4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,15 +4,12 @@ # version: 2 jobs: - multi-test: &multi-test - docker: - - image: node - - steps: + docker: + - image: php + steps: - checkout - - - run: sudo apt update + - run: sudo apt update - run: sudo apt install zlib1g-dev - run: sudo docker-php-ext-install zip @@ -36,12 +33,10 @@ jobs: key: node-v1-{{ checksum "composer.json" }} paths: - node_modules - - -run: + - run: - e2e - codecov - e2e: steps: - run: @@ -77,9 +72,7 @@ jobs: test-php7.4: <<: *multi-test docker: - - image: circleci/php:7.4-node-browsers - - + - image: circleci/php:7.4-node-browsers workflows: version: 2 multi-test: @@ -88,4 +81,4 @@ workflows: - test-php5.6 - test-php7.1 - test-php7.3 - - test-php7.4 + - test-php7.4 \ No newline at end of file From 70cb079fbd7004ce477b3d2520352788f4184b76 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Wed, 7 Oct 2020 12:27:46 -0500 Subject: [PATCH 017/151] Add in unit test ater conversion --- .circleci/config.yml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 26df9e4..6c091cb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,17 +34,12 @@ jobs: paths: - node_modules - run: - - e2e - - codecov - - e2e: - steps: - - run: + name: 'Running unit tests' + command: './vendor/bin/phpunit test' + - run: name: 'Running E2E tests' command: '.buildscript/e2e.sh' - codecov: - steps: - - run: + - run: when: on_success name: 'Code coverage' command: 'bash <(curl -s https://codecov.io/bash)' From f38fe93242c343d19cb2ca1ea5d9a43ec7f7cd83 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Wed, 7 Oct 2020 13:25:47 -0500 Subject: [PATCH 018/151] remove unsupported php versions, current minimum version is 7.3 --- .circleci/config.yml | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6c091cb..2aac747 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,25 +44,10 @@ jobs: name: 'Code coverage' command: 'bash <(curl -s https://codecov.io/bash)' - test-php5: + test-php7.2: <<: *multi-test docker: - - image: circleci/php:5-node-browsers - - test-php5.6: - <<: *multi-test - docker: - - image: circleci/php:5.6-node-browsers - - test-php7.1: - <<: *multi-test - docker: - - image: circleci/php:7.1-node-browsers - - test-php7.3: - <<: *multi-test - docker: - - image: circleci/php:7.3-node-browsers + - image: circleci/php:7.2-node-browsers test-php7.4: <<: *multi-test @@ -72,8 +57,5 @@ workflows: version: 2 multi-test: jobs: - - test-php5 - - test-php5.6 - - test-php7.1 - - test-php7.3 + - test-php7.2 - test-php7.4 \ No newline at end of file From 57bc3a0dd51ffbc8974fd63a805a8de30720e32c Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Wed, 7 Oct 2020 13:25:47 -0500 Subject: [PATCH 019/151] remove unsupported php versions, current minimum version is 7.2 --- .circleci/config.yml | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6c091cb..2aac747 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,25 +44,10 @@ jobs: name: 'Code coverage' command: 'bash <(curl -s https://codecov.io/bash)' - test-php5: + test-php7.2: <<: *multi-test docker: - - image: circleci/php:5-node-browsers - - test-php5.6: - <<: *multi-test - docker: - - image: circleci/php:5.6-node-browsers - - test-php7.1: - <<: *multi-test - docker: - - image: circleci/php:7.1-node-browsers - - test-php7.3: - <<: *multi-test - docker: - - image: circleci/php:7.3-node-browsers + - image: circleci/php:7.2-node-browsers test-php7.4: <<: *multi-test @@ -72,8 +57,5 @@ workflows: version: 2 multi-test: jobs: - - test-php5 - - test-php5.6 - - test-php7.1 - - test-php7.3 + - test-php7.2 - test-php7.4 \ No newline at end of file From af8539ec12c0718bc8c093acb1fd6e2f6139ca06 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Tue, 13 Oct 2020 15:28:36 -0500 Subject: [PATCH 020/151] moved coverage to its own job --- .circleci/config.yml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2aac747..337f463 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,6 +4,14 @@ # version: 2 jobs: + + coverage: + steps: + - run: + when: on_success + name: 'Code coverage' + command: 'bash <(curl -s https://codecov.io/bash)' + multi-test: &multi-test docker: - image: php @@ -39,10 +47,7 @@ jobs: - run: name: 'Running E2E tests' command: '.buildscript/e2e.sh' - - run: - when: on_success - name: 'Code coverage' - command: 'bash <(curl -s https://codecov.io/bash)' + test-php7.2: <<: *multi-test @@ -57,5 +62,6 @@ workflows: version: 2 multi-test: jobs: + - coverage - test-php7.2 - test-php7.4 \ No newline at end of file From 715b7934d7f90679fd92495c71cc394cc78d38e2 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Tue, 13 Oct 2020 15:49:53 -0500 Subject: [PATCH 021/151] syntax update --- .circleci/config.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 337f463..fcdf082 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,11 +6,10 @@ version: 2 jobs: coverage: + docker: + - image: circleci/php:7.2-node-browsers steps: - - run: - when: on_success - name: 'Code coverage' - command: 'bash <(curl -s https://codecov.io/bash)' + - run: 'bash <(curl -s https://codecov.io/bash)' multi-test: &multi-test docker: @@ -48,12 +47,10 @@ jobs: name: 'Running E2E tests' command: '.buildscript/e2e.sh' - test-php7.2: <<: *multi-test docker: - image: circleci/php:7.2-node-browsers - test-php7.4: <<: *multi-test docker: From 3efec563381801b0e296b60f36782a4cfa9b8937 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Mon, 19 Oct 2020 16:11:18 -0500 Subject: [PATCH 022/151] Update status badging in readme #139 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a9bb62..ee469eb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ analytics-php ============== +[![Segmentio](https://circleci.com/gh/segmentio/analytics-php.svg?style=shield)](https://app.circleci.com/pipelines/github/segmentio/analytics-php) -[![Build Status](https://travis-ci.org/segmentio/analytics-php.png?branch=master)](https://travis-ci.org/segmentio/analytics-php) analytics-php is a php client for [Segment](https://segment.com) From 6c9d0b3ab55ecfecfbb0396fad8528ad1bcdb9c4 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Mon, 19 Oct 2020 17:11:53 -0500 Subject: [PATCH 023/151] Include circleci token in the badge #139 Had to include an API token slated for status in the projects circleci settings for this to render properly. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ee469eb..2575ae8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ analytics-php ============== -[![Segmentio](https://circleci.com/gh/segmentio/analytics-php.svg?style=shield)](https://app.circleci.com/pipelines/github/segmentio/analytics-php) +[![Segmentio](https://circleci.com/gh/segmentio/analytics-php.svg?style=shield&circle-token=a20a4b1daa865d164e62ca59a65d6753d286c1c1)](https://app.circleci.com/pipelines/github/segmentio/analytics-php) analytics-php is a php client for [Segment](https://segment.com) From 926f23b1809d083e38a060ae1afe85d8ee3ffc74 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Wed, 21 Oct 2020 17:19:25 -0500 Subject: [PATCH 024/151] Add codecov badge to readme #139 unable to add snyk but able to add codecov --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2575ae8..a55e929 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ analytics-php ============== [![Segmentio](https://circleci.com/gh/segmentio/analytics-php.svg?style=shield&circle-token=a20a4b1daa865d164e62ca59a65d6753d286c1c1)](https://app.circleci.com/pipelines/github/segmentio/analytics-php) +[![codecov](https://codecov.io/gh/segmentio/analytics-php/branch/master/graph/badge.svg?token=804hPfMd0C)](https://codecov.io/gh/segmentio/analytics-php) + analytics-php is a php client for [Segment](https://segment.com) From 751b006be948c875d60167be68f292a8ae6f086c Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Thu, 29 Oct 2020 10:58:02 -0500 Subject: [PATCH 025/151] Move Badges to be In-line Update location of badge to be in-line per request --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index a55e929..2935844 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ analytics-php ============== -[![Segmentio](https://circleci.com/gh/segmentio/analytics-php.svg?style=shield&circle-token=a20a4b1daa865d164e62ca59a65d6753d286c1c1)](https://app.circleci.com/pipelines/github/segmentio/analytics-php) - -[![codecov](https://codecov.io/gh/segmentio/analytics-php/branch/master/graph/badge.svg?token=804hPfMd0C)](https://codecov.io/gh/segmentio/analytics-php) - +[![Segmentio](https://circleci.com/gh/segmentio/analytics-php.svg?style=shield&circle-token=a20a4b1daa865d164e62ca59a65d6753d286c1c1)](https://app.circleci.com/pipelines/github/segmentio/analytics-php)[![codecov](https://codecov.io/gh/segmentio/analytics-php/branch/master/graph/badge.svg?token=804hPfMd0C)](https://codecov.io/gh/segmentio/analytics-php) analytics-php is a php client for [Segment](https://segment.com) From b0047dcb16426f4d7e3a545e8585e020a25f752a Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sun, 1 Nov 2020 14:20:30 -0600 Subject: [PATCH 026/151] Issue #137 conform to message, batch and queue size requirements and messaging --- lib/Segment/Consumer/ForkCurl.php | 6 ++---- lib/Segment/Consumer/LibCurl.php | 6 ++---- lib/Segment/Consumer/Socket.php | 6 ++---- lib/Segment/QueueConsumer.php | 19 +++++++++++++++++++ 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/lib/Segment/Consumer/ForkCurl.php b/lib/Segment/Consumer/ForkCurl.php index 6f73a00..c7b1f9e 100644 --- a/lib/Segment/Consumer/ForkCurl.php +++ b/lib/Segment/Consumer/ForkCurl.php @@ -69,10 +69,8 @@ public function flushBatch($messages) { // Verify message size is below than 32KB if (strlen($payload) >= 32 * 1024) { - if ($this->debug()) { - $msg = "Message size is larger than 32KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); - } + $msg = "Message size is larger than 32KB"; + error_log("[Analytics][" . $this->type . "] " . $msg); return false; } diff --git a/lib/Segment/Consumer/LibCurl.php b/lib/Segment/Consumer/LibCurl.php index 842a803..052f4bb 100644 --- a/lib/Segment/Consumer/LibCurl.php +++ b/lib/Segment/Consumer/LibCurl.php @@ -34,10 +34,8 @@ public function flushBatch($messages) { // Verify message size is below than 32KB if (strlen($payload) >= 32 * 1024) { - if ($this->debug()) { - $msg = "Message size is larger than 32KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); - } + $msg = "Message size is larger than 32KB"; + error_log("[Analytics][" . $this->type . "] " . $msg); return false; } diff --git a/lib/Segment/Consumer/Socket.php b/lib/Segment/Consumer/Socket.php index 2bec79b..ae58e0f 100644 --- a/lib/Segment/Consumer/Socket.php +++ b/lib/Segment/Consumer/Socket.php @@ -193,10 +193,8 @@ private function createBody($host, $content) { // Verify message size is below than 32KB if (strlen($req) >= 32 * 1024) { - if ($this->debug()) { - $msg = "Message size is larger than 32KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); - } + $msg = "Message size is larger than 32KB"; + error_log("[Analytics][" . $this->type . "] " . $msg); return false; } diff --git a/lib/Segment/QueueConsumer.php b/lib/Segment/QueueConsumer.php index 682950f..27d7edf 100644 --- a/lib/Segment/QueueConsumer.php +++ b/lib/Segment/QueueConsumer.php @@ -5,7 +5,10 @@ abstract class Segment_QueueConsumer extends Segment_Consumer { protected $queue; protected $max_queue_size = 1000; + protected $max_queue_size_bytes = 33,554,432; //32M + protected $batch_size = 100; + protected $max_batch_size_bytes = 512000; //500kb protected $maximum_backoff_duration = 10000; // Set maximum waiting limit to 10s protected $host = ""; protected $compress_request = false; @@ -111,6 +114,14 @@ public function flush() { while ($count > 0 && $success) { $batch = array_splice($this->queue, 0, min($this->batch_size, $count)); + + if (strlen($batch) >= $max_batch_size_bytes) { + $msg = "Batch size is larger than 500KB"; + error_log("[Analytics][" . $this->type . "] " . $msg); + + return false; + } + $success = $this->flushBatch($batch); $count = count($this->queue); @@ -131,7 +142,15 @@ protected function enqueue($item) { return false; } + if (strlen($this->queue) >= $max_queue_size_bytes) { + $msg = "Queue size is larger than 32MB"; + error_log("[Analytics][" . $this->type . "] " . $msg); + + return false; + } + $count = array_push($this->queue, $item); + if ($count >= $this->batch_size) { return $this->flush(); // return ->flush() result: true on success From 2e4983939c275b05c35db56b93d3126757470ca9 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sun, 1 Nov 2020 14:28:35 -0600 Subject: [PATCH 027/151] Issue #137: Resolve syntax error in number. --- lib/Segment/QueueConsumer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Segment/QueueConsumer.php b/lib/Segment/QueueConsumer.php index 27d7edf..ee4c1dd 100644 --- a/lib/Segment/QueueConsumer.php +++ b/lib/Segment/QueueConsumer.php @@ -5,7 +5,7 @@ abstract class Segment_QueueConsumer extends Segment_Consumer { protected $queue; protected $max_queue_size = 1000; - protected $max_queue_size_bytes = 33,554,432; //32M + protected $max_queue_size_bytes = 33554432; //32M protected $batch_size = 100; protected $max_batch_size_bytes = 512000; //500kb From 8daa3af96b6288e1e34c15ea06cd69c554e73ef6 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sun, 1 Nov 2020 14:42:15 -0600 Subject: [PATCH 028/151] Issue #137: Update size test to accommodate byte size of arrays --- lib/Segment/QueueConsumer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Segment/QueueConsumer.php b/lib/Segment/QueueConsumer.php index ee4c1dd..062d3a1 100644 --- a/lib/Segment/QueueConsumer.php +++ b/lib/Segment/QueueConsumer.php @@ -115,7 +115,7 @@ public function flush() { while ($count > 0 && $success) { $batch = array_splice($this->queue, 0, min($this->batch_size, $count)); - if (strlen($batch) >= $max_batch_size_bytes) { + if (mb_strlen(serialize((array)$this->queue), '8bit') >= $this->max_batch_size_bytes) { $msg = "Batch size is larger than 500KB"; error_log("[Analytics][" . $this->type . "] " . $msg); @@ -142,7 +142,7 @@ protected function enqueue($item) { return false; } - if (strlen($this->queue) >= $max_queue_size_bytes) { + if (mb_strlen(serialize((array)$this->queue), '8bit') >= $this->max_queue_size_bytes) { $msg = "Queue size is larger than 32MB"; error_log("[Analytics][" . $this->type . "] " . $msg); From af9f3aa79bd4f1e8aa64a0572b57ca052dba40ad Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sun, 1 Nov 2020 16:07:05 -0600 Subject: [PATCH 029/151] Issue #142 update e2e to version 0.4.1-pre1 --- .buildscript/e2e.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildscript/e2e.sh b/.buildscript/e2e.sh index ed25afe..5ccdab4 100755 --- a/.buildscript/e2e.sh +++ b/.buildscript/e2e.sh @@ -6,7 +6,7 @@ if [ "$RUN_E2E_TESTS" != "true" ]; then echo "Skipping end to end tests." else echo "Running end to end tests..." - wget https://github.com/segmentio/library-e2e-tester/releases/download/0.2.1/tester_linux_amd64 -O tester + wget https://github.com/segmentio/library-e2e-tester/releases/download/0.4.1-pre1/tester_linux_amd64 -O tester chmod +x tester ./tester -path='./bin/analytics' echo "End to end tests completed!" From 797543d333a6a79ed1aa3439684e70cee27efd61 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sun, 1 Nov 2020 16:38:07 -0600 Subject: [PATCH 030/151] Revert "Issue #142 update e2e to version 0.4.1-pre1" This reverts commit af9f3aa79bd4f1e8aa64a0572b57ca052dba40ad. --- .buildscript/e2e.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildscript/e2e.sh b/.buildscript/e2e.sh index 5ccdab4..ed25afe 100755 --- a/.buildscript/e2e.sh +++ b/.buildscript/e2e.sh @@ -6,7 +6,7 @@ if [ "$RUN_E2E_TESTS" != "true" ]; then echo "Skipping end to end tests." else echo "Running end to end tests..." - wget https://github.com/segmentio/library-e2e-tester/releases/download/0.4.1-pre1/tester_linux_amd64 -O tester + wget https://github.com/segmentio/library-e2e-tester/releases/download/0.2.1/tester_linux_amd64 -O tester chmod +x tester ./tester -path='./bin/analytics' echo "End to end tests completed!" From cd051eac727cb84ef8953b712e00d997446f1dd1 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sun, 1 Nov 2020 16:41:52 -0600 Subject: [PATCH 031/151] Issue #142 update e2e to version 0.4.1-pre1 --- .buildscript/e2e.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.buildscript/e2e.sh b/.buildscript/e2e.sh index ed25afe..5ccdab4 100755 --- a/.buildscript/e2e.sh +++ b/.buildscript/e2e.sh @@ -6,7 +6,7 @@ if [ "$RUN_E2E_TESTS" != "true" ]; then echo "Skipping end to end tests." else echo "Running end to end tests..." - wget https://github.com/segmentio/library-e2e-tester/releases/download/0.2.1/tester_linux_amd64 -O tester + wget https://github.com/segmentio/library-e2e-tester/releases/download/0.4.1-pre1/tester_linux_amd64 -O tester chmod +x tester ./tester -path='./bin/analytics' echo "End to end tests completed!" From 887deb912adebef7ef4da1f39cff267ce5d2a22c Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Tue, 17 Nov 2020 12:14:14 -0600 Subject: [PATCH 032/151] Issue #135: add default flush_interval option in constructor and allow it to be user configurable with 1 second limit and add error message if set lower that 1. add flush_at option and refactor batch_size option; add configurable limit to be no less that 1 and add error message if set lower that 1; also warn user of batch_size future deprecation --- lib/Segment/Consumer/ForkCurl.php | 2 +- lib/Segment/Consumer/LibCurl.php | 2 +- lib/Segment/QueueConsumer.php | 36 ++++++++++++++++++++++++++++--- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/lib/Segment/Consumer/ForkCurl.php b/lib/Segment/Consumer/ForkCurl.php index c7b1f9e..e837ead 100644 --- a/lib/Segment/Consumer/ForkCurl.php +++ b/lib/Segment/Consumer/ForkCurl.php @@ -10,7 +10,7 @@ class Segment_Consumer_ForkCurl extends Segment_QueueConsumer { * @param array $options * boolean "debug" - whether to use debug output, wait for response. * number "max_queue_size" - the max size of messages to enqueue - * number "batch_size" - how many messages to send in a single request + * number "flush_at" - how many messages to send in a single request */ public function __construct($secret, $options = array()) { parent::__construct($secret, $options); diff --git a/lib/Segment/Consumer/LibCurl.php b/lib/Segment/Consumer/LibCurl.php index 052f4bb..09e9500 100644 --- a/lib/Segment/Consumer/LibCurl.php +++ b/lib/Segment/Consumer/LibCurl.php @@ -9,7 +9,7 @@ class Segment_Consumer_LibCurl extends Segment_QueueConsumer { * @param array $options * boolean "debug" - whether to use debug output, wait for response. * number "max_queue_size" - the max size of messages to enqueue - * number "batch_size" - how many messages to send in a single request + * number "flush_at" - how many messages to send in a single request */ public function __construct($secret, $options = array()) { parent::__construct($secret, $options); diff --git a/lib/Segment/QueueConsumer.php b/lib/Segment/QueueConsumer.php index 062d3a1..9125042 100644 --- a/lib/Segment/QueueConsumer.php +++ b/lib/Segment/QueueConsumer.php @@ -7,12 +7,14 @@ abstract class Segment_QueueConsumer extends Segment_Consumer { protected $max_queue_size = 1000; protected $max_queue_size_bytes = 33554432; //32M - protected $batch_size = 100; + protected $flush_at = 100; protected $max_batch_size_bytes = 512000; //500kb protected $maximum_backoff_duration = 10000; // Set maximum waiting limit to 10s protected $host = ""; protected $compress_request = false; + protected $flush_interval_in_mills = 10000; //frequency in milliseconds to send data, default 10 + /** * Store our secret and options as part of this consumer * @param string $secret @@ -26,9 +28,25 @@ public function __construct($secret, $options = array()) { } if (isset($options["batch_size"])) { - $this->batch_size = $options["batch_size"]; + if($options["batch_size"] < 1) { + $msg = "Batch Size must not be less than 1"; + error_log("[Analytics][" . $this->type . "] " . $msg); + } else { + $msg = "WARNING: batch_size option to be depricated soon, please use new option flush_at"; + error_log("[Analytics][" . $this->type . "] " . $msg); + $this->flush_at = $options["batch_size"]; + } } + if (isset($options["flush_at"])) { + if($options["flush_at"] < 1) { + $msg = "Flush at Size must not be less than 1"; + error_log("[Analytics][" . $this->type . "] " . $msg); + } else { + $this->flush_at = $options["flush_at"]; + } + } + if (isset($options["host"])) { $this->host = $options["host"]; } @@ -36,6 +54,15 @@ public function __construct($secret, $options = array()) { if (isset($options["compress_request"])) { $this->compress_request = json_decode($options["compress_request"]); } + + if (isset($options["flush_interval"])) { + if($options["flush_interval"] < 1000) { + $msg = "Flush interval must not be less than 1 second"; + error_log("[Analytics][" . $this->type . "] " . $msg); + } else { + $this->flush_interval_in_mills = $options["flush_interval"]; + } + } $this->queue = array(); } @@ -113,6 +140,7 @@ public function flush() { $success = true; while ($count > 0 && $success) { + $batch = array_splice($this->queue, 0, min($this->batch_size, $count)); if (mb_strlen(serialize((array)$this->queue), '8bit') >= $this->max_batch_size_bytes) { @@ -125,6 +153,8 @@ public function flush() { $success = $this->flushBatch($batch); $count = count($this->queue); + + usleep($this->flush_interval_in_mills * 1000); } return $success; @@ -152,7 +182,7 @@ protected function enqueue($item) { $count = array_push($this->queue, $item); - if ($count >= $this->batch_size) { + if ($count >= $this->flush_at) { return $this->flush(); // return ->flush() result: true on success } From 96ee294cedaf6094f9135880cf62adb2c3fd36fd Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Tue, 17 Nov 2020 14:54:17 -0600 Subject: [PATCH 033/151] update missed batch_size call --- lib/Segment/QueueConsumer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Segment/QueueConsumer.php b/lib/Segment/QueueConsumer.php index 9125042..5008de0 100644 --- a/lib/Segment/QueueConsumer.php +++ b/lib/Segment/QueueConsumer.php @@ -141,7 +141,7 @@ public function flush() { while ($count > 0 && $success) { - $batch = array_splice($this->queue, 0, min($this->batch_size, $count)); + $batch = array_splice($this->queue, 0, min($this->flush_at, $count)); if (mb_strlen(serialize((array)$this->queue), '8bit') >= $this->max_batch_size_bytes) { $msg = "Batch size is larger than 500KB"; From 698619c1b1ce6172a71f413fc52b0c90aca84377 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sat, 5 Dec 2020 16:59:44 -0600 Subject: [PATCH 034/151] Circleci docker xdebug fix --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index fcdf082..08bc7c5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,6 +19,7 @@ jobs: - run: sudo apt update - run: sudo apt install zlib1g-dev - run: sudo docker-php-ext-install zip + - run: sudo docker-php-ext-enable xdebug - restore_cache: keys: From 566c9b038dbb75743ea27778dd846c3bcb0c7fba Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sat, 5 Dec 2020 17:08:08 -0600 Subject: [PATCH 035/151] More additions to docker settings --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 08bc7c5..ade250e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,6 +20,7 @@ jobs: - run: sudo apt install zlib1g-dev - run: sudo docker-php-ext-install zip - run: sudo docker-php-ext-enable xdebug + - run: sudo docker-php-ext-enable coverage - restore_cache: keys: From d887fa626430cb6ca266f211ca038fef0c063bbc Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sat, 5 Dec 2020 17:24:38 -0600 Subject: [PATCH 036/151] Enabling xdebug for code coverage --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ade250e..7b4d1ea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,7 +20,7 @@ jobs: - run: sudo apt install zlib1g-dev - run: sudo docker-php-ext-install zip - run: sudo docker-php-ext-enable xdebug - - run: sudo docker-php-ext-enable coverage + - run: sudo docker-php-ext-enable xdebug.mode = coverage - restore_cache: keys: From 10c99f349b9c06bbc805ee83037d76bdab3a804f Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sat, 5 Dec 2020 17:38:02 -0600 Subject: [PATCH 037/151] enable xdebug_mode --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7b4d1ea..9921973 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,7 +20,8 @@ jobs: - run: sudo apt install zlib1g-dev - run: sudo docker-php-ext-install zip - run: sudo docker-php-ext-enable xdebug - - run: sudo docker-php-ext-enable xdebug.mode = coverage + - run: sudo XDEBUG_MODE=coverage + - run: sudo export XDEBUG_MODE - restore_cache: keys: From 15035d42ce21fd86f22b28493903be00339ea8fc Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sat, 5 Dec 2020 17:42:18 -0600 Subject: [PATCH 038/151] ubuntu syntax mod --- .circleci/config.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9921973..2935b66 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,9 +19,7 @@ jobs: - run: sudo apt update - run: sudo apt install zlib1g-dev - run: sudo docker-php-ext-install zip - - run: sudo docker-php-ext-enable xdebug - - run: sudo XDEBUG_MODE=coverage - - run: sudo export XDEBUG_MODE + - run: sudo export XDEBUG_MODE=coverage - restore_cache: keys: From 6c49b1a723e7e01914d31ae505160fbc9f65630e Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Sat, 5 Dec 2020 17:46:52 -0600 Subject: [PATCH 039/151] set the env var a different way --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2935b66..cd8c136 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,12 +14,13 @@ jobs: multi-test: &multi-test docker: - image: php + environment: + XDEBUG_MODE: coverage steps: - checkout - run: sudo apt update - run: sudo apt install zlib1g-dev - run: sudo docker-php-ext-install zip - - run: sudo export XDEBUG_MODE=coverage - restore_cache: keys: From 72b2f9baf9cde1c7e3a81a4a116df67d9ced1f23 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Wed, 16 Dec 2020 12:06:40 -0600 Subject: [PATCH 040/151] Modify default max queue size setting to match docs --- lib/Segment/QueueConsumer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Segment/QueueConsumer.php b/lib/Segment/QueueConsumer.php index 5008de0..ca25e3c 100644 --- a/lib/Segment/QueueConsumer.php +++ b/lib/Segment/QueueConsumer.php @@ -4,7 +4,7 @@ abstract class Segment_QueueConsumer extends Segment_Consumer { protected $type = "QueueConsumer"; protected $queue; - protected $max_queue_size = 1000; + protected $max_queue_size = 10000; protected $max_queue_size_bytes = 33554432; //32M protected $flush_at = 100; From bba6e86e0d46972d0778416607cd3544c1325c6e Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 24 Feb 2021 10:04:18 -0600 Subject: [PATCH 041/151] Update circleci for php 8 tests --- .circleci/config.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cd8c136..7fa7374 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -57,10 +57,15 @@ jobs: <<: *multi-test docker: - image: circleci/php:7.4-node-browsers + test-php8.0: + <<: *multi-test + docker: + - image: circleci/php:8.0-node-browsers workflows: version: 2 multi-test: jobs: - coverage - test-php7.2 - - test-php7.4 \ No newline at end of file + - test-php7.4 + - test-php8.0 \ No newline at end of file From f295d7a976681c832b79ecb2ca23fc6d2c07dfcd Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 24 Feb 2021 10:19:28 -0600 Subject: [PATCH 042/151] Update image name to match alias --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7fa7374..777db5d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -60,7 +60,7 @@ jobs: test-php8.0: <<: *multi-test docker: - - image: circleci/php:8.0-node-browsers + - image: circleci/php:8.0 workflows: version: 2 multi-test: From ad6f21068fc5455b8a85a5d922c47a92645c27f5 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 24 Feb 2021 10:24:03 -0600 Subject: [PATCH 043/151] update --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 777db5d..5a9b74b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -60,7 +60,7 @@ jobs: test-php8.0: <<: *multi-test docker: - - image: circleci/php:8.0 + - image: circleci/php:8.0-node workflows: version: 2 multi-test: From 398154bff4d15b0fe3260c31db4c5af3b83d911a Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 24 Feb 2021 11:05:11 -0600 Subject: [PATCH 044/151] trying to force correct version of phpunit for php8 --- .circleci/config.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5a9b74b..bcfe172 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -60,7 +60,11 @@ jobs: test-php8.0: <<: *multi-test docker: - - image: circleci/php:8.0-node + - image: circleci/php:8.0-node + steps: + - run: + name: 'Running unit tests' + command: './vendor/bin/phpunit ^9 test' workflows: version: 2 multi-test: From be727c23177b1e81124779dd3753062c6e394843 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 24 Feb 2021 11:19:18 -0600 Subject: [PATCH 045/151] upgrading phpunit --- .circleci/config.yml | 4 ---- composer.json | 4 ++-- phpunit.xml | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bcfe172..7f69317 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -61,10 +61,6 @@ jobs: <<: *multi-test docker: - image: circleci/php:8.0-node - steps: - - run: - name: 'Running unit tests' - command: './vendor/bin/phpunit ^9 test' workflows: version: 2 multi-test: diff --git a/composer.json b/composer.json index 5d12661..99edb8e 100644 --- a/composer.json +++ b/composer.json @@ -17,10 +17,10 @@ } ], "require": { - "php": ">=5.3.3" + "php": ">=7.2" }, "require-dev": { - "phpunit/phpunit": "~4.0", + "phpunit/phpunit": "~9.0", "overtrue/phplint": "^1.1", "squizlabs/php_codesniffer": "^3.3" }, diff --git a/phpunit.xml b/phpunit.xml index 36a1da3..16b4d0b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,6 +1,6 @@ Date: Wed, 24 Feb 2021 11:23:29 -0600 Subject: [PATCH 046/151] Update includes --- test/AnalyticsTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/test/AnalyticsTest.php b/test/AnalyticsTest.php index ede6259..adbe925 100644 --- a/test/AnalyticsTest.php +++ b/test/AnalyticsTest.php @@ -1,6 +1,7 @@ Date: Fri, 26 Feb 2021 12:52:26 -0600 Subject: [PATCH 047/151] Updating test suite for new version of phpUnit --- test/AnalyticsTest.php | 7 +++---- test/ClientTest.php | 2 +- test/ConsumerFileTest.php | 6 +++--- test/ConsumerForkCurlTest.php | 4 ++-- test/ConsumerLibCurlTest.php | 4 ++-- test/ConsumerSocketTest.php | 4 ++-- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/test/AnalyticsTest.php b/test/AnalyticsTest.php index adbe925..f297fbe 100644 --- a/test/AnalyticsTest.php +++ b/test/AnalyticsTest.php @@ -1,11 +1,10 @@ true)); @@ -43,7 +42,7 @@ public function testGroupAnonymous() /** * @expectedException \Exception - * @expectedExceptionMessage Segment::group() requires userId or anonymousId + * @expectedExceptionMessage Segment::group() expects groupId */ public function testGroupNoUser() { diff --git a/test/ClientTest.php b/test/ClientTest.php index 754f4c5..e9d7bd7 100644 --- a/test/ClientTest.php +++ b/test/ClientTest.php @@ -2,7 +2,7 @@ require_once __DIR__ . '/../lib/Segment/Client.php'; -class ClientTest extends PHPUnit_Framework_TestCase +class ClientTest extends \PHPUnit\Framework\TestCase { /** @test */ public function it_uses_the_lib_curl_consumer_as_default() diff --git a/test/ConsumerFileTest.php b/test/ConsumerFileTest.php index 82ab848..103d3f8 100644 --- a/test/ConsumerFileTest.php +++ b/test/ConsumerFileTest.php @@ -2,12 +2,12 @@ require_once __DIR__ . "/../lib/Segment/Client.php"; -class ConsumerFileTest extends PHPUnit_Framework_TestCase +class ConsumerFileTest extends \PHPUnit\Framework\TestCase { private $client; private $filename = "/tmp/analytics.log"; - public function setUp() + public function setUp(): void { date_default_timezone_set("UTC"); if (file_exists($this->filename())) { @@ -23,7 +23,7 @@ public function setUp() ); } - public function tearDown() + public function tearDown(): void { if (file_exists($this->filename)) { unlink($this->filename); diff --git a/test/ConsumerForkCurlTest.php b/test/ConsumerForkCurlTest.php index d072b77..f915544 100644 --- a/test/ConsumerForkCurlTest.php +++ b/test/ConsumerForkCurlTest.php @@ -2,11 +2,11 @@ require_once __DIR__ . "/../lib/Segment/Client.php"; -class ConsumerForkCurlTest extends PHPUnit_Framework_TestCase +class ConsumerForkCurlTest extends \PHPUnit\Framework\TestCase { private $client; - public function setUp() + public function setUp(): void { date_default_timezone_set("UTC"); $this->client = new Segment_Client( diff --git a/test/ConsumerLibCurlTest.php b/test/ConsumerLibCurlTest.php index a54a42f..4a7331f 100644 --- a/test/ConsumerLibCurlTest.php +++ b/test/ConsumerLibCurlTest.php @@ -2,11 +2,11 @@ require_once __DIR__ . "/../lib/Segment/Client.php"; -class ConsumerLibCurlTest extends PHPUnit_Framework_TestCase +class ConsumerLibCurlTest extends \PHPUnit\Framework\TestCase { private $client; - public function setUp() + public function setUp(): void { date_default_timezone_set("UTC"); $this->client = new Segment_Client( diff --git a/test/ConsumerSocketTest.php b/test/ConsumerSocketTest.php index 8575bba..8ab8d40 100644 --- a/test/ConsumerSocketTest.php +++ b/test/ConsumerSocketTest.php @@ -2,11 +2,11 @@ require_once __DIR__ . "/../lib/Segment/Client.php"; -class ConsumerSocketTest extends PHPUnit_Framework_TestCase +class ConsumerSocketTest extends \PHPUnit\Framework\TestCase { private $client; - public function setUp() + public function setUp(): void { date_default_timezone_set("UTC"); $this->client = new Segment_Client( From 9e9ea0819b0a05259befc750ddea33edd668cbc4 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Sat, 27 Feb 2021 12:02:12 -0600 Subject: [PATCH 048/151] Test updates --- test/AnalyticsTest.php | 2 +- test/ConsumerFileTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/AnalyticsTest.php b/test/AnalyticsTest.php index f297fbe..4e36d91 100644 --- a/test/AnalyticsTest.php +++ b/test/AnalyticsTest.php @@ -42,7 +42,7 @@ public function testGroupAnonymous() /** * @expectedException \Exception - * @expectedExceptionMessage Segment::group() expects groupId + * @expectedExceptionMessage Segment::group() requires userId or anonymousId */ public function testGroupNoUser() { diff --git a/test/ConsumerFileTest.php b/test/ConsumerFileTest.php index 103d3f8..1a11296 100644 --- a/test/ConsumerFileTest.php +++ b/test/ConsumerFileTest.php @@ -105,7 +105,7 @@ public function testSend() } exec("php --define date.timezone=UTC send.php --secret oq0vdlg7yi --file /tmp/analytics.log", $output); $this->assertSame("sent 200 from 200 requests successfully", trim($output[0])); - $this->assertFileNotExists($this->filename()); + $this->assertFileDoesNotExist($this->filename()); } public function testProductionProblems() From d4f02eb80dac3aa999f07d348c55235802a5e232 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Mon, 1 Mar 2021 16:09:04 -0600 Subject: [PATCH 049/151] Update tests to not only eval if userId exists in the array, but that zero ("0") is a valid userId --- lib/Segment.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/Segment.php b/lib/Segment.php index 2e1aee0..10c25d9 100644 --- a/lib/Segment.php +++ b/lib/Segment.php @@ -52,8 +52,8 @@ public static function identify(array $message) { */ public static function group(array $message) { self::checkClient(); - $groupId = !empty($message["groupId"]); - self::assert($groupId, "Segment::group() expects groupId"); + $groupId = !empty($message['groupId']); + self::assert($groupId, "Segment::group() expects a groupId"); self::validate($message, "group"); return self::$client->group($message); @@ -93,8 +93,8 @@ public static function screen(array $message) { */ public static function alias(array $message) { self::checkClient(); - $userId = !empty($message["userId"]); - $previousId = !empty($message["previousId"]); + $userId = (array_key_exists('userId', $message) && strlen((string) $message['userId']) > 0); + $previousId = (array_key_exists('previousId', $message) && strlen((string) $message['previousId']) > 0); self::assert($userId && $previousId, "Segment::alias() requires both userId and previousId"); return self::$client->alias($message); @@ -103,12 +103,12 @@ public static function alias(array $message) { /** * Validate common properties. * - * @param array $msg + * @param array $message * @param string $type */ - public static function validate($msg, $type){ - $userId = !empty($msg["userId"]); - $anonId = !empty($msg["anonymousId"]); + public static function validate($message, $type){ + $userId = (array_key_exists('userId', $message) && strlen((string) $message['userId']) > 0); + $anonId = !empty($message['anonymousId']); self::assert($userId || $anonId, "Segment::${type}() requires userId or anonymousId"); } From f2e2b6a9bbf5a6c23cec38e71ac8f2481b9e5063 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Mon, 1 Mar 2021 16:09:04 -0600 Subject: [PATCH 050/151] Update tests to not only eval if userId exists in the array, but that zero ("0") is a valid userId --- lib/Segment.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/Segment.php b/lib/Segment.php index 2e1aee0..10c25d9 100644 --- a/lib/Segment.php +++ b/lib/Segment.php @@ -52,8 +52,8 @@ public static function identify(array $message) { */ public static function group(array $message) { self::checkClient(); - $groupId = !empty($message["groupId"]); - self::assert($groupId, "Segment::group() expects groupId"); + $groupId = !empty($message['groupId']); + self::assert($groupId, "Segment::group() expects a groupId"); self::validate($message, "group"); return self::$client->group($message); @@ -93,8 +93,8 @@ public static function screen(array $message) { */ public static function alias(array $message) { self::checkClient(); - $userId = !empty($message["userId"]); - $previousId = !empty($message["previousId"]); + $userId = (array_key_exists('userId', $message) && strlen((string) $message['userId']) > 0); + $previousId = (array_key_exists('previousId', $message) && strlen((string) $message['previousId']) > 0); self::assert($userId && $previousId, "Segment::alias() requires both userId and previousId"); return self::$client->alias($message); @@ -103,12 +103,12 @@ public static function alias(array $message) { /** * Validate common properties. * - * @param array $msg + * @param array $message * @param string $type */ - public static function validate($msg, $type){ - $userId = !empty($msg["userId"]); - $anonId = !empty($msg["anonymousId"]); + public static function validate($message, $type){ + $userId = (array_key_exists('userId', $message) && strlen((string) $message['userId']) > 0); + $anonId = !empty($message['anonymousId']); self::assert($userId || $anonId, "Segment::${type}() requires userId or anonymousId"); } From 0b0492409f8674cae8c318e4d72621c3023cf39f Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 11 Mar 2021 14:38:53 -0600 Subject: [PATCH 051/151] Update change log and version for preparation for version release, after merge run: make release VERSION=1.7.0 --- HISTORY.md | 16 ++++++++++++++++ composer.json | 4 ++-- lib/Segment/Version.php | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 6dce29a..95016ca 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,19 @@ +1.7.0-beta / 2021-03-10 +======================= + + * Retry Network errors (#136) + * Update Tests [Improvement] (#132) + * Updtate Readme Status Badging (#139) + * Bump e2e tests to latest version [Improvement] (#142) + * Add Limits to message, batch and memeory usage [Feature] (#137) + * Add Configurable flush parameters [Feature] (#135) + * Add ability to use custom consumer [Feature] (#61) + * Add ability to set file permmissions [Feature] (#122) + * Fix curl error handler [Improvement] (#97) + * Fix timestamp implementation for microseconds (#94) + * Modify max queue size setting to match requirements (#153, #146) + * Add ability to set userid as zero (#157) + 1.6.1-beta / 2018-05-01 ======================= diff --git a/composer.json b/composer.json index 5d12661..39a4e94 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "segmentio/analytics-php", - "version": "1.6.1-beta", + "version": "1.7.0", "description": "Segment Analytics PHP Library", "keywords": [ "analytics", @@ -32,4 +32,4 @@ "bin": [ "bin/analytics" ] -} +} \ No newline at end of file diff --git a/lib/Segment/Version.php b/lib/Segment/Version.php index 083fc80..7b697c6 100644 --- a/lib/Segment/Version.php +++ b/lib/Segment/Version.php @@ -1,3 +1,3 @@ Date: Thu, 11 Mar 2021 14:43:15 -0600 Subject: [PATCH 052/151] Update HISTORY.md Removed beta from release version level --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 95016ca..764e3f8 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,4 @@ -1.7.0-beta / 2021-03-10 +1.7.0 / 2021-03-10 ======================= * Retry Network errors (#136) From 9bacd723f8846c9e3c695f81370e02f3603528ba Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 7 Apr 2021 10:37:11 -0500 Subject: [PATCH 053/151] Update usleep to run only when items remain in the queue --- lib/Segment/QueueConsumer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Segment/QueueConsumer.php b/lib/Segment/QueueConsumer.php index ca25e3c..ff2527e 100644 --- a/lib/Segment/QueueConsumer.php +++ b/lib/Segment/QueueConsumer.php @@ -154,7 +154,8 @@ public function flush() { $count = count($this->queue); - usleep($this->flush_interval_in_mills * 1000); + if($count > 0) + usleep($this->flush_interval_in_mills * 1000); } return $success; From 966bc0bfcde34e414ff4cf0b64437d6f2671b35c Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 7 Apr 2021 11:11:08 -0500 Subject: [PATCH 054/151] Trying to force circleci to build --- lib/Segment/QueueConsumer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Segment/QueueConsumer.php b/lib/Segment/QueueConsumer.php index ff2527e..bbf8a09 100644 --- a/lib/Segment/QueueConsumer.php +++ b/lib/Segment/QueueConsumer.php @@ -203,4 +203,4 @@ protected function payload($batch){ "sentAt" => date("c"), ); } -} +} \ No newline at end of file From 0a10aa596047f822af3f37b287dd4ef4c4d23b88 Mon Sep 17 00:00:00 2001 From: Zach Ovington Date: Wed, 7 Apr 2021 13:20:54 -0600 Subject: [PATCH 055/151] as a user I can override the messageId on track events --- lib/Segment/Client.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Segment/Client.php b/lib/Segment/Client.php index 78e9dff..8f8c075 100644 --- a/lib/Segment/Client.php +++ b/lib/Segment/Client.php @@ -218,7 +218,10 @@ private function message($msg, $def = ""){ $msg["timestamp"] = null; } $msg["timestamp"] = $this->formatTime($msg["timestamp"]); - $msg["messageId"] = self::messageId(); + + if (!isset($msg["messageId"])) { + $msg["messageId"] = self::messageId(); + } return $msg; } From 660d196a0c5b4775bd34b6faecf7b7fcacb7b492 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Thu, 15 Apr 2021 12:11:54 -0700 Subject: [PATCH 056/151] Remove circleci config --- .circleci/config.yml | 66 -------------------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index cd8c136..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,66 +0,0 @@ -# PHP CircleCI 2.0 configuration file -# -# Check https://circleci.com/docs/2.0/language-php/ for more details -# -version: 2 -jobs: - - coverage: - docker: - - image: circleci/php:7.2-node-browsers - steps: - - run: 'bash <(curl -s https://codecov.io/bash)' - - multi-test: &multi-test - docker: - - image: php - environment: - XDEBUG_MODE: coverage - steps: - - checkout - - run: sudo apt update - - run: sudo apt install zlib1g-dev - - run: sudo docker-php-ext-install zip - - - restore_cache: - keys: - - v1-dependencies-{{ checksum "composer.json" }} - - v1-dependencies- - - - run: composer install -n --prefer-dist - - - save_cache: - key: v1-dependencies-{{ checksum "composer.json" }} - paths: - - ./vendor - - restore_cache: - keys: - - node-v1-{{ checksum "composer.json" }} - - node-v1- - - run: yarn install - - save_cache: - key: node-v1-{{ checksum "composer.json" }} - paths: - - node_modules - - run: - name: 'Running unit tests' - command: './vendor/bin/phpunit test' - - run: - name: 'Running E2E tests' - command: '.buildscript/e2e.sh' - - test-php7.2: - <<: *multi-test - docker: - - image: circleci/php:7.2-node-browsers - test-php7.4: - <<: *multi-test - docker: - - image: circleci/php:7.4-node-browsers -workflows: - version: 2 - multi-test: - jobs: - - coverage - - test-php7.2 - - test-php7.4 \ No newline at end of file From e3f2c7643fa23bb7ce7e729dbfdf030ea5b6fa36 Mon Sep 17 00:00:00 2001 From: PLL Date: Sun, 18 Apr 2021 14:55:36 +0200 Subject: [PATCH 057/151] keep batching messages until size is just below the maximum of 32KB --- send.php | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/send.php b/send.php index c8c0d4c..a6b06a2 100755 --- a/send.php +++ b/send.php @@ -67,19 +67,32 @@ $total = 0; $successful = 0; foreach ($lines as $line) { - if (!trim($line)) continue; - $payload = json_decode($line, true); - $dt = new DateTime($payload["timestamp"]); - $ts = floatval($dt->getTimestamp() . "." . $dt->format("u")); - $payload["timestamp"] = $ts; - $type = $payload["type"]; - $ret = call_user_func_array(array("Segment", $type), array($payload)); - if ($ret) $successful++; - $total++; - if ($total % 100 === 0) Segment::flush(); + if (!trim($line)) continue; + $total++; + $payload = json_decode($line, true); + $dt = new DateTime($payload["timestamp"]); + $ts = floatval($dt->getTimestamp() . "." . $dt->format("u")); + $payload["timestamp"] = date("c", (int) $ts); + $type = $payload["type"]; + $currentBatch[] = $payload; + // flush before batch gets too big + if (strlen(json_encode(array('batch' => $currentBatch, 'sentAt' => date("c")))) >= 32 * 1024) { + $libCurlResponse = Segment::flush(); + if ($libCurlResponse) { + $successful += count($currentBatch) - 1; + } else { + // todo: maybe write batch to analytics-error.log for more controlled errorhandling + } + $currentBatch = array(); + } + $payload["timestamp"] = $ts; + call_user_func_array(array("Segment", $type), array($payload)); } -Segment::flush(); +$libCurlResponse = Segment::flush(); +if ($libCurlResponse) { + $successful += count($lines); +} unlink($file); /** From cac992926331fec19444bfb5d23c0fba34e0720c Mon Sep 17 00:00:00 2001 From: PLL Date: Sun, 18 Apr 2021 14:59:15 +0200 Subject: [PATCH 058/151] keep batching messages until size is just below the maximum of 32KB --- send.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/send.php b/send.php index a6b06a2..69c6be7 100755 --- a/send.php +++ b/send.php @@ -91,7 +91,7 @@ $libCurlResponse = Segment::flush(); if ($libCurlResponse) { - $successful += count($lines); + $successful += count($lines) - $total !== 0 ?: $total; } unlink($file); From 55588d2fe3af19a1b7a4585656a6d40e5aff19ab Mon Sep 17 00:00:00 2001 From: PLL Date: Sun, 18 Apr 2021 15:53:01 +0200 Subject: [PATCH 059/151] fixed successful count --- send.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/send.php b/send.php index 69c6be7..0b56eff 100755 --- a/send.php +++ b/send.php @@ -91,7 +91,7 @@ $libCurlResponse = Segment::flush(); if ($libCurlResponse) { - $successful += count($lines) - $total !== 0 ?: $total; + $successful += $total - $successful; } unlink($file); From c69bfed9443a37f45779be2aa923dc8dce24c94d Mon Sep 17 00:00:00 2001 From: PLL Date: Sun, 18 Apr 2021 19:08:38 +0200 Subject: [PATCH 060/151] #167 | updated endpoint to batch, switched batch check to 500KB instead of 32KB --- lib/Segment/Consumer/LibCurl.php | 10 +--------- lib/Segment/QueueConsumer.php | 18 +++++++++++++----- send.php | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/Segment/Consumer/LibCurl.php b/lib/Segment/Consumer/LibCurl.php index 22726a3..75e76aa 100644 --- a/lib/Segment/Consumer/LibCurl.php +++ b/lib/Segment/Consumer/LibCurl.php @@ -32,14 +32,6 @@ public function flushBatch($messages) { $payload = json_encode($body); $secret = $this->secret; - // Verify message size is below than 32KB - if (strlen($payload) >= 32 * 1024) { - $msg = "Message size is larger than 32KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); - - return false; - } - if ($this->compress_request) { $payload = gzencode($payload); } @@ -50,7 +42,7 @@ public function flushBatch($messages) { } else { $host = "api.segment.io"; } - $path = "/v1/import"; + $path = "/v1/batch"; $url = $protocol . $host . $path; $backoff = 100; // Set initial waiting time to 100ms diff --git a/lib/Segment/QueueConsumer.php b/lib/Segment/QueueConsumer.php index bbf8a09..62abde9 100644 --- a/lib/Segment/QueueConsumer.php +++ b/lib/Segment/QueueConsumer.php @@ -9,6 +9,7 @@ abstract class Segment_QueueConsumer extends Segment_Consumer { protected $flush_at = 100; protected $max_batch_size_bytes = 512000; //500kb + protected $max_item_size_bytes = 32000; // 32kb protected $maximum_backoff_duration = 10000; // Set maximum waiting limit to 10s protected $host = ""; protected $compress_request = false; @@ -143,12 +144,12 @@ public function flush() { $batch = array_splice($this->queue, 0, min($this->flush_at, $count)); - if (mb_strlen(serialize((array)$this->queue), '8bit') >= $this->max_batch_size_bytes) { - $msg = "Batch size is larger than 500KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); + if (mb_strlen(serialize($batch), '8bit') >= $this->max_batch_size_bytes) { + $msg = "Batch size is larger than 500KB"; + error_log("[Analytics][" . $this->type . "] " . $msg); - return false; - } + return false; + } $success = $this->flushBatch($batch); @@ -180,6 +181,13 @@ protected function enqueue($item) { return false; } + if (mb_strlen(json_encode($item), '8bit') >= $this->max_item_size_bytes) { + $msg = "Item size is larger than 32KB"; + error_log("[Analytics][" . $this->type . "] " . $msg); + + return false; + } + $count = array_push($this->queue, $item); diff --git a/send.php b/send.php index 0b56eff..9ce708f 100755 --- a/send.php +++ b/send.php @@ -76,7 +76,7 @@ $type = $payload["type"]; $currentBatch[] = $payload; // flush before batch gets too big - if (strlen(json_encode(array('batch' => $currentBatch, 'sentAt' => date("c")))) >= 32 * 1024) { + if (mb_strlen((json_encode(array('batch' => $currentBatch, 'sentAt' => date("c")))), '8bit') >= 512000) { $libCurlResponse = Segment::flush(); if ($libCurlResponse) { $successful += count($currentBatch) - 1; From 65b88ca7fb3d891acbbea4ab6ab93ace11f5a4f8 Mon Sep 17 00:00:00 2001 From: PLL Date: Sun, 18 Apr 2021 19:15:03 +0200 Subject: [PATCH 061/151] #167 | update endpoint following http api documentation --- lib/Segment/Consumer/ForkCurl.php | 2 +- lib/Segment/Consumer/Socket.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Segment/Consumer/ForkCurl.php b/lib/Segment/Consumer/ForkCurl.php index e837ead..d04e949 100644 --- a/lib/Segment/Consumer/ForkCurl.php +++ b/lib/Segment/Consumer/ForkCurl.php @@ -41,7 +41,7 @@ public function flushBatch($messages) { } else { $host = "api.segment.io"; } - $path = "/v1/import"; + $path = "/v1/batch"; $url = $protocol . $host . $path; $cmd = "curl -u ${secret}: -X POST -H 'Content-Type: application/json'"; diff --git a/lib/Segment/Consumer/Socket.php b/lib/Segment/Consumer/Socket.php index ae58e0f..ec7cb52 100644 --- a/lib/Segment/Consumer/Socket.php +++ b/lib/Segment/Consumer/Socket.php @@ -167,7 +167,7 @@ private function makeRequest($socket, $req, $retry = true) { */ private function createBody($host, $content) { $req = ""; - $req.= "POST /v1/import HTTP/1.1\r\n"; + $req.= "POST /v1/batch HTTP/1.1\r\n"; $req.= "Host: " . $host . "\r\n"; $req.= "Content-Type: application/json\r\n"; $req.= "Authorization: Basic " . base64_encode($this->secret . ":") . "\r\n"; From 43aa37289fe3fdc95cb4825282479108cff759ae Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 12 May 2021 10:04:24 -0500 Subject: [PATCH 062/151] Update circleci config to not include code coverage --- .circleci/config.yml | 59 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..c3a5ebd --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,59 @@ +# PHP CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-php/ for more details +# +version: 2 +jobs: + + multi-test: &multi-test + docker: + - image: php + environment: + XDEBUG_MODE + steps: + - checkout + - run: sudo apt update + - run: sudo apt install zlib1g-dev + - run: sudo docker-php-ext-install zip + + - restore_cache: + keys: + - v1-dependencies-{{ checksum "composer.json" }} + - v1-dependencies- + + - run: composer install -n --prefer-dist + + - save_cache: + key: v1-dependencies-{{ checksum "composer.json" }} + paths: + - ./vendor + - restore_cache: + keys: + - node-v1-{{ checksum "composer.json" }} + - node-v1- + - run: yarn install + - save_cache: + key: node-v1-{{ checksum "composer.json" }} + paths: + - node_modules + - run: + name: 'Running unit tests' + command: './vendor/bin/phpunit test' + - run: + name: 'Running E2E tests' + command: '.buildscript/e2e.sh' + + test-php7.2: + <<: *multi-test + docker: + - image: circleci/php:7.2-node-browsers + test-php7.4: + <<: *multi-test + docker: + - image: circleci/php:7.4-node-browsers +workflows: + version: 2 + multi-test: + jobs: + - test-php7.2 + - test-php7.4 \ No newline at end of file From 7eeb0747b3ae3f71e37873b46b1f4f0ef2010b4e Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 12 May 2021 10:08:29 -0500 Subject: [PATCH 063/151] Modify Xdebug_mode setting --- .circleci/config.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c3a5ebd..c7345f5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,8 +8,6 @@ jobs: multi-test: &multi-test docker: - image: php - environment: - XDEBUG_MODE steps: - checkout - run: sudo apt update From f7e9a0eb76206e5a59743d61be544328231f516d Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 12 May 2021 10:12:34 -0500 Subject: [PATCH 064/151] Fix xdebug issue --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index c7345f5..17767ce 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,6 +8,8 @@ jobs: multi-test: &multi-test docker: - image: php + environment: + XDEBUG_MODE: php-code-coverage steps: - checkout - run: sudo apt update From 5bd1603a0d1dd9e840a1715538df0c288a6ee71a Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 12 May 2021 10:22:09 -0500 Subject: [PATCH 065/151] Modify xdebug setting --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 17767ce..e4937b7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,7 +9,7 @@ jobs: docker: - image: php environment: - XDEBUG_MODE: php-code-coverage + XDEBUG_MODE: coverage steps: - checkout - run: sudo apt update From 809a62b80debd10b93ed9b88950d1415a13764d3 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 12 May 2021 10:34:28 -0500 Subject: [PATCH 066/151] Removed failing codecov bar from readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2935844..f25cf00 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ analytics-php ============== -[![Segmentio](https://circleci.com/gh/segmentio/analytics-php.svg?style=shield&circle-token=a20a4b1daa865d164e62ca59a65d6753d286c1c1)](https://app.circleci.com/pipelines/github/segmentio/analytics-php)[![codecov](https://codecov.io/gh/segmentio/analytics-php/branch/master/graph/badge.svg?token=804hPfMd0C)](https://codecov.io/gh/segmentio/analytics-php) +[![Segmentio](https://circleci.com/gh/segmentio/analytics-php.svg?style=shield&circle-token=a20a4b1daa865d164e62ca59a65d6753d286c1c1)](https://app.circleci.com/pipelines/github/segmentio/analytics-php) analytics-php is a php client for [Segment](https://segment.com) From 9022738498f38747c7bf7071c9961fdfdf68eaa6 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 26 May 2021 16:09:19 -0500 Subject: [PATCH 067/151] Issue #172 Initialize return variable before use --- lib/Segment/Consumer/Socket.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Segment/Consumer/Socket.php b/lib/Segment/Consumer/Socket.php index ec7cb52..715e30e 100644 --- a/lib/Segment/Consumer/Socket.php +++ b/lib/Segment/Consumer/Socket.php @@ -97,6 +97,7 @@ private function makeRequest($socket, $req, $retry = true) { $bytes_written = 0; $bytes_total = strlen($req); $closed = false; + $success = true; // Retries with exponential backoff until success $backoff = 100; // Set initial waiting time to 100ms From 0054cad129e5ab636d88b4199f334a695f478687 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 26 May 2021 16:29:41 -0500 Subject: [PATCH 068/151] Issue #176 Update Version and Change log for new release --- HISTORY.md | 9 +++++++++ composer.json | 2 +- lib/Segment/Version.php | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 764e3f8..a9ac83b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,12 @@ +1.8.0 / 2021-05-26 +======================= + + * Fix socket return response (#174) + * API Endpoint update (#168) + * Update Batch Size Check (#168) + * Remove messageID override capabilities (#163) + * Update flush sleep waiting period (#161) + 1.7.0 / 2021-03-10 ======================= diff --git a/composer.json b/composer.json index 39a4e94..8fdd8e0 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "segmentio/analytics-php", - "version": "1.7.0", + "version": "1.8.0", "description": "Segment Analytics PHP Library", "keywords": [ "analytics", diff --git a/lib/Segment/Version.php b/lib/Segment/Version.php index 7b697c6..5e07030 100644 --- a/lib/Segment/Version.php +++ b/lib/Segment/Version.php @@ -1,3 +1,3 @@ Date: Mon, 31 May 2021 15:44:22 -0700 Subject: [PATCH 069/151] Release 1.8.0 --- HISTORY.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index a9ac83b..e4dc517 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,6 @@ -1.8.0 / 2021-05-26 -======================= + +1.8.0 / 2021-05-31 +================== * Fix socket return response (#174) * API Endpoint update (#168) From 50731b4ae27b1bc902e746a3bec360c4fb6260ac Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 8 Jul 2021 15:39:20 -0500 Subject: [PATCH 070/151] Update phpunit config --- phpunit.xml | 51 ++++++++++++++++----------------------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 16b4d0b..f6c06e3 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,36 +1,17 @@ - - - - test - - - - - ./lib - - - - - + + + + + ./lib + + + + + + + + test + + + \ No newline at end of file From 879917414601b56ab1f01caf1d18fb07afc4397d Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 8 Jul 2021 16:04:31 -0500 Subject: [PATCH 071/151] Update assertions within tests --- test/AnalyticsTest.php | 3 ++- test/ConsumerLibCurlTest.php | 2 +- test/ConsumerSocketTest.php | 7 ++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/test/AnalyticsTest.php b/test/AnalyticsTest.php index 4e36d91..7e7570a 100644 --- a/test/AnalyticsTest.php +++ b/test/AnalyticsTest.php @@ -44,8 +44,9 @@ public function testGroupAnonymous() * @expectedException \Exception * @expectedExceptionMessage Segment::group() requires userId or anonymousId */ - public function testGroupNoUser() + public function testGroupNoUser(): void { + $this->expectException(Exception::class); Segment::group(array( "groupId" => "group-id", "traits" => array( diff --git a/test/ConsumerLibCurlTest.php b/test/ConsumerLibCurlTest.php index 4a7331f..c293065 100644 --- a/test/ConsumerLibCurlTest.php +++ b/test/ConsumerLibCurlTest.php @@ -91,7 +91,7 @@ public function testRequestCompression() { $client = new Segment_Client("x", $options); # Should error out with debug on. - $client->track(array("user_id" => "some-user", "event" => "Socket PHP Event")); + $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); $client->__destruct(); } diff --git a/test/ConsumerSocketTest.php b/test/ConsumerSocketTest.php index 8ab8d40..5a79bf8 100644 --- a/test/ConsumerSocketTest.php +++ b/test/ConsumerSocketTest.php @@ -110,7 +110,7 @@ public function testProductionProblems() ); // Shouldn't error out without debug on. - $client->track(array("user_id" => "some-user", "event" => "Production Problems")); + $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Production Problems"))); $client->__destruct(); } @@ -129,7 +129,7 @@ public function testDebugProblems() $client = new Segment_Client("x", $options); // Should error out with debug on. - $client->track(array("user_id" => "some-user", "event" => "Socket PHP Event")); + $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); $client->__destruct(); } @@ -190,6 +190,7 @@ public function testLargeMessageSizeError() */ public function testConnectionError() { + $this->expectException(RuntimeException::class); $client = new Segment_Client("x", array( "consumer" => "socket", "host" => "api.segment.ioooooo", @@ -214,7 +215,7 @@ public function testRequestCompression() { $client = new Segment_Client("x", $options); # Should error out with debug on. - $client->track(array("user_id" => "some-user", "event" => "Socket PHP Event")); + $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); $client->__destruct(); } } From c136f47d56c049e5798829239cc9848fa0691c2b Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 8 Jul 2021 16:07:57 -0500 Subject: [PATCH 072/151] Remove php 7.2 tests which no longer work with the upgraded unit tests --- .circleci/config.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 581b6cb..7cb947c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -43,10 +43,6 @@ jobs: name: 'Running E2E tests' command: '.buildscript/e2e.sh' - test-php7.2: - <<: *multi-test - docker: - - image: circleci/php:7.2-node-browsers test-php7.4: <<: *multi-test docker: @@ -59,6 +55,5 @@ workflows: version: 2 multi-test: jobs: - - test-php7.2 - test-php7.4 - test-php8.0 \ No newline at end of file From 7043d91e5f99fdc167d0a69e1fa4c0668f087add Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 15 Jul 2021 10:17:46 -0500 Subject: [PATCH 073/151] Add Namespaces and refactor; update change log; update min php version requirements --- HISTORY.md | 9 +++++++++ composer.json | 4 ++-- lib/Segment.php | 1 + lib/Segment/Client.php | 18 ++++++++++-------- lib/Segment/Consumer/File.php | 3 ++- lib/Segment/Consumer/ForkCurl.php | 3 ++- lib/Segment/Consumer/LibCurl.php | 3 ++- lib/Segment/Consumer/Socket.php | 3 ++- lib/Segment/QueueConsumer.php | 4 +++- lib/Segment/Version.php | 2 +- test/AnalyticsTest.php | 1 + test/ClientTest.php | 15 ++++++++------- test/ConsumerFileTest.php | 9 +++++---- test/ConsumerForkCurlTest.php | 5 +++-- test/ConsumerLibCurlTest.php | 7 ++++--- test/ConsumerSocketTest.php | 17 +++++++++-------- 16 files changed, 64 insertions(+), 40 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index e4dc517..9cc3cf8 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,13 @@ +2.0.0 / 2021-07-14 +================== + +* Modify Endpoint to match API docs (#171) +* usleep in flush() causes unexpected delays on page loads (#173) +* Support PHP 8 (#152) +* Namespacing (#182) + + 1.8.0 / 2021-05-31 ================== diff --git a/composer.json b/composer.json index 3b0a9bb..eaa777a 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "segmentio/analytics-php", - "version": "1.8.0", + "version": "2.0.0", "description": "Segment Analytics PHP Library", "keywords": [ "analytics", @@ -17,7 +17,7 @@ } ], "require": { - "php": ">=7.2" + "php": ">=7.4" }, "require-dev": { "phpunit/phpunit": "~9.0", diff --git a/lib/Segment.php b/lib/Segment.php index 10c25d9..4e86527 100644 --- a/lib/Segment.php +++ b/lib/Segment.php @@ -1,4 +1,5 @@ "Segment_Consumer_Socket", - "file" => "Segment_Consumer_File", - "fork_curl" => "Segment_Consumer_ForkCurl", - "lib_curl" => "Segment_Consumer_LibCurl" + "socket" => "Segment\Consumer\Socket", + "file" => "Segment\Consumer\File", + "fork_curl" => "Segment\Consumer\ForkCurl", + "lib_curl" => "Segment\Consumer\LibCurl" ); // Use our socket libcurl by default $consumer_type = isset($options["consumer"]) ? $options["consumer"] : "lib_curl"; if (!array_key_exists($consumer_type, $consumers) && class_exists($consumer_type)) { - if (!is_subclass_of($consumer_type, Segment_Consumer::class)) { - throw new Exception('Consumers must extend the Segment_Consumer abstract class'); + if (!is_subclass_of($consumer_type, Consumer::class)) { + throw new Exception('Consumers must extend the Segment/Consumer/Consumer abstract class'); } // Try to resolve it by class name $this->consumer = new $consumer_type($secret, $options); @@ -141,7 +143,7 @@ public function flush() { } /** - * @return Segment_Consumer + * @return Segment\Consumer\Consumer */ public function getConsumer() { return $this->consumer; diff --git a/lib/Segment/Consumer/File.php b/lib/Segment/Consumer/File.php index a0d385e..8ef5461 100644 --- a/lib/Segment/Consumer/File.php +++ b/lib/Segment/Consumer/File.php @@ -1,6 +1,7 @@ assertInstanceOf(Segment_Consumer_LibCurl::class, $client->getConsumer()); + $client = new Segment\Consumer\Client('foobar', []); + $this->assertInstanceOf(Segment\Consumer\LibCurl::class, $client->getConsumer()); } /** @test */ public function can_provide_the_consumer_configuration_as_string() { - $client = new Segment_Client('foobar', [ + $client = new Segment\Consumer\Client('foobar', [ 'consumer' => 'fork_curl', ]); - $this->assertInstanceOf(Segment_Consumer_ForkCurl::class, $client->getConsumer()); + $this->assertInstanceOf(Segment\Consumer\ForkCurl::class, $client->getConsumer()); } /** @test */ public function can_provide_a_class_namespace_as_consumer_configuration() { - $client = new Segment_Client('foobar', [ - 'consumer' => Segment_Consumer_ForkCurl::class, + $client = new Segment\Consumer\Client('foobar', [ + 'consumer' => Segment\Consumer\ForkCurl::class, ]); - $this->assertInstanceOf(Segment_Consumer_ForkCurl::class, $client->getConsumer()); + $this->assertInstanceOf(Segment\Consumer\ForkCurl::class, $client->getConsumer()); } } diff --git a/test/ConsumerFileTest.php b/test/ConsumerFileTest.php index 1a11296..a0c0fe1 100644 --- a/test/ConsumerFileTest.php +++ b/test/ConsumerFileTest.php @@ -1,4 +1,5 @@ filename()); } - $this->client = new Segment_Client( + $this->client = new Segment\Consumer\Client( "oq0vdlg7yi", array( "consumer" => "file", @@ -111,7 +112,7 @@ public function testSend() public function testProductionProblems() { // Open to a place where we should not have write access. - $client = new Segment_Client( + $client = new Segment\Consumer\Client( "oq0vdlg7yi", array( "consumer" => "file", @@ -124,7 +125,7 @@ public function testProductionProblems() } public function testFileSecurityCustom() { - $client = new Segment_Client( + $client = new Segment\Consumer\Client( "testsecret", array( "consumer" => "file", @@ -137,7 +138,7 @@ public function testFileSecurityCustom() { } public function testFileSecurityDefaults() { - $client = new Segment_Client( + $client = new Segment\Consumer\Client( "testsecret", array( "consumer" => "file", diff --git a/test/ConsumerForkCurlTest.php b/test/ConsumerForkCurlTest.php index f915544..12181cb 100644 --- a/test/ConsumerForkCurlTest.php +++ b/test/ConsumerForkCurlTest.php @@ -1,4 +1,5 @@ client = new Segment_Client( + $this->client = new Segment\Consumer\Client( "OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", array( "consumer" => "fork_curl", @@ -87,7 +88,7 @@ public function testRequestCompression() { ); // Create client and send Track message - $client = new Segment_Client("OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", $options); + $client = new Segment\Consumer\Client("OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", $options); $result = $client->track(array( "userId" => "some-user", "event" => "PHP Fork Curl'd\" Event with compression", diff --git a/test/ConsumerLibCurlTest.php b/test/ConsumerLibCurlTest.php index c293065..7adb1e5 100644 --- a/test/ConsumerLibCurlTest.php +++ b/test/ConsumerLibCurlTest.php @@ -1,4 +1,5 @@ client = new Segment_Client( + $this->client = new Segment\Consumer\Client( "oq0vdlg7yi", array( "consumer" => "lib_curl", @@ -88,7 +89,7 @@ public function testRequestCompression() { }, ); - $client = new Segment_Client("x", $options); + $client = new Segment\Consumer\Client("x", $options); # Should error out with debug on. $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); @@ -102,7 +103,7 @@ public function testLargeMessageSizeError() "consumer" => "lib_curl", ); - $client = new Segment_Client("testlargesize", $options); + $client = new Segment\Consumer\Client("testlargesize", $options); $big_property = ""; diff --git a/test/ConsumerSocketTest.php b/test/ConsumerSocketTest.php index 5a79bf8..f4909fd 100644 --- a/test/ConsumerSocketTest.php +++ b/test/ConsumerSocketTest.php @@ -1,4 +1,5 @@ client = new Segment_Client( + $this->client = new Segment\Consumer\Client( "oq0vdlg7yi", array("consumer" => "socket") ); @@ -77,7 +78,7 @@ public function testAlias() public function testShortTimeout() { - $client = new Segment_Client( + $client = new Segment\Consumer\Client( "oq0vdlg7yi", array( "timeout" => 0.01, @@ -100,7 +101,7 @@ public function testShortTimeout() public function testProductionProblems() { - $client = new Segment_Client("x", + $client = new Segment\Consumer\Client("x", array( "consumer" => "socket", "error_handler" => function () { @@ -126,7 +127,7 @@ public function testDebugProblems() }, ); - $client = new Segment_Client("x", $options); + $client = new Segment\Consumer\Client("x", $options); // Should error out with debug on. $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); @@ -140,7 +141,7 @@ public function testLargeMessage() "consumer" => "socket", ); - $client = new Segment_Client("testsecret", $options); + $client = new Segment\Consumer\Client("testsecret", $options); $big_property = ""; @@ -164,7 +165,7 @@ public function testLargeMessageSizeError() "consumer" => "socket", ); - $client = new Segment_Client("testlargesize", $options); + $client = new Segment\Consumer\Client("testlargesize", $options); $big_property = ""; @@ -191,7 +192,7 @@ public function testLargeMessageSizeError() public function testConnectionError() { $this->expectException(RuntimeException::class); - $client = new Segment_Client("x", array( + $client = new Segment\Consumer\Client("x", array( "consumer" => "socket", "host" => "api.segment.ioooooo", "error_handler" => function ($errno, $errmsg) { @@ -212,7 +213,7 @@ public function testRequestCompression() { }, ); - $client = new Segment_Client("x", $options); + $client = new Segment\Consumer\Client("x", $options); # Should error out with debug on. $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); From 0ed61351b61d469ea7ff66418e597da2ff48388d Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 15 Jul 2021 10:20:38 -0500 Subject: [PATCH 074/151] Update pathing error --- lib/Segment/Consumer/File.php | 2 +- lib/Segment/Consumer/ForkCurl.php | 2 +- lib/Segment/Consumer/LibCurl.php | 2 +- lib/Segment/Consumer/Socket.php | 2 +- lib/Segment/QueueConsumer.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Segment/Consumer/File.php b/lib/Segment/Consumer/File.php index 8ef5461..b473e68 100644 --- a/lib/Segment/Consumer/File.php +++ b/lib/Segment/Consumer/File.php @@ -1,7 +1,7 @@ Date: Thu, 15 Jul 2021 11:07:41 -0500 Subject: [PATCH 075/151] Fix multiple namespace issues --- lib/Segment.php | 9 +++++---- lib/Segment/Consumer.php | 5 ++++- send.php | 6 +++--- test/AnalyticsTest.php | 4 ++-- test/ClientTest.php | 14 +++++++------- test/ConsumerFileTest.php | 8 ++++---- test/ConsumerForkCurlTest.php | 4 ++-- test/ConsumerLibCurlTest.php | 6 +++--- test/ConsumerSocketTest.php | 18 +++++++++--------- 9 files changed, 39 insertions(+), 35 deletions(-) diff --git a/lib/Segment.php b/lib/Segment.php index 4e86527..71a8be4 100644 --- a/lib/Segment.php +++ b/lib/Segment.php @@ -1,5 +1,6 @@ getTimestamp() . "." . $dt->format("u")); $payload["timestamp"] = date("c", (int) $ts); $type = $payload["type"]; @@ -86,7 +86,7 @@ $currentBatch = array(); } $payload["timestamp"] = $ts; - call_user_func_array(array("Segment", $type), array($payload)); + call_user_func_array(array("Segment\Segment", $type), array($payload)); } $libCurlResponse = Segment::flush(); diff --git a/test/AnalyticsTest.php b/test/AnalyticsTest.php index f9d267a..2bebad4 100644 --- a/test/AnalyticsTest.php +++ b/test/AnalyticsTest.php @@ -1,5 +1,5 @@ expectException(Exception::class); + $this->expectException( \Exception::class); Segment::group(array( "groupId" => "group-id", "traits" => array( diff --git a/test/ClientTest.php b/test/ClientTest.php index f33ed6b..c2d7487 100644 --- a/test/ClientTest.php +++ b/test/ClientTest.php @@ -8,25 +8,25 @@ class ClientTest extends \PHPUnit\Framework\TestCase /** @test */ public function it_uses_the_lib_curl_consumer_as_default() { - $client = new Segment\Consumer\Client('foobar', []); - $this->assertInstanceOf(Segment\Consumer\LibCurl::class, $client->getConsumer()); + $client = new Client('foobar', []); + $this->assertInstanceOf(LibCurl::class, $client->getConsumer()); } /** @test */ public function can_provide_the_consumer_configuration_as_string() { - $client = new Segment\Consumer\Client('foobar', [ + $client = new Client('foobar', [ 'consumer' => 'fork_curl', ]); - $this->assertInstanceOf(Segment\Consumer\ForkCurl::class, $client->getConsumer()); + $this->assertInstanceOf(ForkCurl::class, $client->getConsumer()); } /** @test */ public function can_provide_a_class_namespace_as_consumer_configuration() { - $client = new Segment\Consumer\Client('foobar', [ - 'consumer' => Segment\Consumer\ForkCurl::class, + $client = new Client('foobar', [ + 'consumer' => ForkCurl::class, ]); - $this->assertInstanceOf(Segment\Consumer\ForkCurl::class, $client->getConsumer()); + $this->assertInstanceOf(ForkCurl::class, $client->getConsumer()); } } diff --git a/test/ConsumerFileTest.php b/test/ConsumerFileTest.php index a0c0fe1..f5c3e9f 100644 --- a/test/ConsumerFileTest.php +++ b/test/ConsumerFileTest.php @@ -15,7 +15,7 @@ public function setUp(): void unlink($this->filename()); } - $this->client = new Segment\Consumer\Client( + $this->client = new Client( "oq0vdlg7yi", array( "consumer" => "file", @@ -112,7 +112,7 @@ public function testSend() public function testProductionProblems() { // Open to a place where we should not have write access. - $client = new Segment\Consumer\Client( + $client = new Client( "oq0vdlg7yi", array( "consumer" => "file", @@ -125,7 +125,7 @@ public function testProductionProblems() } public function testFileSecurityCustom() { - $client = new Segment\Consumer\Client( + $client = new Client( "testsecret", array( "consumer" => "file", @@ -138,7 +138,7 @@ public function testFileSecurityCustom() { } public function testFileSecurityDefaults() { - $client = new Segment\Consumer\Client( + $client = new Client( "testsecret", array( "consumer" => "file", diff --git a/test/ConsumerForkCurlTest.php b/test/ConsumerForkCurlTest.php index 12181cb..c6abb2e 100644 --- a/test/ConsumerForkCurlTest.php +++ b/test/ConsumerForkCurlTest.php @@ -10,7 +10,7 @@ class ConsumerForkCurlTest extends \PHPUnit\Framework\TestCase public function setUp(): void { date_default_timezone_set("UTC"); - $this->client = new Segment\Consumer\Client( + $this->client = new Client( "OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", array( "consumer" => "fork_curl", @@ -88,7 +88,7 @@ public function testRequestCompression() { ); // Create client and send Track message - $client = new Segment\Consumer\Client("OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", $options); + $client = new Client("OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", $options); $result = $client->track(array( "userId" => "some-user", "event" => "PHP Fork Curl'd\" Event with compression", diff --git a/test/ConsumerLibCurlTest.php b/test/ConsumerLibCurlTest.php index 7adb1e5..c386c5a 100644 --- a/test/ConsumerLibCurlTest.php +++ b/test/ConsumerLibCurlTest.php @@ -10,7 +10,7 @@ class ConsumerLibCurlTest extends \PHPUnit\Framework\TestCase public function setUp(): void { date_default_timezone_set("UTC"); - $this->client = new Segment\Consumer\Client( + $this->client = new Client( "oq0vdlg7yi", array( "consumer" => "lib_curl", @@ -89,7 +89,7 @@ public function testRequestCompression() { }, ); - $client = new Segment\Consumer\Client("x", $options); + $client = new Client("x", $options); # Should error out with debug on. $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); @@ -103,7 +103,7 @@ public function testLargeMessageSizeError() "consumer" => "lib_curl", ); - $client = new Segment\Consumer\Client("testlargesize", $options); + $client = new Client("testlargesize", $options); $big_property = ""; diff --git a/test/ConsumerSocketTest.php b/test/ConsumerSocketTest.php index f4909fd..36265dc 100644 --- a/test/ConsumerSocketTest.php +++ b/test/ConsumerSocketTest.php @@ -10,7 +10,7 @@ class ConsumerSocketTest extends \PHPUnit\Framework\TestCase public function setUp(): void { date_default_timezone_set("UTC"); - $this->client = new Segment\Consumer\Client( + $this->client = new Client( "oq0vdlg7yi", array("consumer" => "socket") ); @@ -78,7 +78,7 @@ public function testAlias() public function testShortTimeout() { - $client = new Segment\Consumer\Client( + $client = new Client( "oq0vdlg7yi", array( "timeout" => 0.01, @@ -101,7 +101,7 @@ public function testShortTimeout() public function testProductionProblems() { - $client = new Segment\Consumer\Client("x", + $client = new Client("x", array( "consumer" => "socket", "error_handler" => function () { @@ -127,7 +127,7 @@ public function testDebugProblems() }, ); - $client = new Segment\Consumer\Client("x", $options); + $client = new Client("x", $options); // Should error out with debug on. $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); @@ -141,7 +141,7 @@ public function testLargeMessage() "consumer" => "socket", ); - $client = new Segment\Consumer\Client("testsecret", $options); + $client = new Client("testsecret", $options); $big_property = ""; @@ -165,7 +165,7 @@ public function testLargeMessageSizeError() "consumer" => "socket", ); - $client = new Segment\Consumer\Client("testlargesize", $options); + $client = new Client("testlargesize", $options); $big_property = ""; @@ -191,8 +191,8 @@ public function testLargeMessageSizeError() */ public function testConnectionError() { - $this->expectException(RuntimeException::class); - $client = new Segment\Consumer\Client("x", array( + $this->expectException(\RuntimeException::class); + $client = new Client("x", array( "consumer" => "socket", "host" => "api.segment.ioooooo", "error_handler" => function ($errno, $errmsg) { @@ -213,7 +213,7 @@ public function testRequestCompression() { }, ); - $client = new Segment\Consumer\Client("x", $options); + $client = new Client("x", $options); # Should error out with debug on. $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); From 3617d19237a1edbd8ea9ff98aeb233a90b45c442 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 15 Jul 2021 11:20:45 -0500 Subject: [PATCH 076/151] Update path --- lib/Segment/Consumer/File.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Segment/Consumer/File.php b/lib/Segment/Consumer/File.php index b473e68..983cc9f 100644 --- a/lib/Segment/Consumer/File.php +++ b/lib/Segment/Consumer/File.php @@ -26,7 +26,7 @@ public function __construct($secret, $options = array()) { } else { chmod($options["filename"], 0777); } - } catch (Exception $e) { + } catch (\Exception $e) { $this->handleError($e->getCode(), $e->getMessage()); } } From 7072ba2ce001fa5e4acb8aaed3f243c4fcfcc7a3 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 15 Jul 2021 11:28:03 -0500 Subject: [PATCH 077/151] Update paths --- lib/Segment/Client.php | 2 +- test/ConsumerSocketTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Segment/Client.php b/lib/Segment/Client.php index 356cf65..ba0de25 100644 --- a/lib/Segment/Client.php +++ b/lib/Segment/Client.php @@ -36,7 +36,7 @@ public function __construct($secret, $options = array()) { if (!array_key_exists($consumer_type, $consumers) && class_exists($consumer_type)) { if (!is_subclass_of($consumer_type, Consumer::class)) { - throw new Exception('Consumers must extend the Segment/Consumer/Consumer abstract class'); + throw new \Exception('Consumers must extend the Segment/Consumer/Consumer abstract class'); } // Try to resolve it by class name $this->consumer = new $consumer_type($secret, $options); diff --git a/test/ConsumerSocketTest.php b/test/ConsumerSocketTest.php index 36265dc..1838b1c 100644 --- a/test/ConsumerSocketTest.php +++ b/test/ConsumerSocketTest.php @@ -105,7 +105,7 @@ public function testProductionProblems() array( "consumer" => "socket", "error_handler" => function () { - throw new Exception("Was called"); + throw new \Exception("Was called"); }, ) ); @@ -122,7 +122,7 @@ public function testDebugProblems() "consumer" => "socket", "error_handler" => function ($errno, $errmsg) { if (400 != $errno) { - throw new Exception("Response is not 400"); + throw new \Exception("Response is not 400"); } }, ); From 5d4ca62d3a0a410af2fc3dae8d860937201707c9 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 15 Jul 2021 12:18:46 -0500 Subject: [PATCH 078/151] Update Changelog --- HISTORY.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 9cc3cf8..c9ff49b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -2,10 +2,11 @@ 2.0.0 / 2021-07-14 ================== -* Modify Endpoint to match API docs (#171) -* usleep in flush() causes unexpected delays on page loads (#173) -* Support PHP 8 (#152) -* Namespacing (#182) + * Modify Endpoint to match API docs (#171) + * usleep in flush() causes unexpected delays on page loads (#173) + * Support PHP 8 (#152) + * Remove Support for PHP 7.2 + * Namespacing (#182) 1.8.0 / 2021-05-31 From 0048caedf87b9019f2588aadd1d8dd7a6d9c5abb Mon Sep 17 00:00:00 2001 From: Pooya Jaferian Date: Fri, 16 Jul 2021 09:29:21 -0700 Subject: [PATCH 079/151] Release 2.0.0 --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index c9ff49b..14fe3bb 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,5 @@ -2.0.0 / 2021-07-14 +2.0.0 / 2021-07-16 ================== * Modify Endpoint to match API docs (#171) From 1d80b50a2ce389a26a218cba74f40e29b80ff3c1 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Thu, 23 Sep 2021 18:27:41 +0200 Subject: [PATCH 080/151] Switch to PSR12 and reformat with phpcbf --- lib/Segment.php | 146 +++++----- lib/Segment/Client.php | 264 +++++++++-------- lib/Segment/Consumer.php | 61 ++-- lib/Segment/Consumer/File.php | 122 ++++---- lib/Segment/Consumer/ForkCurl.php | 159 ++++++----- lib/Segment/Consumer/LibCurl.php | 183 ++++++------ lib/Segment/Consumer/Socket.php | 349 ++++++++++++----------- lib/Segment/QueueConsumer.php | 260 +++++++++-------- lib/Segment/Version.php | 1 + phpcs.xml | 32 +-- test/AnalyticsTest.php | 455 +++++++++++++++--------------- test/ClientTest.php | 5 +- test/ConsumerFileTest.php | 301 ++++++++++---------- test/ConsumerForkCurlTest.php | 156 +++++----- test/ConsumerLibCurlTest.php | 224 +++++++-------- test/ConsumerSocketTest.php | 383 ++++++++++++------------- 16 files changed, 1582 insertions(+), 1519 deletions(-) diff --git a/lib/Segment.php b/lib/Segment.php index 71a8be4..1577c66 100644 --- a/lib/Segment.php +++ b/lib/Segment.php @@ -1,21 +1,25 @@ track($message); - } + public static function track(array $message) + { + self::checkClient(); + $event = !empty($message["event"]); + self::assert($event, "Segment::track() expects an event"); + self::validate($message, "track"); + + return self::$client->track($message); + } /** * Tags traits about the user. @@ -38,13 +43,14 @@ public static function track(array $message) { * @param array $message * @return boolean whether the identify call succeeded */ - public static function identify(array $message) { - self::checkClient(); - $message["type"] = "identify"; - self::validate($message, "identify"); + public static function identify(array $message) + { + self::checkClient(); + $message["type"] = "identify"; + self::validate($message, "identify"); - return self::$client->identify($message); - } + return self::$client->identify($message); + } /** * Tags traits about the group. @@ -52,14 +58,15 @@ public static function identify(array $message) { * @param array $message * @return boolean whether the group call succeeded */ - public static function group(array $message) { - self::checkClient(); - $groupId = !empty($message['groupId']); - self::assert($groupId, "Segment::group() expects a groupId"); - self::validate($message, "group"); - - return self::$client->group($message); - } + public static function group(array $message) + { + self::checkClient(); + $groupId = !empty($message['groupId']); + self::assert($groupId, "Segment::group() expects a groupId"); + self::validate($message, "group"); + + return self::$client->group($message); + } /** * Tracks a page view @@ -67,12 +74,13 @@ public static function group(array $message) { * @param array $message * @return boolean whether the page call succeeded */ - public static function page(array $message) { - self::checkClient(); - self::validate($message, "page"); + public static function page(array $message) + { + self::checkClient(); + self::validate($message, "page"); - return self::$client->page($message); - } + return self::$client->page($message); + } /** * Tracks a screen view @@ -80,12 +88,13 @@ public static function page(array $message) { * @param array $message * @return boolean whether the screen call succeeded */ - public static function screen(array $message) { - self::checkClient(); - self::validate($message, "screen"); + public static function screen(array $message) + { + self::checkClient(); + self::validate($message, "screen"); - return self::$client->screen($message); - } + return self::$client->screen($message); + } /** * Aliases the user id from a temporary id to a permanent one @@ -93,14 +102,15 @@ public static function screen(array $message) { * @param array $from user id to alias from * @return boolean whether the alias call succeeded */ - public static function alias(array $message) { - self::checkClient(); - $userId = (array_key_exists('userId', $message) && strlen((string) $message['userId']) > 0); - $previousId = (array_key_exists('previousId', $message) && strlen((string) $message['previousId']) > 0); - self::assert($userId && $previousId, "Segment::alias() requires both userId and previousId"); - - return self::$client->alias($message); - } + public static function alias(array $message) + { + self::checkClient(); + $userId = (array_key_exists('userId', $message) && strlen((string) $message['userId']) > 0); + $previousId = (array_key_exists('previousId', $message) && strlen((string) $message['previousId']) > 0); + self::assert($userId && $previousId, "Segment::alias() requires both userId and previousId"); + + return self::$client->alias($message); + } /** * Validate common properties. @@ -108,34 +118,37 @@ public static function alias(array $message) { * @param array $message * @param string $type */ - public static function validate($message, $type){ - $userId = (array_key_exists('userId', $message) && strlen((string) $message['userId']) > 0); - $anonId = !empty($message['anonymousId']); - self::assert($userId || $anonId, "Segment::${type}() requires userId or anonymousId"); - } + public static function validate($message, $type) + { + $userId = (array_key_exists('userId', $message) && strlen((string) $message['userId']) > 0); + $anonId = !empty($message['anonymousId']); + self::assert($userId || $anonId, "Segment::${type}() requires userId or anonymousId"); + } /** * Flush the client */ - public static function flush(){ - self::checkClient(); + public static function flush() + { + self::checkClient(); - return self::$client->flush(); - } + return self::$client->flush(); + } /** * Check the client. * * @throws Exception */ - private static function checkClient(){ - if (null != self::$client) { - return; - } + private static function checkClient() + { + if (null != self::$client) { + return; + } - throw new \Exception("Segment::init() must be called before any other tracking method."); - } + throw new \Exception("Segment::init() must be called before any other tracking method."); + } /** * Assert `value` or throw. @@ -144,13 +157,14 @@ private static function checkClient(){ * @param string $msg * @throws Exception */ - private static function assert($value, $msg) { - if (!$value) { - throw new \Exception($msg); + private static function assert($value, $msg) + { + if (!$value) { + throw new \Exception($msg); + } } - } } if (!function_exists('json_encode')) { - throw new \Exception('Segment needs the JSON PHP extension.'); + throw new \Exception('Segment needs the JSON PHP extension.'); } diff --git a/lib/Segment/Client.php b/lib/Segment/Client.php index ba0de25..2c3d88f 100644 --- a/lib/Segment/Client.php +++ b/lib/Segment/Client.php @@ -10,8 +10,9 @@ require_once(__DIR__ . '/Consumer/Socket.php'); require_once(__DIR__ . '/Version.php'); -class Client { - protected $consumer; +class Client +{ + protected $consumer; /** * Create a new analytics object with your app's secret @@ -22,35 +23,37 @@ class Client { * @param string Consumer constructor to use, libcurl by default. * */ - public function __construct($secret, $options = array()) { + public function __construct($secret, $options = array()) + { - $consumers = array( + $consumers = array( "socket" => "Segment\Consumer\Socket", "file" => "Segment\Consumer\File", "fork_curl" => "Segment\Consumer\ForkCurl", "lib_curl" => "Segment\Consumer\LibCurl" - ); - // Use our socket libcurl by default - $consumer_type = isset($options["consumer"]) ? $options["consumer"] : + ); + // Use our socket libcurl by default + $consumer_type = isset($options["consumer"]) ? $options["consumer"] : "lib_curl"; - if (!array_key_exists($consumer_type, $consumers) && class_exists($consumer_type)) { - if (!is_subclass_of($consumer_type, Consumer::class)) { - throw new \Exception('Consumers must extend the Segment/Consumer/Consumer abstract class'); + if (!array_key_exists($consumer_type, $consumers) && class_exists($consumer_type)) { + if (!is_subclass_of($consumer_type, Consumer::class)) { + throw new \Exception('Consumers must extend the Segment/Consumer/Consumer abstract class'); + } + // Try to resolve it by class name + $this->consumer = new $consumer_type($secret, $options); + return; } - // Try to resolve it by class name - $this->consumer = new $consumer_type($secret, $options); - return; - } - $Consumer = $consumers[$consumer_type]; + $Consumer = $consumers[$consumer_type]; - $this->consumer = new $Consumer($secret, $options); - } + $this->consumer = new $Consumer($secret, $options); + } - public function __destruct() { - $this->consumer->__destruct(); - } + public function __destruct() + { + $this->consumer->__destruct(); + } /** * Tracks a user action @@ -58,12 +61,13 @@ public function __destruct() { * @param array $message * @return [boolean] whether the track call succeeded */ - public function track(array $message) { - $message = $this->message($message, "properties"); - $message["type"] = "track"; + public function track(array $message) + { + $message = $this->message($message, "properties"); + $message["type"] = "track"; - return $this->consumer->track($message); - } + return $this->consumer->track($message); + } /** * Tags traits about the user. @@ -71,12 +75,13 @@ public function track(array $message) { * @param [array] $message * @return [boolean] whether the track call succeeded */ - public function identify(array $message) { - $message = $this->message($message, "traits"); - $message["type"] = "identify"; + public function identify(array $message) + { + $message = $this->message($message, "traits"); + $message["type"] = "identify"; - return $this->consumer->identify($message); - } + return $this->consumer->identify($message); + } /** * Tags traits about the group. @@ -84,12 +89,13 @@ public function identify(array $message) { * @param [array] $message * @return [boolean] whether the group call succeeded */ - public function group(array $message) { - $message = $this->message($message, "traits"); - $message["type"] = "group"; + public function group(array $message) + { + $message = $this->message($message, "traits"); + $message["type"] = "group"; - return $this->consumer->group($message); - } + return $this->consumer->group($message); + } /** * Tracks a page view. @@ -97,12 +103,13 @@ public function group(array $message) { * @param [array] $message * @return [boolean] whether the page call succeeded */ - public function page(array $message) { - $message = $this->message($message, "properties"); - $message["type"] = "page"; + public function page(array $message) + { + $message = $this->message($message, "properties"); + $message["type"] = "page"; - return $this->consumer->page($message); - } + return $this->consumer->page($message); + } /** * Tracks a screen view. @@ -110,12 +117,13 @@ public function page(array $message) { * @param [array] $message * @return [boolean] whether the screen call succeeded */ - public function screen(array $message) { - $message = $this->message($message, "properties"); - $message["type"] = "screen"; + public function screen(array $message) + { + $message = $this->message($message, "properties"); + $message["type"] = "screen"; - return $this->consumer->screen($message); - } + return $this->consumer->screen($message); + } /** * Aliases from one user id to another @@ -123,30 +131,33 @@ public function screen(array $message) { * @param array $message * @return boolean whether the alias call succeeded */ - public function alias(array $message) { - $message = $this->message($message); - $message["type"] = "alias"; + public function alias(array $message) + { + $message = $this->message($message); + $message["type"] = "alias"; - return $this->consumer->alias($message); - } + return $this->consumer->alias($message); + } /** * Flush any async consumers * @return boolean true if flushed successfully */ - public function flush() { - if (method_exists($this->consumer, 'flush')) { - return $this->consumer->flush(); - } + public function flush() + { + if (method_exists($this->consumer, 'flush')) { + return $this->consumer->flush(); + } - return true; - } + return true; + } /** * @return Segment\Consumer\Consumer */ - public function getConsumer() { - return $this->consumer; + public function getConsumer() + { + return $this->consumer; } @@ -163,37 +174,38 @@ public function getConsumer() { * * @param ts $timestamp - time in seconds (time()) */ - private function formatTime($ts) { - // time() - if (null == $ts || !$ts) { - $ts = time(); - } - if (false !== filter_var($ts, FILTER_VALIDATE_INT)) { - return date("c", (int) $ts); - } + private function formatTime($ts) + { + // time() + if (null == $ts || !$ts) { + $ts = time(); + } + if (false !== filter_var($ts, FILTER_VALIDATE_INT)) { + return date("c", (int) $ts); + } - // anything else try to strtotime the date. - if (false === filter_var($ts, FILTER_VALIDATE_FLOAT)) { - if (is_string($ts)) { - return date("c", strtotime($ts)); - } - - return date("c"); - } + // anything else try to strtotime the date. + if (false === filter_var($ts, FILTER_VALIDATE_FLOAT)) { + if (is_string($ts)) { + return date("c", strtotime($ts)); + } - // fix for floatval casting in send.php - $parts = explode(".", (string)$ts); - if (!isset($parts[1])) { - return date("c", (int)$parts[0]); - } + return date("c"); + } - // microtime(true) - $sec = $parts[0]; - $usec = $parts[1]; - $fmt = sprintf("Y-m-d\TH:i:s.%sP", $usec); + // fix for floatval casting in send.php + $parts = explode(".", (string)$ts); + if (!isset($parts[1])) { + return date("c", (int)$parts[0]); + } - return date($fmt, (int)$sec); - } + // microtime(true) + $sec = $parts[0]; + $usec = $parts[1]; + $fmt = sprintf("Y-m-d\TH:i:s.%sP", $usec); + + return date($fmt, (int)$sec); + } /** * Add common fields to the given `message` @@ -203,30 +215,31 @@ private function formatTime($ts) { * @return array */ - private function message($msg, $def = ""){ - if ($def && !isset($msg[$def])) { - $msg[$def] = array(); - } - if ($def && empty($msg[$def])) { - $msg[$def] = (object)$msg[$def]; - } + private function message($msg, $def = "") + { + if ($def && !isset($msg[$def])) { + $msg[$def] = array(); + } + if ($def && empty($msg[$def])) { + $msg[$def] = (object)$msg[$def]; + } - if (!isset($msg["context"])) { - $msg["context"] = array(); - } - $msg["context"] = array_merge($this->getDefaultContext(), $msg["context"]); + if (!isset($msg["context"])) { + $msg["context"] = array(); + } + $msg["context"] = array_merge($this->getDefaultContext(), $msg["context"]); - if (!isset($msg["timestamp"])) { - $msg["timestamp"] = null; - } - $msg["timestamp"] = $this->formatTime($msg["timestamp"]); + if (!isset($msg["timestamp"])) { + $msg["timestamp"] = null; + } + $msg["timestamp"] = $this->formatTime($msg["timestamp"]); - if (!isset($msg["messageId"])) { - $msg["messageId"] = self::messageId(); - } + if (!isset($msg["messageId"])) { + $msg["messageId"] = self::messageId(); + } - return $msg; - } + return $msg; + } /** * Generate a random messageId. @@ -236,32 +249,35 @@ private function message($msg, $def = ""){ * @return string */ - private static function messageId(){ - return sprintf("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", - mt_rand(0, 0xffff), - mt_rand(0, 0xffff), - mt_rand(0, 0xffff), - mt_rand(0, 0x0fff) | 0x4000, - mt_rand(0, 0x3fff) | 0x8000, - mt_rand(0, 0xffff), - mt_rand(0, 0xffff), - mt_rand(0, 0xffff) - ); - } + private static function messageId() + { + return sprintf( + "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0x0fff) | 0x4000, + mt_rand(0, 0x3fff) | 0x8000, + mt_rand(0, 0xffff), + mt_rand(0, 0xffff), + mt_rand(0, 0xffff) + ); + } /** * Add the segment.io context to the request * @return array additional context */ - private function getDefaultContext() { - global $SEGMENT_VERSION; + private function getDefaultContext() + { + global $SEGMENT_VERSION; - return array( - "library" => array( + return array( + "library" => array( "name" => "analytics-php", "version" => $SEGMENT_VERSION, "consumer" => $this->consumer->getConsumer() - ) - ); - } + ) + ); + } } diff --git a/lib/Segment/Consumer.php b/lib/Segment/Consumer.php index 8a3ffab..90bfbb0 100644 --- a/lib/Segment/Consumer.php +++ b/lib/Segment/Consumer.php @@ -2,21 +2,23 @@ namespace Segment\Consumer; -abstract class Consumer { - protected $type = "Consumer"; +abstract class Consumer +{ + protected $type = "Consumer"; - protected $options; - protected $secret; + protected $options; + protected $secret; /** * Store our secret and options as part of this consumer * @param string $secret * @param array $options */ - public function __construct($secret, $options = array()) { - $this->secret = $secret; - $this->options = $options; - } + public function __construct($secret, $options = array()) + { + $this->secret = $secret; + $this->options = $options; + } /** * Tracks a user action @@ -24,7 +26,7 @@ public function __construct($secret, $options = array()) { * @param array $message * @return boolean whether the track call succeeded */ - abstract public function track(array $message); + abstract public function track(array $message); /** * Tags traits about the user. @@ -32,7 +34,7 @@ abstract public function track(array $message); * @param array $message * @return boolean whether the identify call succeeded */ - abstract public function identify(array $message); + abstract public function identify(array $message); /** * Tags traits about the group. @@ -40,7 +42,7 @@ abstract public function identify(array $message); * @param array $message * @return boolean whether the group call succeeded */ - abstract public function group(array $message); + abstract public function group(array $message); /** * Tracks a page view. @@ -48,7 +50,7 @@ abstract public function group(array $message); * @param array $message * @return boolean whether the page call succeeded */ - abstract public function page(array $message); + abstract public function page(array $message); /** * Tracks a screen view. @@ -56,7 +58,7 @@ abstract public function page(array $message); * @param array $message * @return boolean whether the group call succeeded */ - abstract public function screen(array $message); + abstract public function screen(array $message); /** * Aliases from one user id to another @@ -64,15 +66,16 @@ abstract public function screen(array $message); * @param array $message * @return boolean whether the alias call succeeded */ - abstract public function alias(array $message); + abstract public function alias(array $message); /** * Check whether debug mode is enabled * @return boolean */ - protected function debug() { - return isset($this->options["debug"]) ? $this->options["debug"] : false; - } + protected function debug() + { + return isset($this->options["debug"]) ? $this->options["debug"] : false; + } /** * Check whether we should connect to the API using SSL. This is enabled by @@ -80,9 +83,10 @@ protected function debug() { * which can save on round-trip times, you may disable it. * @return boolean */ - protected function ssl() { - return isset($this->options["ssl"]) ? $this->options["ssl"] : true; - } + protected function ssl() + { + return isset($this->options["ssl"]) ? $this->options["ssl"] : true; + } /** * On an error, try and call the error handler, if debugging output to @@ -90,14 +94,15 @@ protected function ssl() { * @param string $code * @param string $msg */ - protected function handleError($code, $msg) { - if (isset($this->options['error_handler'])) { - $handler = $this->options['error_handler']; - $handler($code, $msg); - } + protected function handleError($code, $msg) + { + if (isset($this->options['error_handler'])) { + $handler = $this->options['error_handler']; + $handler($code, $msg); + } - if ($this->debug()) { - error_log("[Analytics][" . $this->type . "] " . $msg); + if ($this->debug()) { + error_log("[Analytics][" . $this->type . "] " . $msg); + } } - } } diff --git a/lib/Segment/Consumer/File.php b/lib/Segment/Consumer/File.php index 983cc9f..491b6f6 100644 --- a/lib/Segment/Consumer/File.php +++ b/lib/Segment/Consumer/File.php @@ -1,10 +1,12 @@ file_handle = fopen($options["filename"], "a"); - if (isset($options["filepermissions"])) { - chmod($options["filename"], $options["filepermissions"]); - } else { - chmod($options["filename"], 0777); - } - } catch (\Exception $e) { - $this->handleError($e->getCode(), $e->getMessage()); + public function __construct($secret, $options = array()) + { + if (!isset($options["filename"])) { + $options["filename"] = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "analytics.log"; + } + + parent::__construct($secret, $options); + + try { + $this->file_handle = fopen($options["filename"], "a"); + if (isset($options["filepermissions"])) { + chmod($options["filename"], $options["filepermissions"]); + } else { + chmod($options["filename"], 0777); + } + } catch (\Exception $e) { + $this->handleError($e->getCode(), $e->getMessage()); + } } - } - public function __destruct() { - if ($this->file_handle && - "Unknown" != get_resource_type($this->file_handle)) { - fclose($this->file_handle); + public function __destruct() + { + if ( + $this->file_handle && + "Unknown" != get_resource_type($this->file_handle) + ) { + fclose($this->file_handle); + } } - } //define getter method for consumer type - public function getConsumer() { - return $this->type; - } + public function getConsumer() + { + return $this->type; + } /** * Tracks a user action @@ -49,9 +56,10 @@ public function getConsumer() { * @param array $message * @return [boolean] whether the track call succeeded */ - public function track(array $message) { - return $this->write($message); - } + public function track(array $message) + { + return $this->write($message); + } /** * Tags traits about the user. @@ -59,9 +67,10 @@ public function track(array $message) { * @param array $message * @return [boolean] whether the identify call succeeded */ - public function identify(array $message) { - return $this->write($message); - } + public function identify(array $message) + { + return $this->write($message); + } /** * Tags traits about the group. @@ -69,9 +78,10 @@ public function identify(array $message) { * @param array $message * @return [boolean] whether the group call succeeded */ - public function group(array $message) { - return $this->write($message); - } + public function group(array $message) + { + return $this->write($message); + } /** * Tracks a page view. @@ -79,9 +89,10 @@ public function group(array $message) { * @param array $message * @return [boolean] whether the page call succeeded */ - public function page(array $message) { - return $this->write($message); - } + public function page(array $message) + { + return $this->write($message); + } /** * Tracks a screen view. @@ -89,9 +100,10 @@ public function page(array $message) { * @param array $message * @return [boolean] whether the screen call succeeded */ - public function screen(array $message) { - return $this->write($message); - } + public function screen(array $message) + { + return $this->write($message); + } /** * Aliases from one user id to another @@ -99,23 +111,25 @@ public function screen(array $message) { * @param array $message * @return boolean whether the alias call succeeded */ - public function alias(array $message) { - return $this->write($message); - } + public function alias(array $message) + { + return $this->write($message); + } /** * Writes the API call to a file as line-delimited json * @param [array] $body post body content. * @return [boolean] whether the request succeeded */ - private function write($body) { - if (!$this->file_handle) { - return false; - } + private function write($body) + { + if (!$this->file_handle) { + return false; + } - $content = json_encode($body); - $content.= "\n"; + $content = json_encode($body); + $content .= "\n"; - return fwrite($this->file_handle, $content) == strlen($content); - } + return fwrite($this->file_handle, $content) == strlen($content); + } } diff --git a/lib/Segment/Consumer/ForkCurl.php b/lib/Segment/Consumer/ForkCurl.php index 2a74776..20f3eef 100644 --- a/lib/Segment/Consumer/ForkCurl.php +++ b/lib/Segment/Consumer/ForkCurl.php @@ -1,8 +1,10 @@ type; - } + public function getConsumer() + { + return $this->type; + } /** * Make an async request to our API. Fork a curl process, immediately send @@ -28,74 +32,75 @@ public function getConsumer() { * @param array $messages array of all the messages to send * @return boolean whether the request succeeded */ - public function flushBatch($messages) { - $body = $this->payload($messages); - $payload = json_encode($body); - - // Escape for shell usage. - $payload = escapeshellarg($payload); - $secret = escapeshellarg($this->secret); - - $protocol = $this->ssl() ? "https://" : "http://"; - if ($this->host) { - $host = $this->host; - } else { - $host = "api.segment.io"; - } - $path = "/v1/batch"; - $url = $protocol . $host . $path; - - $cmd = "curl -u ${secret}: -X POST -H 'Content-Type: application/json'"; - - $tmpfname = ""; - if ($this->compress_request) { - // Compress request to file - $tmpfname = tempnam("/tmp", "forkcurl_"); - $cmd2 = "echo " . $payload . " | gzip > " . $tmpfname; - exec($cmd2, $output, $exit); - - if (0 != $exit) { - $this->handleError($exit, $output); - return false; - } - - $cmd.= " -H 'Content-Encoding: gzip'"; - - $cmd.= " --data-binary '@" . $tmpfname . "'"; - } else { - $cmd.= " -d " . $payload; - } - - $cmd.= " '" . $url . "'"; - - // Verify message size is below than 32KB - if (strlen($payload) >= 32 * 1024) { - $msg = "Message size is larger than 32KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); - - return false; - } - - // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. - $library = $messages[0]['context']['library']; - $libName = $library['name']; - $libVersion = $library['version']; - $cmd.= " -H 'User-Agent: ${libName}/${libVersion}'"; - - if (!$this->debug()) { - $cmd .= " > /dev/null 2>&1 &"; - } - - exec($cmd, $output, $exit); - - if (0 != $exit) { - $this->handleError($exit, $output); - } - - if ($tmpfname != "") { - unlink($tmpfname); + public function flushBatch($messages) + { + $body = $this->payload($messages); + $payload = json_encode($body); + + // Escape for shell usage. + $payload = escapeshellarg($payload); + $secret = escapeshellarg($this->secret); + + $protocol = $this->ssl() ? "https://" : "http://"; + if ($this->host) { + $host = $this->host; + } else { + $host = "api.segment.io"; + } + $path = "/v1/batch"; + $url = $protocol . $host . $path; + + $cmd = "curl -u ${secret}: -X POST -H 'Content-Type: application/json'"; + + $tmpfname = ""; + if ($this->compress_request) { + // Compress request to file + $tmpfname = tempnam("/tmp", "forkcurl_"); + $cmd2 = "echo " . $payload . " | gzip > " . $tmpfname; + exec($cmd2, $output, $exit); + + if (0 != $exit) { + $this->handleError($exit, $output); + return false; + } + + $cmd .= " -H 'Content-Encoding: gzip'"; + + $cmd .= " --data-binary '@" . $tmpfname . "'"; + } else { + $cmd .= " -d " . $payload; + } + + $cmd .= " '" . $url . "'"; + + // Verify message size is below than 32KB + if (strlen($payload) >= 32 * 1024) { + $msg = "Message size is larger than 32KB"; + error_log("[Analytics][" . $this->type . "] " . $msg); + + return false; + } + + // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. + $library = $messages[0]['context']['library']; + $libName = $library['name']; + $libVersion = $library['version']; + $cmd .= " -H 'User-Agent: ${libName}/${libVersion}'"; + + if (!$this->debug()) { + $cmd .= " > /dev/null 2>&1 &"; + } + + exec($cmd, $output, $exit); + + if (0 != $exit) { + $this->handleError($exit, $output); + } + + if ($tmpfname != "") { + unlink($tmpfname); + } + + return 0 == $exit; } - - return 0 == $exit; - } } diff --git a/lib/Segment/Consumer/LibCurl.php b/lib/Segment/Consumer/LibCurl.php index f25e51a..7291354 100644 --- a/lib/Segment/Consumer/LibCurl.php +++ b/lib/Segment/Consumer/LibCurl.php @@ -1,8 +1,10 @@ type; - } + public function getConsumer() + { + return $this->type; + } /** * Make a sync request to our API. If debug is @@ -28,88 +32,89 @@ public function getConsumer() { * @param array $messages array of all the messages to send * @return boolean whether the request succeeded */ - public function flushBatch($messages) { - $body = $this->payload($messages); - $payload = json_encode($body); - $secret = $this->secret; - - if ($this->compress_request) { - $payload = gzencode($payload); - } - - $protocol = $this->ssl() ? "https://" : "http://"; - if ($this->host) { - $host = $this->host; - } else { - $host = "api.segment.io"; - } - $path = "/v1/batch"; - $url = $protocol . $host . $path; - - $backoff = 100; // Set initial waiting time to 100ms - - while ($backoff < $this->maximum_backoff_duration) { - $start_time = microtime(true); - - // open connection - $ch = curl_init(); - - // set the url, number of POST vars, POST data - curl_setopt($ch, CURLOPT_USERPWD, $secret . ':'); - curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); - - // set variables for headers - $header = array(); - $header[] = 'Content-Type: application/json'; - - if ($this->compress_request) { - $header[] = 'Content-Encoding: gzip'; - } - - // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. - $library = $messages[0]['context']['library']; - $libName = $library['name']; - $libVersion = $library['version']; - $header[] = "User-Agent: ${libName}/${libVersion}"; - - curl_setopt($ch, CURLOPT_HTTPHEADER, $header); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // retry failed requests just once to diminish impact on performance - $responseContent = curl_exec($ch); - - $err = curl_error($ch); - if ($err) { - $this->handleError(curl_errno($ch), $err); - return; - } - - $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - - //close connection - curl_close($ch); - - $elapsed_time = microtime(true) - $start_time; - - if (200 != $responseCode) { - // log error - $this->handleError($responseCode, $responseContent); + public function flushBatch($messages) + { + $body = $this->payload($messages); + $payload = json_encode($body); + $secret = $this->secret; + + if ($this->compress_request) { + $payload = gzencode($payload); + } - if (($responseCode >= 500 && $responseCode <= 600) || 429 == $responseCode) { - // If status code is greater than 500 and less than 600, it indicates server error - // Error code 429 indicates rate limited. - // Retry uploading in these cases. - usleep($backoff * 1000); - $backoff *= 2; - } elseif ($responseCode >= 400) { - break; + $protocol = $this->ssl() ? "https://" : "http://"; + if ($this->host) { + $host = $this->host; + } else { + $host = "api.segment.io"; + } + $path = "/v1/batch"; + $url = $protocol . $host . $path; + + $backoff = 100; // Set initial waiting time to 100ms + + while ($backoff < $this->maximum_backoff_duration) { + $start_time = microtime(true); + + // open connection + $ch = curl_init(); + + // set the url, number of POST vars, POST data + curl_setopt($ch, CURLOPT_USERPWD, $secret . ':'); + curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); + + // set variables for headers + $header = array(); + $header[] = 'Content-Type: application/json'; + + if ($this->compress_request) { + $header[] = 'Content-Encoding: gzip'; + } + + // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. + $library = $messages[0]['context']['library']; + $libName = $library['name']; + $libVersion = $library['version']; + $header[] = "User-Agent: ${libName}/${libVersion}"; + + curl_setopt($ch, CURLOPT_HTTPHEADER, $header); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + // retry failed requests just once to diminish impact on performance + $responseContent = curl_exec($ch); + + $err = curl_error($ch); + if ($err) { + $this->handleError(curl_errno($ch), $err); + return; + } + + $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + //close connection + curl_close($ch); + + $elapsed_time = microtime(true) - $start_time; + + if (200 != $responseCode) { + // log error + $this->handleError($responseCode, $responseContent); + + if (($responseCode >= 500 && $responseCode <= 600) || 429 == $responseCode) { + // If status code is greater than 500 and less than 600, it indicates server error + // Error code 429 indicates rate limited. + // Retry uploading in these cases. + usleep($backoff * 1000); + $backoff *= 2; + } elseif ($responseCode >= 400) { + break; + } + } else { + break; // no error + } } - } else { - break; // no error - } - } - return $responseCode; - } + return $responseCode; + } } diff --git a/lib/Segment/Consumer/Socket.php b/lib/Segment/Consumer/Socket.php index 75f0283..ffef645 100644 --- a/lib/Segment/Consumer/Socket.php +++ b/lib/Segment/Consumer/Socket.php @@ -1,9 +1,11 @@ type; - } + public function getConsumer() + { + return $this->type; + } - public function flushBatch($batch) { - $socket = $this->createSocket(); + public function flushBatch($batch) + { + $socket = $this->createSocket(); - if (!$socket) { - return; - } + if (!$socket) { + return; + } - $payload = $this->payload($batch); - $payload = json_encode($payload); + $payload = $this->payload($batch); + $payload = json_encode($payload); - $body = $this->createBody($this->options["host"], $payload); - if (false === $body) { - return false; + $body = $this->createBody($this->options["host"], $payload); + if (false === $body) { + return false; + } + + return $this->makeRequest($socket, $body); } - return $this->makeRequest($socket, $body); - } + private function createSocket() + { + if ($this->socket_failed) { + return false; + } - private function createSocket() { - if ($this->socket_failed) { - return false; - } + $protocol = $this->ssl() ? "ssl" : "tcp"; + $host = $this->options["host"]; + $port = $this->ssl() ? 443 : 80; + $timeout = $this->options["timeout"]; + + try { + // Open our socket to the API Server. + // Since we're try catch'ing prevent PHP logs. + $socket = @pfsockopen( + $protocol . "://" . $host, + $port, + $errno, + $errstr, + $timeout + ); + + // If we couldn't open the socket, handle the error. + if (false === $socket) { + $this->handleError($errno, $errstr); + $this->socket_failed = true; + + return false; + } + + return $socket; + } catch (Exception $e) { + $this->handleError($e->getCode(), $e->getMessage()); + $this->socket_failed = true; - $protocol = $this->ssl() ? "ssl" : "tcp"; - $host = $this->options["host"]; - $port = $this->ssl() ? 443 : 80; - $timeout = $this->options["timeout"]; - - try { - // Open our socket to the API Server. - // Since we're try catch'ing prevent PHP logs. - $socket = @pfsockopen( - $protocol . "://" . $host, - $port, - $errno, - $errstr, - $timeout - ); - - // If we couldn't open the socket, handle the error. - if (false === $socket) { - $this->handleError($errno, $errstr); - $this->socket_failed = true; - - return false; - } - - return $socket; - } catch (Exception $e) { - $this->handleError($e->getCode(), $e->getMessage()); - $this->socket_failed = true; - - return false; + return false; + } } - } /** * Attempt to write the request to the socket, wait for response if debug @@ -94,115 +100,117 @@ private function createSocket() { * @param boolean $retry * @return boolean $success */ - private function makeRequest($socket, $req, $retry = true) { - $bytes_written = 0; - $bytes_total = strlen($req); - $closed = false; - $success = true; - - // Retries with exponential backoff until success - $backoff = 100; // Set initial waiting time to 100ms - - while (true) { - // Send request to server - while (!$closed && $bytes_written < $bytes_total) { - try { - // Since we're try catch'ing prevent PHP logs. - $written = @fwrite($socket, substr($req, $bytes_written)); - } catch (Exception $e) { - $this->handleError($e->getCode(), $e->getMessage()); - $closed = true; - } - if (!isset($written) || !$written) { - $closed = true; - } else { - $bytes_written += $written; - } - } - - // Get response for request - $statusCode = 0; - $errorMessage = ""; - - if (!$closed) { - $res = $this->parseResponse(fread($socket, 2048)); - $statusCode = (int)$res["status"]; - $errorMessage = $res["message"]; - } - fclose($socket); - - // If status code is 200, return true - if (200 == $statusCode) { - return true; - } - - // If status code is greater than 500 and less than 600, it indicates server error - // Error code 429 indicates rate limited. - // Retry uploading in these cases. - if (($statusCode >= 500 && $statusCode <= 600) || 429 == $statusCode || 0 == $statusCode) { - if ($backoff >= $this->maximum_backoff_duration) { - break; - } - - usleep($backoff * 1000); - } elseif ($statusCode >= 400) { - if ($this->debug()) { - $this->handleError($res["status"], $res["message"]); + private function makeRequest($socket, $req, $retry = true) + { + $bytes_written = 0; + $bytes_total = strlen($req); + $closed = false; + $success = true; + + // Retries with exponential backoff until success + $backoff = 100; // Set initial waiting time to 100ms + + while (true) { + // Send request to server + while (!$closed && $bytes_written < $bytes_total) { + try { + // Since we're try catch'ing prevent PHP logs. + $written = @fwrite($socket, substr($req, $bytes_written)); + } catch (Exception $e) { + $this->handleError($e->getCode(), $e->getMessage()); + $closed = true; + } + if (!isset($written) || !$written) { + $closed = true; + } else { + $bytes_written += $written; + } + } + + // Get response for request + $statusCode = 0; + $errorMessage = ""; + + if (!$closed) { + $res = $this->parseResponse(fread($socket, 2048)); + $statusCode = (int)$res["status"]; + $errorMessage = $res["message"]; + } + fclose($socket); + + // If status code is 200, return true + if (200 == $statusCode) { + return true; + } + + // If status code is greater than 500 and less than 600, it indicates server error + // Error code 429 indicates rate limited. + // Retry uploading in these cases. + if (($statusCode >= 500 && $statusCode <= 600) || 429 == $statusCode || 0 == $statusCode) { + if ($backoff >= $this->maximum_backoff_duration) { + break; + } + + usleep($backoff * 1000); + } elseif ($statusCode >= 400) { + if ($this->debug()) { + $this->handleError($res["status"], $res["message"]); + } + + break; + } + + // Retry uploading... + $backoff *= 2; + $socket = $this->createSocket(); } - break; - } - - // Retry uploading... - $backoff *= 2; - $socket = $this->createSocket(); + return $success; } - return $success; - } - /** * Create the body to send as the post request. * @param string $host * @param string $content * @return string body */ - private function createBody($host, $content) { - $req = ""; - $req.= "POST /v1/batch HTTP/1.1\r\n"; - $req.= "Host: " . $host . "\r\n"; - $req.= "Content-Type: application/json\r\n"; - $req.= "Authorization: Basic " . base64_encode($this->secret . ":") . "\r\n"; - $req.= "Accept: application/json\r\n"; - - // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. - $content_json = json_decode($content, true); - $library = $content_json['batch'][0]['context']['library']; - $libName = $library['name']; - $libVersion = $library['version']; - $req.= "User-Agent: ${libName}/${libVersion}\r\n"; - - // Compress content if compress_request is true - if ($this->compress_request) { - $content = gzencode($content); - - $req.= "Content-Encoding: gzip\r\n"; - } + private function createBody($host, $content) + { + $req = ""; + $req .= "POST /v1/batch HTTP/1.1\r\n"; + $req .= "Host: " . $host . "\r\n"; + $req .= "Content-Type: application/json\r\n"; + $req .= "Authorization: Basic " . base64_encode($this->secret . ":") . "\r\n"; + $req .= "Accept: application/json\r\n"; + + // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. + $content_json = json_decode($content, true); + $library = $content_json['batch'][0]['context']['library']; + $libName = $library['name']; + $libVersion = $library['version']; + $req .= "User-Agent: ${libName}/${libVersion}\r\n"; + + // Compress content if compress_request is true + if ($this->compress_request) { + $content = gzencode($content); + + $req .= "Content-Encoding: gzip\r\n"; + } - $req.= "Content-length: " . strlen($content) . "\r\n"; - $req.= "\r\n"; - $req.= $content; + $req .= "Content-length: " . strlen($content) . "\r\n"; + $req .= "\r\n"; + $req .= $content; - // Verify message size is below than 32KB - if (strlen($req) >= 32 * 1024) { - $msg = "Message size is larger than 32KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); + // Verify message size is below than 32KB + if (strlen($req) >= 32 * 1024) { + $msg = "Message size is larger than 32KB"; + error_log("[Analytics][" . $this->type . "] " . $msg); - return false; - } + return false; + } - return $req; - } + return $req; + } /** * Parse our response from the server, check header and body. @@ -211,17 +219,18 @@ private function createBody($host, $content) { * string $status HTTP code, e.g. "200" * string $message JSON response from the api */ - private function parseResponse($res) { - $contents = explode("\n", $res); - - // Response comes back as HTTP/1.1 200 OK - // Final line contains HTTP response. - $status = explode(" ", $contents[0], 3); - $result = $contents[count($contents) - 1]; - - return array( - "status" => isset($status[1]) ? $status[1] : null, - "message" => $result - ); - } + private function parseResponse($res) + { + $contents = explode("\n", $res); + + // Response comes back as HTTP/1.1 200 OK + // Final line contains HTTP response. + $status = explode(" ", $contents[0], 3); + $result = $contents[count($contents) - 1]; + + return array( + "status" => isset($status[1]) ? $status[1] : null, + "message" => $result + ); + } } diff --git a/lib/Segment/QueueConsumer.php b/lib/Segment/QueueConsumer.php index 67fac0a..0220922 100644 --- a/lib/Segment/QueueConsumer.php +++ b/lib/Segment/QueueConsumer.php @@ -2,88 +2,92 @@ namespace Segment\Consumer; -abstract class QueueConsumer extends Consumer { - protected $type = "QueueConsumer"; +abstract class QueueConsumer extends Consumer +{ + protected $type = "QueueConsumer"; - protected $queue; - protected $max_queue_size = 10000; - protected $max_queue_size_bytes = 33554432; //32M + protected $queue; + protected $max_queue_size = 10000; + protected $max_queue_size_bytes = 33554432; //32M - protected $flush_at = 100; - protected $max_batch_size_bytes = 512000; //500kb - protected $max_item_size_bytes = 32000; // 32kb - protected $maximum_backoff_duration = 10000; // Set maximum waiting limit to 10s - protected $host = ""; - protected $compress_request = false; + protected $flush_at = 100; + protected $max_batch_size_bytes = 512000; //500kb + protected $max_item_size_bytes = 32000; // 32kb + protected $maximum_backoff_duration = 10000; // Set maximum waiting limit to 10s + protected $host = ""; + protected $compress_request = false; - protected $flush_interval_in_mills = 10000; //frequency in milliseconds to send data, default 10 + protected $flush_interval_in_mills = 10000; //frequency in milliseconds to send data, default 10 /** * Store our secret and options as part of this consumer * @param string $secret * @param array $options */ - public function __construct($secret, $options = array()) { - parent::__construct($secret, $options); - - if (isset($options["max_queue_size"])) { - $this->max_queue_size = $options["max_queue_size"]; - } - - if (isset($options["batch_size"])) { - if($options["batch_size"] < 1) { - $msg = "Batch Size must not be less than 1"; - error_log("[Analytics][" . $this->type . "] " . $msg); - } else { - $msg = "WARNING: batch_size option to be depricated soon, please use new option flush_at"; - error_log("[Analytics][" . $this->type . "] " . $msg); - $this->flush_at = $options["batch_size"]; - } + public function __construct($secret, $options = array()) + { + parent::__construct($secret, $options); + + if (isset($options["max_queue_size"])) { + $this->max_queue_size = $options["max_queue_size"]; + } + + if (isset($options["batch_size"])) { + if ($options["batch_size"] < 1) { + $msg = "Batch Size must not be less than 1"; + error_log("[Analytics][" . $this->type . "] " . $msg); + } else { + $msg = "WARNING: batch_size option to be depricated soon, please use new option flush_at"; + error_log("[Analytics][" . $this->type . "] " . $msg); + $this->flush_at = $options["batch_size"]; + } + } + + if (isset($options["flush_at"])) { + if ($options["flush_at"] < 1) { + $msg = "Flush at Size must not be less than 1"; + error_log("[Analytics][" . $this->type . "] " . $msg); + } else { + $this->flush_at = $options["flush_at"]; + } + } + + if (isset($options["host"])) { + $this->host = $options["host"]; + } + + if (isset($options["compress_request"])) { + $this->compress_request = json_decode($options["compress_request"]); + } + + if (isset($options["flush_interval"])) { + if ($options["flush_interval"] < 1000) { + $msg = "Flush interval must not be less than 1 second"; + error_log("[Analytics][" . $this->type . "] " . $msg); + } else { + $this->flush_interval_in_mills = $options["flush_interval"]; + } + } + + $this->queue = array(); } - if (isset($options["flush_at"])) { - if($options["flush_at"] < 1) { - $msg = "Flush at Size must not be less than 1"; - error_log("[Analytics][" . $this->type . "] " . $msg); - } else { - $this->flush_at = $options["flush_at"]; - } - } - - if (isset($options["host"])) { - $this->host = $options["host"]; + public function __destruct() + { + // Flush our queue on destruction + $this->flush(); } - if (isset($options["compress_request"])) { - $this->compress_request = json_decode($options["compress_request"]); - } - - if (isset($options["flush_interval"])) { - if($options["flush_interval"] < 1000) { - $msg = "Flush interval must not be less than 1 second"; - error_log("[Analytics][" . $this->type . "] " . $msg); - } else { - $this->flush_interval_in_mills = $options["flush_interval"]; - } - } - - $this->queue = array(); - } - - public function __destruct() { - // Flush our queue on destruction - $this->flush(); - } - /** * Tracks a user action * * @param array $message * @return boolean whether the track call succeeded */ - public function track(array $message) { - return $this->enqueue($message); - } + public function track(array $message) + { + return $this->enqueue($message); + } /** * Tags traits about the user. @@ -91,9 +95,10 @@ public function track(array $message) { * @param array $message * @return boolean whether the identify call succeeded */ - public function identify(array $message) { - return $this->enqueue($message); - } + public function identify(array $message) + { + return $this->enqueue($message); + } /** * Tags traits about the group. @@ -101,9 +106,10 @@ public function identify(array $message) { * @param array $message * @return boolean whether the group call succeeded */ - public function group(array $message) { - return $this->enqueue($message); - } + public function group(array $message) + { + return $this->enqueue($message); + } /** * Tracks a page view. @@ -111,9 +117,10 @@ public function group(array $message) { * @param array $message * @return boolean whether the page call succeeded */ - public function page(array $message) { - return $this->enqueue($message); - } + public function page(array $message) + { + return $this->enqueue($message); + } /** * Tracks a screen view. @@ -121,9 +128,10 @@ public function page(array $message) { * @param array $message * @return boolean whether the screen call succeeded */ - public function screen(array $message) { - return $this->enqueue($message); - } + public function screen(array $message) + { + return $this->enqueue($message); + } /** * Aliases from one user id to another @@ -131,74 +139,77 @@ public function screen(array $message) { * @param array $message * @return boolean whether the alias call succeeded */ - public function alias(array $message) { - return $this->enqueue($message); - } + public function alias(array $message) + { + return $this->enqueue($message); + } /** * Flushes our queue of messages by batching them to the server */ - public function flush() { - $count = count($this->queue); - $success = true; + public function flush() + { + $count = count($this->queue); + $success = true; - while ($count > 0 && $success) { + while ($count > 0 && $success) { + $batch = array_splice($this->queue, 0, min($this->flush_at, $count)); - $batch = array_splice($this->queue, 0, min($this->flush_at, $count)); + if (mb_strlen(serialize($batch), '8bit') >= $this->max_batch_size_bytes) { + $msg = "Batch size is larger than 500KB"; + error_log("[Analytics][" . $this->type . "] " . $msg); - if (mb_strlen(serialize($batch), '8bit') >= $this->max_batch_size_bytes) { - $msg = "Batch size is larger than 500KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); + return false; + } - return false; - } + $success = $this->flushBatch($batch); - $success = $this->flushBatch($batch); + $count = count($this->queue); - $count = count($this->queue); + if ($count > 0) { + usleep($this->flush_interval_in_mills * 1000); + } + } - if($count > 0) - usleep($this->flush_interval_in_mills * 1000); + return $success; } - return $success; - } - /** * Adds an item to our queue. * @param mixed $item * @return boolean whether call has succeeded */ - protected function enqueue($item) { - $count = count($this->queue); + protected function enqueue($item) + { + $count = count($this->queue); - if ($count > $this->max_queue_size) { - return false; - } + if ($count > $this->max_queue_size) { + return false; + } - if (mb_strlen(serialize((array)$this->queue), '8bit') >= $this->max_queue_size_bytes) { - $msg = "Queue size is larger than 32MB"; - error_log("[Analytics][" . $this->type . "] " . $msg); + if (mb_strlen(serialize((array)$this->queue), '8bit') >= $this->max_queue_size_bytes) { + $msg = "Queue size is larger than 32MB"; + error_log("[Analytics][" . $this->type . "] " . $msg); - return false; - } + return false; + } - if (mb_strlen(json_encode($item), '8bit') >= $this->max_item_size_bytes) { - $msg = "Item size is larger than 32KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); + if (mb_strlen(json_encode($item), '8bit') >= $this->max_item_size_bytes) { + $msg = "Item size is larger than 32KB"; + error_log("[Analytics][" . $this->type . "] " . $msg); - return false; - } + return false; + } - $count = array_push($this->queue, $item); - + $count = array_push($this->queue, $item); - if ($count >= $this->flush_at) { - return $this->flush(); // return ->flush() result: true on success - } - return true; - } + if ($count >= $this->flush_at) { + return $this->flush(); // return ->flush() result: true on success + } + + return true; + } /** * Given a batch of messages the method returns @@ -207,10 +218,11 @@ protected function enqueue($item) { * @param {Array} $batch * @return {Array} */ - protected function payload($batch){ - return array( - "batch" => $batch, - "sentAt" => date("c"), - ); - } -} \ No newline at end of file + protected function payload($batch) + { + return array( + "batch" => $batch, + "sentAt" => date("c"), + ); + } +} diff --git a/lib/Segment/Version.php b/lib/Segment/Version.php index b8988f4..7a06e70 100644 --- a/lib/Segment/Version.php +++ b/lib/Segment/Version.php @@ -1,3 +1,4 @@ ./lib/ ./test/ - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - 0 - - \ No newline at end of file diff --git a/test/AnalyticsTest.php b/test/AnalyticsTest.php index 2bebad4..57aaaf5 100644 --- a/test/AnalyticsTest.php +++ b/test/AnalyticsTest.php @@ -1,251 +1,252 @@ true)); - } - - public function testTrack() - { - $this->assertTrue(Segment::track(array( - "userId" => "john", - "event" => "Module PHP Event", - ))); - } - - public function testGroup() - { - $this->assertTrue(Segment::group(array( - "groupId" => "group-id", - "userId" => "user-id", - "traits" => array( + public function setUp(): void + { + date_default_timezone_set("UTC"); + Segment::init("oq0vdlg7yi", array("debug" => true)); + } + + public function testTrack() + { + $this->assertTrue(Segment::track(array( + "userId" => "john", + "event" => "Module PHP Event", + ))); + } + + public function testGroup() + { + $this->assertTrue(Segment::group(array( + "groupId" => "group-id", + "userId" => "user-id", + "traits" => array( "plan" => "startup", - ), - ))); - } - - public function testGroupAnonymous() - { - $this->assertTrue(Segment::group(array( - "groupId" => "group-id", - "anonymousId" => "anonymous-id", - "traits" => array( + ), + ))); + } + + public function testGroupAnonymous() + { + $this->assertTrue(Segment::group(array( + "groupId" => "group-id", + "anonymousId" => "anonymous-id", + "traits" => array( "plan" => "startup", - ), - ))); - } + ), + ))); + } /** * @expectedException \Exception * @expectedExceptionMessage Segment::group() requires userId or anonymousId */ - public function testGroupNoUser(): void - { - $this->expectException( \Exception::class); - Segment::group(array( - "groupId" => "group-id", - "traits" => array( + public function testGroupNoUser(): void + { + $this->expectException(\Exception::class); + Segment::group(array( + "groupId" => "group-id", + "traits" => array( "plan" => "startup", - ), - )); - } - - public function testMicrotime() - { - $this->assertTrue(Segment::page(array( - "anonymousId" => "anonymous-id", - "name" => "analytics-php-microtime", - "category" => "docs", - "timestamp" => microtime(true), - "properties" => array( + ), + )); + } + + public function testMicrotime() + { + $this->assertTrue(Segment::page(array( + "anonymousId" => "anonymous-id", + "name" => "analytics-php-microtime", + "category" => "docs", + "timestamp" => microtime(true), + "properties" => array( "path" => "/docs/libraries/php/", "url" => "https://segment.io/docs/libraries/php/", - ), - ))); - } - - public function testPage() - { - $this->assertTrue(Segment::page(array( - "anonymousId" => "anonymous-id", - "name" => "analytics-php", - "category" => "docs", - "properties" => array( + ), + ))); + } + + public function testPage() + { + $this->assertTrue(Segment::page(array( + "anonymousId" => "anonymous-id", + "name" => "analytics-php", + "category" => "docs", + "properties" => array( "path" => "/docs/libraries/php/", "url" => "https://segment.io/docs/libraries/php/", - ), - ))); - } - - public function testBasicPage() - { - $this->assertTrue(Segment::page(array( - "anonymousId" => "anonymous-id", - ))); - } - - public function testScreen() - { - $this->assertTrue(Segment::screen(array( - "anonymousId" => "anonymous-id", - "name" => "2048", - "category" => "game built with php :)", - "properties" => array( + ), + ))); + } + + public function testBasicPage() + { + $this->assertTrue(Segment::page(array( + "anonymousId" => "anonymous-id", + ))); + } + + public function testScreen() + { + $this->assertTrue(Segment::screen(array( + "anonymousId" => "anonymous-id", + "name" => "2048", + "category" => "game built with php :)", + "properties" => array( "points" => 300 - ), - ))); - } - - public function testBasicScreen() - { - $this->assertTrue(Segment::screen(array( - "anonymousId" => "anonymous-id" - ))); - } - - public function testIdentify() - { - $this->assertTrue(Segment::identify(array( - "userId" => "doe", - "traits" => array( + ), + ))); + } + + public function testBasicScreen() + { + $this->assertTrue(Segment::screen(array( + "anonymousId" => "anonymous-id" + ))); + } + + public function testIdentify() + { + $this->assertTrue(Segment::identify(array( + "userId" => "doe", + "traits" => array( "loves_php" => false, "birthday" => time(), - ), - ))); - } - - public function testEmptyTraits() - { - $this->assertTrue(Segment::identify(array( - "userId" => "empty-traits", - ))); - - $this->assertTrue(Segment::group(array( - "userId" => "empty-traits", - "groupId" => "empty-traits", - ))); - } - - public function testEmptyArrayTraits() - { - $this->assertTrue(Segment::identify(array( - "userId" => "empty-traits", - "traits" => array(), - ))); - - $this->assertTrue(Segment::group(array( - "userId" => "empty-traits", - "groupId" => "empty-traits", - "traits" => array(), - ))); - } - - public function testEmptyProperties() - { - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "empty-properties", - ))); - - $this->assertTrue(Segment::page(array( - "category" => "empty-properties", - "name" => "empty-properties", - "userId" => "user-id", - ))); - } - - public function testEmptyArrayProperties() - { - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "empty-properties", - "properties" => array(), - ))); - - $this->assertTrue(Segment::page(array( - "category" => "empty-properties", - "name" => "empty-properties", - "userId" => "user-id", - "properties" => array(), - ))); - } - - public function testAlias() - { - $this->assertTrue(Segment::alias(array( - "previousId" => "previous-id", - "userId" => "user-id", - ))); - } - - public function testContextEmpty() - { - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "Context Test", - "context" => array(), - ))); - } - - public function testContextCustom() - { - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "Context Test", - "context" => array( + ), + ))); + } + + public function testEmptyTraits() + { + $this->assertTrue(Segment::identify(array( + "userId" => "empty-traits", + ))); + + $this->assertTrue(Segment::group(array( + "userId" => "empty-traits", + "groupId" => "empty-traits", + ))); + } + + public function testEmptyArrayTraits() + { + $this->assertTrue(Segment::identify(array( + "userId" => "empty-traits", + "traits" => array(), + ))); + + $this->assertTrue(Segment::group(array( + "userId" => "empty-traits", + "groupId" => "empty-traits", + "traits" => array(), + ))); + } + + public function testEmptyProperties() + { + $this->assertTrue(Segment::track(array( + "userId" => "user-id", + "event" => "empty-properties", + ))); + + $this->assertTrue(Segment::page(array( + "category" => "empty-properties", + "name" => "empty-properties", + "userId" => "user-id", + ))); + } + + public function testEmptyArrayProperties() + { + $this->assertTrue(Segment::track(array( + "userId" => "user-id", + "event" => "empty-properties", + "properties" => array(), + ))); + + $this->assertTrue(Segment::page(array( + "category" => "empty-properties", + "name" => "empty-properties", + "userId" => "user-id", + "properties" => array(), + ))); + } + + public function testAlias() + { + $this->assertTrue(Segment::alias(array( + "previousId" => "previous-id", + "userId" => "user-id", + ))); + } + + public function testContextEmpty() + { + $this->assertTrue(Segment::track(array( + "userId" => "user-id", + "event" => "Context Test", + "context" => array(), + ))); + } + + public function testContextCustom() + { + $this->assertTrue(Segment::track(array( + "userId" => "user-id", + "event" => "Context Test", + "context" => array( "active" => false, - ), - ))); - } - - public function testTimestamps() - { - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "integer-timestamp", - "timestamp" => (int) mktime(0, 0, 0, date('n'), 1, date('Y')), - ))); - - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "string-integer-timestamp", - "timestamp" => (string) mktime(0, 0, 0, date('n'), 1, date('Y')), - ))); - - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "iso8630-timestamp", - "timestamp" => date(DATE_ATOM, mktime(0, 0, 0, date('n'), 1, date('Y'))), - ))); - - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "iso8601-timestamp", - "timestamp" => date(DATE_ATOM, mktime(0, 0, 0, date('n'), 1, date('Y'))), - ))); - - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "strtotime-timestamp", - "timestamp" => strtotime('1 week ago'), - ))); - - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "microtime-timestamp", - "timestamp" => microtime(true), - ))); - - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "invalid-float-timestamp", - "timestamp" => ((string) mktime(0, 0, 0, date('n'), 1, date('Y'))) . '.', - ))); - } + ), + ))); + } + + public function testTimestamps() + { + $this->assertTrue(Segment::track(array( + "userId" => "user-id", + "event" => "integer-timestamp", + "timestamp" => (int) mktime(0, 0, 0, date('n'), 1, date('Y')), + ))); + + $this->assertTrue(Segment::track(array( + "userId" => "user-id", + "event" => "string-integer-timestamp", + "timestamp" => (string) mktime(0, 0, 0, date('n'), 1, date('Y')), + ))); + + $this->assertTrue(Segment::track(array( + "userId" => "user-id", + "event" => "iso8630-timestamp", + "timestamp" => date(DATE_ATOM, mktime(0, 0, 0, date('n'), 1, date('Y'))), + ))); + + $this->assertTrue(Segment::track(array( + "userId" => "user-id", + "event" => "iso8601-timestamp", + "timestamp" => date(DATE_ATOM, mktime(0, 0, 0, date('n'), 1, date('Y'))), + ))); + + $this->assertTrue(Segment::track(array( + "userId" => "user-id", + "event" => "strtotime-timestamp", + "timestamp" => strtotime('1 week ago'), + ))); + + $this->assertTrue(Segment::track(array( + "userId" => "user-id", + "event" => "microtime-timestamp", + "timestamp" => microtime(true), + ))); + + $this->assertTrue(Segment::track(array( + "userId" => "user-id", + "event" => "invalid-float-timestamp", + "timestamp" => ((string) mktime(0, 0, 0, date('n'), 1, date('Y'))) . '.', + ))); + } } diff --git a/test/ClientTest.php b/test/ClientTest.php index c2d7487..9cf1901 100644 --- a/test/ClientTest.php +++ b/test/ClientTest.php @@ -1,4 +1,5 @@ assertInstanceOf(LibCurl::class, $client->getConsumer()); } - + /** @test */ public function can_provide_the_consumer_configuration_as_string() { @@ -20,7 +21,7 @@ public function can_provide_the_consumer_configuration_as_string() ]); $this->assertInstanceOf(ForkCurl::class, $client->getConsumer()); } - + /** @test */ public function can_provide_a_class_namespace_as_consumer_configuration() { diff --git a/test/ConsumerFileTest.php b/test/ConsumerFileTest.php index f5c3e9f..942c1be 100644 --- a/test/ConsumerFileTest.php +++ b/test/ConsumerFileTest.php @@ -1,167 +1,170 @@ filename())) { - unlink($this->filename()); + private $client; + private $filename = "/tmp/analytics.log"; + + public function setUp(): void + { + date_default_timezone_set("UTC"); + if (file_exists($this->filename())) { + unlink($this->filename()); + } + + $this->client = new Client( + "oq0vdlg7yi", + array( + "consumer" => "file", + "filename" => $this->filename, + ) + ); + } + + public function tearDown(): void + { + if (file_exists($this->filename)) { + unlink($this->filename); + } } - $this->client = new Client( - "oq0vdlg7yi", - array( - "consumer" => "file", - "filename" => $this->filename, - ) - ); - } - - public function tearDown(): void - { - if (file_exists($this->filename)) { - unlink($this->filename); + public function testTrack() + { + $this->assertTrue($this->client->track(array( + "userId" => "some-user", + "event" => "File PHP Event - Microtime", + "timestamp" => microtime(true), + ))); + $this->checkWritten("track"); } - } - - public function testTrack() - { - $this->assertTrue($this->client->track(array( - "userId" => "some-user", - "event" => "File PHP Event - Microtime", - "timestamp" => microtime(true), - ))); - $this->checkWritten("track"); - } - - public function testIdentify() - { - $this->assertTrue($this->client->identify(array( - "userId" => "Calvin", - "traits" => array( + + public function testIdentify() + { + $this->assertTrue($this->client->identify(array( + "userId" => "Calvin", + "traits" => array( "loves_php" => false, "type" => "analytics.log", "birthday" => time(), - ), - ))); - $this->checkWritten("identify"); - } - - public function testGroup() - { - $this->assertTrue($this->client->group(array( - "userId" => "user-id", - "groupId" => "group-id", - "traits" => array( + ), + ))); + $this->checkWritten("identify"); + } + + public function testGroup() + { + $this->assertTrue($this->client->group(array( + "userId" => "user-id", + "groupId" => "group-id", + "traits" => array( "type" => "consumer analytics.log test", - ), - ))); - } - - public function testPage() - { - $this->assertTrue($this->client->page(array( - "userId" => "user-id", - "name" => "analytics-php", - "category" => "analytics.log", - "properties" => array( + ), + ))); + } + + public function testPage() + { + $this->assertTrue($this->client->page(array( + "userId" => "user-id", + "name" => "analytics-php", + "category" => "analytics.log", + "properties" => array( "url" => "https://a.url/", - ), - ))); - } - - public function testScreen() - { - $this->assertTrue($this->client->screen(array( - "userId" => "userId", - "name" => "grand theft auto", - "category" => "analytics.log", - "properties" => array(), - ))); - } - - public function testAlias() - { - $this->assertTrue($this->client->alias(array( - "previousId" => "previous-id", - "userId" => "user-id", - ))); - $this->checkWritten("alias"); - } - - public function testSend() - { - for ($i = 0; $i < 200; ++$i) { - $this->client->track(array( + ), + ))); + } + + public function testScreen() + { + $this->assertTrue($this->client->screen(array( "userId" => "userId", - "event" => "event", - )); + "name" => "grand theft auto", + "category" => "analytics.log", + "properties" => array(), + ))); + } + + public function testAlias() + { + $this->assertTrue($this->client->alias(array( + "previousId" => "previous-id", + "userId" => "user-id", + ))); + $this->checkWritten("alias"); + } + + public function testSend() + { + for ($i = 0; $i < 200; ++$i) { + $this->client->track(array( + "userId" => "userId", + "event" => "event", + )); + } + exec("php --define date.timezone=UTC send.php --secret oq0vdlg7yi --file /tmp/analytics.log", $output); + $this->assertSame("sent 200 from 200 requests successfully", trim($output[0])); + $this->assertFileDoesNotExist($this->filename()); + } + + public function testProductionProblems() + { + // Open to a place where we should not have write access. + $client = new Client( + "oq0vdlg7yi", + array( + "consumer" => "file", + "filename" => "/dev/xxxxxxx", + ) + ); + + $tracked = $client->track(array("userId" => "some-user", "event" => "my event")); + $this->assertFalse($tracked); + } + + public function testFileSecurityCustom() + { + $client = new Client( + "testsecret", + array( + "consumer" => "file", + "filename" => $this->filename, + "filepermissions" => 0700 + ) + ); + $tracked = $client->track(array("userId" => "some_user", "event" => "File PHP Event")); + $this->assertEquals(0700, (fileperms($this->filename) & 0777)); + } + + public function testFileSecurityDefaults() + { + $client = new Client( + "testsecret", + array( + "consumer" => "file", + "filename" => $this->filename + ) + ); + $tracked = $client->track(array("userId" => "some_user", "event" => "File PHP Event")); + $this->assertEquals(0777, (fileperms($this->filename) & 0777)); + } + + public function checkWritten($type) + { + exec("wc -l " . $this->filename, $output); + $out = trim($output[0]); + $this->assertSame($out, "1 " . $this->filename); + $str = file_get_contents($this->filename); + $json = json_decode(trim($str)); + $this->assertSame($type, $json->type); + unlink($this->filename); + } + + public function filename() + { + return '/tmp/analytics.log'; } - exec("php --define date.timezone=UTC send.php --secret oq0vdlg7yi --file /tmp/analytics.log", $output); - $this->assertSame("sent 200 from 200 requests successfully", trim($output[0])); - $this->assertFileDoesNotExist($this->filename()); - } - - public function testProductionProblems() - { - // Open to a place where we should not have write access. - $client = new Client( - "oq0vdlg7yi", - array( - "consumer" => "file", - "filename" => "/dev/xxxxxxx", - ) - ); - - $tracked = $client->track(array("userId" => "some-user", "event" => "my event")); - $this->assertFalse($tracked); - } - - public function testFileSecurityCustom() { - $client = new Client( - "testsecret", - array( - "consumer" => "file", - "filename" => $this->filename, - "filepermissions" => 0700 - ) - ); - $tracked = $client->track(array("userId" => "some_user", "event" => "File PHP Event")); - $this->assertEquals(0700, (fileperms($this->filename) & 0777)); - } - - public function testFileSecurityDefaults() { - $client = new Client( - "testsecret", - array( - "consumer" => "file", - "filename" => $this->filename - ) - ); - $tracked = $client->track(array("userId" => "some_user", "event" => "File PHP Event")); - $this->assertEquals(0777, (fileperms($this->filename) & 0777)); - } - - public function checkWritten($type) - { - exec("wc -l " . $this->filename, $output); - $out = trim($output[0]); - $this->assertSame($out, "1 " . $this->filename); - $str = file_get_contents($this->filename); - $json = json_decode(trim($str)); - $this->assertSame($type, $json->type); - unlink($this->filename); - } - - public function filename() - { - return '/tmp/analytics.log'; - } } diff --git a/test/ConsumerForkCurlTest.php b/test/ConsumerForkCurlTest.php index c6abb2e..4c51a8f 100644 --- a/test/ConsumerForkCurlTest.php +++ b/test/ConsumerForkCurlTest.php @@ -1,100 +1,102 @@ client = new Client( - "OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", - array( - "consumer" => "fork_curl", - "debug" => true, - ) - ); - } + public function setUp(): void + { + date_default_timezone_set("UTC"); + $this->client = new Client( + "OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", + array( + "consumer" => "fork_curl", + "debug" => true, + ) + ); + } - public function testTrack() - { - $this->assertTrue($this->client->track(array( - "userId" => "some-user", - "event" => "PHP Fork Curl'd\" Event", - ))); - } + public function testTrack() + { + $this->assertTrue($this->client->track(array( + "userId" => "some-user", + "event" => "PHP Fork Curl'd\" Event", + ))); + } - public function testIdentify() - { - $this->assertTrue($this->client->identify(array( - "userId" => "user-id", - "traits" => array( + public function testIdentify() + { + $this->assertTrue($this->client->identify(array( + "userId" => "user-id", + "traits" => array( "loves_php" => false, "type" => "consumer fork-curl test", "birthday" => time(), - ), - ))); - } + ), + ))); + } - public function testGroup() - { - $this->assertTrue($this->client->group(array( - "userId" => "user-id", - "groupId" => "group-id", - "traits" => array( + public function testGroup() + { + $this->assertTrue($this->client->group(array( + "userId" => "user-id", + "groupId" => "group-id", + "traits" => array( "type" => "consumer fork-curl test", - ), - ))); - } + ), + ))); + } - public function testPage() - { - $this->assertTrue($this->client->page(array( - "userId" => "userId", - "name" => "analytics-php", - "category" => "fork-curl", - "properties" => array( + public function testPage() + { + $this->assertTrue($this->client->page(array( + "userId" => "userId", + "name" => "analytics-php", + "category" => "fork-curl", + "properties" => array( "url" => "https://a.url/", - ), - ))); - } + ), + ))); + } - public function testScreen() - { - $this->assertTrue($this->client->page(array( - "anonymousId" => "anonymous-id", - "name" => "grand theft auto", - "category" => "fork-curl", - "properties" => array(), - ))); - } + public function testScreen() + { + $this->assertTrue($this->client->page(array( + "anonymousId" => "anonymous-id", + "name" => "grand theft auto", + "category" => "fork-curl", + "properties" => array(), + ))); + } - public function testAlias() - { - $this->assertTrue($this->client->alias(array( - "previousId" => "previous-id", - "userId" => "user-id", - ))); - } + public function testAlias() + { + $this->assertTrue($this->client->alias(array( + "previousId" => "previous-id", + "userId" => "user-id", + ))); + } - public function testRequestCompression() { - $options = array( - "compress_request" => true, - "consumer" => "fork_curl", - "debug" => true, - ); + public function testRequestCompression() + { + $options = array( + "compress_request" => true, + "consumer" => "fork_curl", + "debug" => true, + ); - // Create client and send Track message - $client = new Client("OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", $options); - $result = $client->track(array( - "userId" => "some-user", - "event" => "PHP Fork Curl'd\" Event with compression", - )); - $client->__destruct(); + // Create client and send Track message + $client = new Client("OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", $options); + $result = $client->track(array( + "userId" => "some-user", + "event" => "PHP Fork Curl'd\" Event with compression", + )); + $client->__destruct(); - $this->assertTrue($result); - } + $this->assertTrue($result); + } } diff --git a/test/ConsumerLibCurlTest.php b/test/ConsumerLibCurlTest.php index c386c5a..6b6903c 100644 --- a/test/ConsumerLibCurlTest.php +++ b/test/ConsumerLibCurlTest.php @@ -1,126 +1,128 @@ client = new Client( - "oq0vdlg7yi", - array( - "consumer" => "lib_curl", - "debug" => true, - ) - ); - } - - public function testTrack() - { - $this->assertTrue($this->client->track(array( - "userId" => "lib-curl-track", - "event" => "PHP Lib Curl'd\" Event", - ))); - } - - public function testIdentify() - { - $this->assertTrue($this->client->identify(array( - "userId" => "lib-curl-identify", - "traits" => array( + private $client; + + public function setUp(): void + { + date_default_timezone_set("UTC"); + $this->client = new Client( + "oq0vdlg7yi", + array( + "consumer" => "lib_curl", + "debug" => true, + ) + ); + } + + public function testTrack() + { + $this->assertTrue($this->client->track(array( + "userId" => "lib-curl-track", + "event" => "PHP Lib Curl'd\" Event", + ))); + } + + public function testIdentify() + { + $this->assertTrue($this->client->identify(array( + "userId" => "lib-curl-identify", + "traits" => array( "loves_php" => false, "type" => "consumer lib-curl test", "birthday" => time(), - ), - ))); - } - - public function testGroup() - { - $this->assertTrue($this->client->group(array( - "userId" => "lib-curl-group", - "groupId" => "group-id", - "traits" => array( + ), + ))); + } + + public function testGroup() + { + $this->assertTrue($this->client->group(array( + "userId" => "lib-curl-group", + "groupId" => "group-id", + "traits" => array( "type" => "consumer lib-curl test", - ), - ))); - } - - public function testPage() - { - $this->assertTrue($this->client->page(array( - "userId" => "lib-curl-page", - "name" => "analytics-php", - "category" => "fork-curl", - "properties" => array( + ), + ))); + } + + public function testPage() + { + $this->assertTrue($this->client->page(array( + "userId" => "lib-curl-page", + "name" => "analytics-php", + "category" => "fork-curl", + "properties" => array( "url" => "https://a.url/", - ), - ))); - } - - public function testScreen() - { - $this->assertTrue($this->client->page(array( - "anonymousId" => "lib-curl-screen", - "name" => "grand theft auto", - "category" => "fork-curl", - "properties" => array(), - ))); - } - - public function testAlias() - { - $this->assertTrue($this->client->alias(array( - "previousId" => "lib-curl-alias", - "userId" => "user-id", - ))); - } - - public function testRequestCompression() { - $options = array( - "compress_request" => true, - "consumer" => "lib_curl", - "error_handler" => function ($errno, $errmsg) { - throw new \RuntimeException($errmsg, $errno); - }, - ); - - $client = new Client("x", $options); - - # Should error out with debug on. - $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); - $client->__destruct(); - } - - public function testLargeMessageSizeError() - { - $options = array( - "debug" => true, - "consumer" => "lib_curl", - ); - - $client = new Client("testlargesize", $options); - - $big_property = ""; - - for ($i = 0; $i < 32 * 1024; ++$i) { - $big_property .= "a"; + ), + ))); + } + + public function testScreen() + { + $this->assertTrue($this->client->page(array( + "anonymousId" => "lib-curl-screen", + "name" => "grand theft auto", + "category" => "fork-curl", + "properties" => array(), + ))); } - $this->assertFalse( - $client->track( - array( - "userId" => "some-user", - "event" => "Super Large PHP Event", - "properties" => array("big_property" => $big_property), - ) - ) && $client->flush() - ); - - $client->__destruct(); - } + public function testAlias() + { + $this->assertTrue($this->client->alias(array( + "previousId" => "lib-curl-alias", + "userId" => "user-id", + ))); + } + + public function testRequestCompression() + { + $options = array( + "compress_request" => true, + "consumer" => "lib_curl", + "error_handler" => function ($errno, $errmsg) { + throw new \RuntimeException($errmsg, $errno); + }, + ); + + $client = new Client("x", $options); + + # Should error out with debug on. + $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); + $client->__destruct(); + } + + public function testLargeMessageSizeError() + { + $options = array( + "debug" => true, + "consumer" => "lib_curl", + ); + + $client = new Client("testlargesize", $options); + + $big_property = ""; + + for ($i = 0; $i < 32 * 1024; ++$i) { + $big_property .= "a"; + } + + $this->assertFalse( + $client->track( + array( + "userId" => "some-user", + "event" => "Super Large PHP Event", + "properties" => array("big_property" => $big_property), + ) + ) && $client->flush() + ); + + $client->__destruct(); + } } diff --git a/test/ConsumerSocketTest.php b/test/ConsumerSocketTest.php index 1838b1c..f924448 100644 --- a/test/ConsumerSocketTest.php +++ b/test/ConsumerSocketTest.php @@ -1,222 +1,225 @@ client = new Client( - "oq0vdlg7yi", - array("consumer" => "socket") - ); - } - - public function testTrack() - { - $this->assertTrue($this->client->track(array( - "userId" => "some-user", - "event" => "Socket PHP Event", - ))); - } - - public function testIdentify() - { - $this->assertTrue($this->client->identify(array( - "userId" => "Calvin", - "traits" => array( + private $client; + + public function setUp(): void + { + date_default_timezone_set("UTC"); + $this->client = new Client( + "oq0vdlg7yi", + array("consumer" => "socket") + ); + } + + public function testTrack() + { + $this->assertTrue($this->client->track(array( + "userId" => "some-user", + "event" => "Socket PHP Event", + ))); + } + + public function testIdentify() + { + $this->assertTrue($this->client->identify(array( + "userId" => "Calvin", + "traits" => array( "loves_php" => false, "birthday" => time(), - ), - ))); - } - - public function testGroup() - { - $this->assertTrue($this->client->group(array( - "userId" => "user-id", - "groupId" => "group-id", - "traits" => array( + ), + ))); + } + + public function testGroup() + { + $this->assertTrue($this->client->group(array( + "userId" => "user-id", + "groupId" => "group-id", + "traits" => array( "type" => "consumer socket test", - ), - ))); - } - - public function testPage() - { - $this->assertTrue($this->client->page(array( - "userId" => "user-id", - "name" => "analytics-php", - "category" => "socket", - "properties" => array( + ), + ))); + } + + public function testPage() + { + $this->assertTrue($this->client->page(array( + "userId" => "user-id", + "name" => "analytics-php", + "category" => "socket", + "properties" => array( "url" => "https://a.url/", - ), - ))); - } - - public function testScreen() - { - $this->assertTrue($this->client->screen(array( - "anonymousId" => "anonymousId", - "name" => "grand theft auto", - "category" => "socket", - "properties" => array(), - ))); - } - - public function testAlias() - { - $this->assertTrue($this->client->alias(array( - "previousId" => "some-socket", - "userId" => "new-socket", - ))); - } - - public function testShortTimeout() - { - $client = new Client( - "oq0vdlg7yi", - array( - "timeout" => 0.01, - "consumer" => "socket", - ) - ); - - $this->assertTrue($client->track(array( - "userId" => "some-user", - "event" => "Socket PHP Event", - ))); - - $this->assertTrue($client->identify(array( - "userId" => "some-user", - "traits" => array(), - ))); - - $client->__destruct(); - } - - public function testProductionProblems() - { - $client = new Client("x", - array( - "consumer" => "socket", - "error_handler" => function () { - throw new \Exception("Was called"); - }, - ) - ); - - // Shouldn't error out without debug on. - $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Production Problems"))); - $client->__destruct(); - } - - public function testDebugProblems() - { - $options = array( - "debug" => true, - "consumer" => "socket", - "error_handler" => function ($errno, $errmsg) { - if (400 != $errno) { - throw new \Exception("Response is not 400"); - } - }, - ); + ), + ))); + } - $client = new Client("x", $options); + public function testScreen() + { + $this->assertTrue($this->client->screen(array( + "anonymousId" => "anonymousId", + "name" => "grand theft auto", + "category" => "socket", + "properties" => array(), + ))); + } - // Should error out with debug on. - $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); - $client->__destruct(); - } + public function testAlias() + { + $this->assertTrue($this->client->alias(array( + "previousId" => "some-socket", + "userId" => "new-socket", + ))); + } + + public function testShortTimeout() + { + $client = new Client( + "oq0vdlg7yi", + array( + "timeout" => 0.01, + "consumer" => "socket", + ) + ); + + $this->assertTrue($client->track(array( + "userId" => "some-user", + "event" => "Socket PHP Event", + ))); + + $this->assertTrue($client->identify(array( + "userId" => "some-user", + "traits" => array(), + ))); + + $client->__destruct(); + } - public function testLargeMessage() - { - $options = array( - "debug" => true, - "consumer" => "socket", - ); + public function testProductionProblems() + { + $client = new Client( + "x", + array( + "consumer" => "socket", + "error_handler" => function () { + throw new \Exception("Was called"); + }, + ) + ); + + // Shouldn't error out without debug on. + $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Production Problems"))); + $client->__destruct(); + } - $client = new Client("testsecret", $options); + public function testDebugProblems() + { + $options = array( + "debug" => true, + "consumer" => "socket", + "error_handler" => function ($errno, $errmsg) { + if (400 != $errno) { + throw new \Exception("Response is not 400"); + } + }, + ); - $big_property = ""; + $client = new Client("x", $options); - for ($i = 0; $i < 10000; ++$i) { - $big_property .= "a"; + // Should error out with debug on. + $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); + $client->__destruct(); } - $this->assertTrue($client->track(array( - "userId" => "some-user", - "event" => "Super Large PHP Event", - "properties" => array("big_property" => $big_property), - ))); + public function testLargeMessage() + { + $options = array( + "debug" => true, + "consumer" => "socket", + ); - $client->__destruct(); - } + $client = new Client("testsecret", $options); - public function testLargeMessageSizeError() - { - $options = array( - "debug" => true, - "consumer" => "socket", - ); + $big_property = ""; - $client = new Client("testlargesize", $options); + for ($i = 0; $i < 10000; ++$i) { + $big_property .= "a"; + } - $big_property = ""; + $this->assertTrue($client->track(array( + "userId" => "some-user", + "event" => "Super Large PHP Event", + "properties" => array("big_property" => $big_property), + ))); - for ($i = 0; $i < 32 * 1024; ++$i) { - $big_property .= "a"; + $client->__destruct(); } - $this->assertFalse( - $client->track( - array( - "userId" => "some-user", - "event" => "Super Large PHP Event", - "properties" => array("big_property" => $big_property), - ) - ) && $client->flush() - ); + public function testLargeMessageSizeError() + { + $options = array( + "debug" => true, + "consumer" => "socket", + ); + + $client = new Client("testlargesize", $options); + + $big_property = ""; - $client->__destruct(); - } + for ($i = 0; $i < 32 * 1024; ++$i) { + $big_property .= "a"; + } + + $this->assertFalse( + $client->track( + array( + "userId" => "some-user", + "event" => "Super Large PHP Event", + "properties" => array("big_property" => $big_property), + ) + ) && $client->flush() + ); + + $client->__destruct(); + } /** * @expectedException \RuntimeException */ - public function testConnectionError() - { - $this->expectException(\RuntimeException::class); - $client = new Client("x", array( - "consumer" => "socket", - "host" => "api.segment.ioooooo", - "error_handler" => function ($errno, $errmsg) { - throw new \RuntimeException($errmsg, $errno); - }, - )); - - $client->track(array("user_id" => "some-user", "event" => "Event")); - $client->__destruct(); - } - - public function testRequestCompression() { - $options = array( - "compress_request" => true, - "consumer" => "socket", - "error_handler" => function ($errno, $errmsg) { - throw new \RuntimeException($errmsg, $errno); - }, - ); - - $client = new Client("x", $options); - - # Should error out with debug on. - $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); - $client->__destruct(); - } + public function testConnectionError() + { + $this->expectException(\RuntimeException::class); + $client = new Client("x", array( + "consumer" => "socket", + "host" => "api.segment.ioooooo", + "error_handler" => function ($errno, $errmsg) { + throw new \RuntimeException($errmsg, $errno); + }, + )); + + $client->track(array("user_id" => "some-user", "event" => "Event")); + $client->__destruct(); + } + + public function testRequestCompression() + { + $options = array( + "compress_request" => true, + "consumer" => "socket", + "error_handler" => function ($errno, $errmsg) { + throw new \RuntimeException($errmsg, $errno); + }, + ); + + $client = new Client("x", $options); + + # Should error out with debug on. + $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); + $client->__destruct(); + } } From c66758071a60f8ec39e5da605a4395ed86bafe61 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Thu, 23 Sep 2021 19:39:37 +0200 Subject: [PATCH 081/151] Switch to PSR-4 namespace and file arrangement Rename class namespaces more appropriately Remove all `require` statements Make phpunit use autoloader --- composer.json | 8 ++-- lib/{Segment => }/Client.php | 22 +++++---- lib/{Segment => Consumer}/Consumer.php | 0 lib/{Segment => }/Consumer/File.php | 0 lib/{Segment => }/Consumer/ForkCurl.php | 0 lib/{Segment => }/Consumer/LibCurl.php | 0 lib/{Segment => Consumer}/QueueConsumer.php | 0 lib/{Segment => }/Consumer/Socket.php | 0 lib/Segment.php | 4 -- lib/{Segment => }/Version.php | 0 phpunit.xml | 49 ++++++++++++++------- send.php | 8 ++-- test/AnalyticsTest.php | 7 +-- test/ClientTest.php | 10 +++-- test/ConsumerFileTest.php | 7 +-- test/ConsumerForkCurlTest.php | 7 +-- test/ConsumerLibCurlTest.php | 7 +-- test/ConsumerSocketTest.php | 7 +-- 18 files changed, 80 insertions(+), 56 deletions(-) rename lib/{Segment => }/Client.php (92%) rename lib/{Segment => Consumer}/Consumer.php (100%) rename lib/{Segment => }/Consumer/File.php (100%) rename lib/{Segment => }/Consumer/ForkCurl.php (100%) rename lib/{Segment => }/Consumer/LibCurl.php (100%) rename lib/{Segment => Consumer}/QueueConsumer.php (100%) rename lib/{Segment => }/Consumer/Socket.php (100%) rename lib/{Segment => }/Version.php (100%) diff --git a/composer.json b/composer.json index eaa777a..753666c 100644 --- a/composer.json +++ b/composer.json @@ -25,11 +25,11 @@ "squizlabs/php_codesniffer": "^3.3" }, "autoload": { - "files": [ - "lib/Segment.php" - ] + "psr-4": { + "Segment\\": "lib/" + } }, "bin": [ "bin/analytics" ] -} \ No newline at end of file +} diff --git a/lib/Segment/Client.php b/lib/Client.php similarity index 92% rename from lib/Segment/Client.php rename to lib/Client.php index 2c3d88f..da217ca 100644 --- a/lib/Segment/Client.php +++ b/lib/Client.php @@ -1,14 +1,12 @@ "Segment\Consumer\Socket", - "file" => "Segment\Consumer\File", - "fork_curl" => "Segment\Consumer\ForkCurl", - "lib_curl" => "Segment\Consumer\LibCurl" + "socket" => Socket::class, + "file" => File::class, + "fork_curl" => ForkCurl::class, + "lib_curl" => LibCurl::class ); // Use our socket libcurl by default $consumer_type = isset($options["consumer"]) ? $options["consumer"] : diff --git a/lib/Segment/Consumer.php b/lib/Consumer/Consumer.php similarity index 100% rename from lib/Segment/Consumer.php rename to lib/Consumer/Consumer.php diff --git a/lib/Segment/Consumer/File.php b/lib/Consumer/File.php similarity index 100% rename from lib/Segment/Consumer/File.php rename to lib/Consumer/File.php diff --git a/lib/Segment/Consumer/ForkCurl.php b/lib/Consumer/ForkCurl.php similarity index 100% rename from lib/Segment/Consumer/ForkCurl.php rename to lib/Consumer/ForkCurl.php diff --git a/lib/Segment/Consumer/LibCurl.php b/lib/Consumer/LibCurl.php similarity index 100% rename from lib/Segment/Consumer/LibCurl.php rename to lib/Consumer/LibCurl.php diff --git a/lib/Segment/QueueConsumer.php b/lib/Consumer/QueueConsumer.php similarity index 100% rename from lib/Segment/QueueConsumer.php rename to lib/Consumer/QueueConsumer.php diff --git a/lib/Segment/Consumer/Socket.php b/lib/Consumer/Socket.php similarity index 100% rename from lib/Segment/Consumer/Socket.php rename to lib/Consumer/Socket.php diff --git a/lib/Segment.php b/lib/Segment.php index 1577c66..8a4efce 100644 --- a/lib/Segment.php +++ b/lib/Segment.php @@ -2,10 +2,6 @@ namespace Segment; -use Segment\Consumer\Client as Client; - -require_once __DIR__ . '/Segment/Client.php'; - class Segment { private static $client; diff --git a/lib/Segment/Version.php b/lib/Version.php similarity index 100% rename from lib/Segment/Version.php rename to lib/Version.php diff --git a/phpunit.xml b/phpunit.xml index f6c06e3..4885233 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,17 +1,36 @@ - - - - ./lib - - - - - - - - test - - - + + + + ./lib + + + + + + + + test + + + \ No newline at end of file diff --git a/send.php b/send.php index 3288ebf..7b92349 100755 --- a/send.php +++ b/send.php @@ -1,10 +1,12 @@ getTimestamp() . "." . $dt->format("u")); $payload["timestamp"] = date("c", (int) $ts); $type = $payload["type"]; diff --git a/test/AnalyticsTest.php b/test/AnalyticsTest.php index 57aaaf5..0e71619 100644 --- a/test/AnalyticsTest.php +++ b/test/AnalyticsTest.php @@ -1,10 +1,11 @@ Date: Fri, 24 Sep 2021 00:50:39 +0200 Subject: [PATCH 082/151] Major cleanup! --- composer.json | 3 +- lib/Segment.php | 226 ++++++++++++++++++++++++------------------------ 2 files changed, 115 insertions(+), 114 deletions(-) diff --git a/composer.json b/composer.json index 753666c..7b34d1e 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ } ], "require": { - "php": ">=7.4" + "php": "^7.4 || ^8.0", + "ext-json": "*" }, "require-dev": { "phpunit/phpunit": "~9.0", diff --git a/lib/Segment.php b/lib/Segment.php index 8a4efce..b638a58 100644 --- a/lib/Segment.php +++ b/lib/Segment.php @@ -1,166 +1,166 @@ track($message); } - /** - * Tags traits about the user. - * - * @param array $message - * @return boolean whether the identify call succeeded - */ - public static function identify(array $message) + /** + * Check the client. + * + * @throws Exception + */ + private static function checkClient(): void + { + if (self::$client !== null) { + return; + } + + throw new Exception('Segment::init() must be called before any other tracking method.'); + } + + /** + * Validate common properties. + * + * @param array $message + * @param string $type + */ + public static function validate(array $message, string $type): void + { + $userId = (array_key_exists('userId', $message) && (string)$message['userId'] !== ''); + $anonId = !empty($message['anonymousId']); + self::assert($userId || $anonId, "Segment::$type() requires userId or anonymousId"); + } + + /** + * Tags traits about the user. + * + * @param array $message + * @return bool whether the call succeeded + */ + public static function identify(array $message): bool { self::checkClient(); - $message["type"] = "identify"; - self::validate($message, "identify"); + $message['type'] = 'identify'; + self::validate($message, 'identify'); return self::$client->identify($message); } - /** - * Tags traits about the group. - * - * @param array $message - * @return boolean whether the group call succeeded - */ - public static function group(array $message) + /** + * Tags traits about the group. + * + * @param array $message + * @return bool whether the group call succeeded + */ + public static function group(array $message): bool { self::checkClient(); $groupId = !empty($message['groupId']); - self::assert($groupId, "Segment::group() expects a groupId"); - self::validate($message, "group"); + self::assert($groupId, 'Segment::group() expects a groupId'); + self::validate($message, 'group'); return self::$client->group($message); } - /** - * Tracks a page view - * - * @param array $message - * @return boolean whether the page call succeeded - */ - public static function page(array $message) + /** + * Tracks a page view + * + * @param array $message + * @return bool whether the page call succeeded + */ + public static function page(array $message): bool { self::checkClient(); - self::validate($message, "page"); + self::validate($message, 'page'); return self::$client->page($message); } - /** - * Tracks a screen view - * - * @param array $message - * @return boolean whether the screen call succeeded - */ - public static function screen(array $message) + /** + * Tracks a screen view + * + * @param array $message + * @return bool whether the screen call succeeded + */ + public static function screen(array $message): bool { self::checkClient(); - self::validate($message, "screen"); + self::validate($message, 'screen'); return self::$client->screen($message); } - /** - * Aliases the user id from a temporary id to a permanent one - * - * @param array $from user id to alias from - * @return boolean whether the alias call succeeded - */ - public static function alias(array $message) + /** + * Aliases the user id from a temporary id to a permanent one + * + * @param array $message + * @return bool whether the alias call succeeded + */ + public static function alias(array $message): bool { self::checkClient(); - $userId = (array_key_exists('userId', $message) && strlen((string) $message['userId']) > 0); - $previousId = (array_key_exists('previousId', $message) && strlen((string) $message['previousId']) > 0); - self::assert($userId && $previousId, "Segment::alias() requires both userId and previousId"); + $userId = (array_key_exists('userId', $message) && (string)$message['userId'] !== ''); + $previousId = (array_key_exists('previousId', $message) && (string)$message['previousId'] !== ''); + self::assert($userId && $previousId, 'Segment::alias() requires both userId and previousId'); return self::$client->alias($message); } - /** - * Validate common properties. - * - * @param array $message - * @param string $type - */ - public static function validate($message, $type) - { - $userId = (array_key_exists('userId', $message) && strlen((string) $message['userId']) > 0); - $anonId = !empty($message['anonymousId']); - self::assert($userId || $anonId, "Segment::${type}() requires userId or anonymousId"); - } - - /** - * Flush the client - */ - - public static function flush() + /** + * Flush the client + */ + public static function flush(): bool { self::checkClient(); return self::$client->flush(); } - - /** - * Check the client. - * - * @throws Exception - */ - private static function checkClient() - { - if (null != self::$client) { - return; - } - - throw new \Exception("Segment::init() must be called before any other tracking method."); - } - - /** - * Assert `value` or throw. - * - * @param array $value - * @param string $msg - * @throws Exception - */ - private static function assert($value, $msg) - { - if (!$value) { - throw new \Exception($msg); - } - } -} - -if (!function_exists('json_encode')) { - throw new \Exception('Segment needs the JSON PHP extension.'); } From 9090ec0bfcdfb86505dd9c734882171b458bee5e Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 00:51:46 +0200 Subject: [PATCH 083/151] Major cleanup! --- .gitignore | 2 + bin/analytics | 150 +++++------ composer.json | 12 +- lib/Client.php | 389 ++++++++++++++--------------- lib/Consumer/Consumer.php | 173 +++++++------ lib/Consumer/File.php | 159 ++++++------ lib/Consumer/ForkCurl.php | 80 +++--- lib/Consumer/LibCurl.php | 89 +++---- lib/Consumer/QueueConsumer.php | 288 ++++++++++----------- lib/Consumer/Socket.php | 236 +++++++++--------- lib/Version.php | 4 +- phpcs.xml | 30 ++- send.php | 83 ++++--- test/AnalyticsTest.php | 442 ++++++++++++++++++++------------- test/ClientTest.php | 21 +- test/ConsumerFileTest.php | 210 ++++++++-------- test/ConsumerForkCurlTest.php | 126 +++++----- test/ConsumerLibCurlTest.php | 157 ++++++------ test/ConsumerSocketTest.php | 312 ++++++++++++----------- 19 files changed, 1545 insertions(+), 1418 deletions(-) diff --git a/.gitignore b/.gitignore index 373b920..a3a88b5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ test/analytics.log /.vscode .phplint-cache /.idea +clover.xml +.phpunit.result.cache diff --git a/bin/analytics b/bin/analytics index 1472054..f08aa39 100755 --- a/bin/analytics +++ b/bin/analytics @@ -1,106 +1,94 @@ #!/usr/bin/env php $options['userId'], - 'event' => $options['event'], - 'properties' => parse_json($options['properties']) - )); - break; - - case 'identify': - Segment::identify(array( - 'userId' => $options['userId'], - 'traits' => parse_json($options['traits']) - )); - break; - - case 'page': - Segment::page(array( - 'userId' => $options['userId'], - 'name' => $options['name'], - 'properties' => parse_json($options['properties']) - )); - break; - - case 'group': - Segment::identify(array( - 'userId' => $options['userId'], - 'groupId' => $options['groupId'], - 'traits' => parse_json($options['traits']) - )); - break; - - case 'alias': - Segment::alias(array( - 'userId' => $options['userId'], - 'previousId' => $options['previousId'] - )); - break; - - default: - error(usage()); - break; + case 'track': + Segment::track(array( + 'userId' => $options['userId'], + 'event' => $options['event'], + 'properties' => parse_json($options['properties']) + )); + break; + case 'identify': + Segment::identify(array( + 'userId' => $options['userId'], + 'traits' => parse_json($options['traits']) + )); + break; + case 'page': + Segment::page(array( + 'userId' => $options['userId'], + 'name' => $options['name'], + 'properties' => parse_json($options['properties']) + )); + break; + case 'group': + Segment::identify(array( + 'userId' => $options['userId'], + 'groupId' => $options['groupId'], + 'traits' => parse_json($options['traits']) + )); + break; + case 'alias': + Segment::alias(array( + 'userId' => $options['userId'], + 'previousId' => $options['previousId'] + )); + break; + default: + error(usage()); } Segment::flush(); -function usage() { - return "\n Usage: analytics --type [options]\n\n"; +function usage(): string +{ + return "\n Usage: analytics --type [options]\n\n"; } -function error($message) { - print("$message\n\n"); - exit(1); -} - -function parse_json($input) { - if (empty($input)) { - return null; - } - - return json_decode($input); +function error($message): void +{ + print("$message\n\n"); + exit(1); } -function parse_timestamp($input) { - if (empty($input)) { - return null; - } +function parse_json($input): ?array +{ + if (empty($input)) { + return null; + } - return strtotime($input); + return json_decode($input, true); } diff --git a/composer.json b/composer.json index 7b34d1e..72628bc 100644 --- a/composer.json +++ b/composer.json @@ -21,9 +21,15 @@ "ext-json": "*" }, "require-dev": { - "phpunit/phpunit": "~9.0", - "overtrue/phplint": "^1.1", - "squizlabs/php_codesniffer": "^3.3" + "phpunit/phpunit": "~9.5", + "overtrue/phplint": "^3.0", + "squizlabs/php_codesniffer": "^3.6", + "roave/security-advisories": "dev-latest", + "slevomat/coding-standard": "^7.0" + }, + "suggest": { + "ext-curl": "For using the curl HTTP client", + "ext-zlib": "For using compression" }, "autoload": { "psr-4": { diff --git a/lib/Client.php b/lib/Client.php index da217ca..b757d91 100644 --- a/lib/Client.php +++ b/lib/Client.php @@ -1,45 +1,47 @@ Socket::class, - "file" => File::class, - "fork_curl" => ForkCurl::class, - "lib_curl" => LibCurl::class - ); - // Use our socket libcurl by default - $consumer_type = isset($options["consumer"]) ? $options["consumer"] : - "lib_curl"; + $consumers = [ + 'socket' => Socket::class, + 'file' => File::class, + 'fork_curl' => ForkCurl::class, + 'lib_curl' => LibCurl::class, + ]; + // Use our socket libcurl by default + $consumer_type = $options['consumer'] ?? 'lib_curl'; if (!array_key_exists($consumer_type, $consumers) && class_exists($consumer_type)) { if (!is_subclass_of($consumer_type, Consumer::class)) { - throw new \Exception('Consumers must extend the Segment/Consumer/Consumer abstract class'); + throw new Exception('Consumers must extend the Segment/Consumer/Consumer abstract class'); } // Try to resolve it by class name - $this->consumer = new $consumer_type($secret, $options); + $this->consumer = new $consumer_type($secret, $options); return; } @@ -53,204 +55,125 @@ public function __destruct() $this->consumer->__destruct(); } - /** - * Tracks a user action - * - * @param array $message - * @return [boolean] whether the track call succeeded - */ - public function track(array $message) + /** + * Tracks a user action + * + * @param array $message + * @return bool whether the track call succeeded + */ + public function track(array $message): bool { - $message = $this->message($message, "properties"); - $message["type"] = "track"; + $message = $this->message($message, 'properties'); + $message['type'] = 'track'; return $this->consumer->track($message); } - /** - * Tags traits about the user. - * - * @param [array] $message - * @return [boolean] whether the track call succeeded - */ - public function identify(array $message) - { - $message = $this->message($message, "traits"); - $message["type"] = "identify"; - - return $this->consumer->identify($message); - } - - /** - * Tags traits about the group. - * - * @param [array] $message - * @return [boolean] whether the group call succeeded - */ - public function group(array $message) - { - $message = $this->message($message, "traits"); - $message["type"] = "group"; - - return $this->consumer->group($message); - } - - /** - * Tracks a page view. - * - * @param [array] $message - * @return [boolean] whether the page call succeeded - */ - public function page(array $message) - { - $message = $this->message($message, "properties"); - $message["type"] = "page"; - - return $this->consumer->page($message); - } + /** + * Add common fields to the given `message` + * + * @param array $msg + * @param string $def + * @return array + */ - /** - * Tracks a screen view. - * - * @param [array] $message - * @return [boolean] whether the screen call succeeded - */ - public function screen(array $message) + private function message(array $msg, string $def = ''): array { - $message = $this->message($message, "properties"); - $message["type"] = "screen"; - - return $this->consumer->screen($message); - } + if ($def && !isset($msg[$def])) { + $msg[$def] = []; + } + if ($def && empty($msg[$def])) { + $msg[$def] = (object)$msg[$def]; + } - /** - * Aliases from one user id to another - * - * @param array $message - * @return boolean whether the alias call succeeded - */ - public function alias(array $message) - { - $message = $this->message($message); - $message["type"] = "alias"; + if (!isset($msg['context'])) { + $msg['context'] = []; + } + $msg['context'] = array_merge($this->getDefaultContext(), $msg['context']); - return $this->consumer->alias($message); - } + if (!isset($msg['timestamp'])) { + $msg['timestamp'] = null; + } + $msg['timestamp'] = $this->formatTime((int)$msg['timestamp']); - /** - * Flush any async consumers - * @return boolean true if flushed successfully - */ - public function flush() - { - if (method_exists($this->consumer, 'flush')) { - return $this->consumer->flush(); + if (!isset($msg['messageId'])) { + $msg['messageId'] = self::messageId(); } - return true; + return $msg; } /** - * @return Segment\Consumer\Consumer + * Add the segment.io context to the request + * @return array additional context */ - public function getConsumer() + private function getDefaultContext(): array { - return $this->consumer; - } + global $SEGMENT_VERSION; + return [ + 'library' => [ + 'name' => 'analytics-php', + 'version' => $SEGMENT_VERSION, + 'consumer' => $this->consumer->getConsumer(), + ], + ]; + } - /** - * Formats a timestamp by making sure it is set - * and converting it to iso8601. - * - * The timestamp can be time in seconds `time()` or `microtime(true)`. - * any other input is considered an error and the method will return a new date. - * - * Note: php's date() "u" format (for microseconds) has a bug in it - * it always shows `.000` for microseconds since `date()` only accepts - * ints, so we have to construct the date ourselves if microtime is passed. - * - * @param ts $timestamp - time in seconds (time()) - */ + /** + * Formats a timestamp by making sure it is set + * and converting it to iso8601. + * + * The timestamp can be time in seconds `time()` or `microtime(true)`. + * any other input is considered an error and the method will return a new date. + * + * Note: php's date() "u" format (for microseconds) has a bug in it + * it always shows `.000` for microseconds since `date()` only accepts + * ints, so we have to construct the date ourselves if microtime is passed. + * + * @param mixed $timestamp time in seconds (time()) or a time expression string + */ private function formatTime($ts) { - // time() - if (null == $ts || !$ts) { + if (!$ts) { $ts = time(); } - if (false !== filter_var($ts, FILTER_VALIDATE_INT)) { - return date("c", (int) $ts); + if (filter_var($ts, FILTER_VALIDATE_INT) !== false) { + return date('c', (int)$ts); } - // anything else try to strtotime the date. - if (false === filter_var($ts, FILTER_VALIDATE_FLOAT)) { + // anything else try to strtotime the date. + if (filter_var($ts, FILTER_VALIDATE_FLOAT) === false) { if (is_string($ts)) { - return date("c", strtotime($ts)); + return date('c', strtotime($ts)); } - return date("c"); + return date('c'); } - // fix for floatval casting in send.php - $parts = explode(".", (string)$ts); + // fix for floatval casting in send.php + $parts = explode('.', (string)$ts); if (!isset($parts[1])) { - return date("c", (int)$parts[0]); + return date('c', (int)$parts[0]); } - // microtime(true) - $sec = $parts[0]; - $usec = $parts[1]; - $fmt = sprintf("Y-m-d\TH:i:s.%sP", $usec); + $fmt = sprintf('Y-m-d\TH:i:s.%sP', $parts[1]); - return date($fmt, (int)$sec); + return date($fmt, (int)$parts[0]); } - /** - * Add common fields to the given `message` - * - * @param array $msg - * @param string $def - * @return array - */ - - private function message($msg, $def = "") - { - if ($def && !isset($msg[$def])) { - $msg[$def] = array(); - } - if ($def && empty($msg[$def])) { - $msg[$def] = (object)$msg[$def]; - } - - if (!isset($msg["context"])) { - $msg["context"] = array(); - } - $msg["context"] = array_merge($this->getDefaultContext(), $msg["context"]); - - if (!isset($msg["timestamp"])) { - $msg["timestamp"] = null; - } - $msg["timestamp"] = $this->formatTime($msg["timestamp"]); - - if (!isset($msg["messageId"])) { - $msg["messageId"] = self::messageId(); - } - - return $msg; - } - - /** - * Generate a random messageId. - * - * https://gist.github.com/dahnielson/508447#file-uuid-php-L74 - * - * @return string - */ + /** + * Generate a random messageId. + * + * https://gist.github.com/dahnielson/508447#file-uuid-php-L74 + * + * @return string + */ - private static function messageId() + private static function messageId(): string { return sprintf( - "%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), @@ -262,20 +185,94 @@ private static function messageId() ); } - /** - * Add the segment.io context to the request - * @return array additional context - */ - private function getDefaultContext() + /** + * Tags traits about the user. + * + * @param array $message + * @return bool whether the track call succeeded + */ + public function identify(array $message): bool { - global $SEGMENT_VERSION; + $message = $this->message($message, 'traits'); + $message['type'] = 'identify'; - return array( - "library" => array( - "name" => "analytics-php", - "version" => $SEGMENT_VERSION, - "consumer" => $this->consumer->getConsumer() - ) - ); + return $this->consumer->identify($message); + } + + /** + * Tags traits about the group. + * + * @param array $message + * @return bool whether the group call succeeded + */ + public function group(array $message): bool + { + $message = $this->message($message, 'traits'); + $message['type'] = 'group'; + + return $this->consumer->group($message); + } + + /** + * Tracks a page view. + * + * @param array $message + * @return bool whether the page call succeeded + */ + public function page(array $message): bool + { + $message = $this->message($message, 'properties'); + $message['type'] = 'page'; + + return $this->consumer->page($message); + } + + /** + * Tracks a screen view. + * + * @param array $message + * @return bool whether the screen call succeeded + */ + public function screen(array $message): bool + { + $message = $this->message($message, 'properties'); + $message['type'] = 'screen'; + + return $this->consumer->screen($message); + } + + /** + * Aliases from one user id to another + * + * @param array $message + * @return bool whether the alias call succeeded + */ + public function alias(array $message): bool + { + $message = $this->message($message); + $message['type'] = 'alias'; + + return $this->consumer->alias($message); + } + + /** + * Flush any async consumers + * @return bool true if flushed successfully + */ + public function flush(): bool + { + if (method_exists($this->consumer, 'flush')) { + return $this->consumer->flush(); + } + + return true; + } + + /** + * @return Consumer + */ + public function getConsumer(): Consumer + { + return $this->consumer; } } diff --git a/lib/Consumer/Consumer.php b/lib/Consumer/Consumer.php index 90bfbb0..b9b87f1 100644 --- a/lib/Consumer/Consumer.php +++ b/lib/Consumer/Consumer.php @@ -1,100 +1,106 @@ + */ + protected array $options; + protected string $secret; - /** - * Store our secret and options as part of this consumer - * @param string $secret - * @param array $options - */ - public function __construct($secret, $options = array()) + /** + * Store our secret and options as part of this consumer + * @param string $secret + * @param array $options + */ + public function __construct(string $secret, array $options = []) { $this->secret = $secret; $this->options = $options; } - /** - * Tracks a user action - * - * @param array $message - * @return boolean whether the track call succeeded - */ - abstract public function track(array $message); - - /** - * Tags traits about the user. - * - * @param array $message - * @return boolean whether the identify call succeeded - */ - abstract public function identify(array $message); - - /** - * Tags traits about the group. - * - * @param array $message - * @return boolean whether the group call succeeded - */ - abstract public function group(array $message); - - /** - * Tracks a page view. - * - * @param array $message - * @return boolean whether the page call succeeded - */ - abstract public function page(array $message); - - /** - * Tracks a screen view. - * - * @param array $message - * @return boolean whether the group call succeeded - */ - abstract public function screen(array $message); - - /** - * Aliases from one user id to another - * - * @param array $message - * @return boolean whether the alias call succeeded - */ - abstract public function alias(array $message); - - /** - * Check whether debug mode is enabled - * @return boolean - */ - protected function debug() + /** + * Tracks a user action + * + * @param array $message + * @return bool whether the track call succeeded + */ + abstract public function track(array $message): bool; + + /** + * Tags traits about the user. + * + * @param array $message + * @return bool whether the identify call succeeded + */ + abstract public function identify(array $message): bool; + + /** + * Tags traits about the group. + * + * @param array $message + * @return bool whether the group call succeeded + */ + abstract public function group(array $message): bool; + + /** + * Tracks a page view. + * + * @param array $message + * @return bool whether the page call succeeded + */ + abstract public function page(array $message): bool; + + /** + * Tracks a screen view. + * + * @param array $message + * @return bool whether the group call succeeded + */ + abstract public function screen(array $message): bool; + + /** + * Aliases from one user id to another + * + * @param array $message + * @return bool whether the alias call succeeded + */ + abstract public function alias(array $message): bool; + + /** + * Getter method for consumer type. + * + * @return string + */ + public function getConsumer(): string { - return isset($this->options["debug"]) ? $this->options["debug"] : false; + return $this->type; } - /** - * Check whether we should connect to the API using SSL. This is enabled by - * default with connections which make batching requests. For connections - * which can save on round-trip times, you may disable it. - * @return boolean - */ - protected function ssl() + /** + * Check whether we should connect to the API using SSL. This is enabled by + * default with connections which make batching requests. For connections + * which can save on round-trip times, you may disable it. + * @return bool + */ + protected function ssl(): bool { - return isset($this->options["ssl"]) ? $this->options["ssl"] : true; + return $this->options['ssl'] ?? true; } - /** - * On an error, try and call the error handler, if debugging output to - * error_log as well. - * @param string $code - * @param string $msg - */ - protected function handleError($code, $msg) + /** + * On an error, try and call the error handler, if debugging output to + * error_log as well. + * @param int $code + * @param string $msg + */ + protected function handleError(int $code, string $msg): void { if (isset($this->options['error_handler'])) { $handler = $this->options['error_handler']; @@ -102,7 +108,16 @@ protected function handleError($code, $msg) } if ($this->debug()) { - error_log("[Analytics][" . $this->type . "] " . $msg); + error_log('[Analytics][' . $this->type . '] ' . $msg); } } + + /** + * Check whether debug mode is enabled + * @return bool + */ + protected function debug(): bool + { + return $this->options['debug'] ?? false; + } } diff --git a/lib/Consumer/File.php b/lib/Consumer/File.php index 491b6f6..a390a39 100644 --- a/lib/Consumer/File.php +++ b/lib/Consumer/File.php @@ -1,35 +1,42 @@ file_handle = fopen($options["filename"], "a"); - if (isset($options["filepermissions"])) { - chmod($options["filename"], $options["filepermissions"]); + $this->file_handle = fopen($options['filename'], 'ab'); + if (isset($options['filepermissions'])) { + chmod($options['filename'], $options['filepermissions']); } else { - chmod($options["filename"], 0777); + chmod($options['filename'], 0777); } - } catch (\Exception $e) { + } catch (Exception $e) { $this->handleError($e->getCode(), $e->getMessage()); } } @@ -38,98 +45,92 @@ public function __destruct() { if ( $this->file_handle && - "Unknown" != get_resource_type($this->file_handle) + get_resource_type($this->file_handle) !== 'Unknown' ) { fclose($this->file_handle); } } - //define getter method for consumer type - public function getConsumer() - { - return $this->type; - } - - /** - * Tracks a user action - * - * @param array $message - * @return [boolean] whether the track call succeeded - */ - public function track(array $message) + /** + * Tracks a user action + * + * @param array $message + * @return bool whether the track call succeeded + */ + public function track(array $message): bool { return $this->write($message); } - /** - * Tags traits about the user. - * - * @param array $message - * @return [boolean] whether the identify call succeeded - */ - public function identify(array $message) + /** + * Writes the API call to a file as line-delimited json + * @param array $body post body content. + * @return bool whether the request succeeded + */ + private function write(array $body): bool { - return $this->write($message); + if (!$this->file_handle) { + return false; + } + + $content = json_encode($body); + $content .= "\n"; + + return fwrite($this->file_handle, $content) === strlen($content); } - /** - * Tags traits about the group. - * - * @param array $message - * @return [boolean] whether the group call succeeded - */ - public function group(array $message) + /** + * Tags traits about the user. + * + * @param array $message + * @return bool whether the identify call succeeded + */ + public function identify(array $message): bool { return $this->write($message); } - /** - * Tracks a page view. - * - * @param array $message - * @return [boolean] whether the page call succeeded - */ - public function page(array $message) + /** + * Tags traits about the group. + * + * @param array $message + * @return bool whether the group call succeeded + */ + public function group(array $message): bool { return $this->write($message); } - /** - * Tracks a screen view. - * - * @param array $message - * @return [boolean] whether the screen call succeeded - */ - public function screen(array $message) + /** + * Tracks a page view. + * + * @param array $message + * @return bool whether the page call succeeded + */ + public function page(array $message): bool { return $this->write($message); } - /** - * Aliases from one user id to another - * - * @param array $message - * @return boolean whether the alias call succeeded - */ - public function alias(array $message) + /** + * Tracks a screen view. + * + * @param array $message + * @return bool whether the screen call succeeded + */ + public function screen(array $message): bool { return $this->write($message); } - /** - * Writes the API call to a file as line-delimited json - * @param [array] $body post body content. - * @return [boolean] whether the request succeeded - */ - private function write($body) + /** + * Aliases from one user id to another + * + * @param array $message + * @return bool whether the alias call succeeded + */ + public function alias(array $message): bool { - if (!$this->file_handle) { - return false; - } - - $content = json_encode($body); - $content .= "\n"; - - return fwrite($this->file_handle, $content) == strlen($content); + return $this->write($message); } } diff --git a/lib/Consumer/ForkCurl.php b/lib/Consumer/ForkCurl.php index 20f3eef..0466efc 100644 --- a/lib/Consumer/ForkCurl.php +++ b/lib/Consumer/ForkCurl.php @@ -1,65 +1,47 @@ type; - } - - /** - * Make an async request to our API. Fork a curl process, immediately send - * to the API. If debug is enabled, we wait for the response. - * @param array $messages array of all the messages to send - * @return boolean whether the request succeeded - */ - public function flushBatch($messages) + protected string $type = 'ForkCurl'; + + /** + * Make an async request to our API. Fork a curl process, immediately send + * to the API. If debug is enabled, we wait for the response. + * @param array $messages array of all the messages to send + * @return bool whether the request succeeded + */ + public function flushBatch(array $messages): bool { $body = $this->payload($messages); $payload = json_encode($body); - // Escape for shell usage. + // Escape for shell usage. $payload = escapeshellarg($payload); $secret = escapeshellarg($this->secret); - $protocol = $this->ssl() ? "https://" : "http://"; + $protocol = $this->ssl() ? 'https://' : 'http://'; if ($this->host) { $host = $this->host; } else { - $host = "api.segment.io"; + $host = 'api.segment.io'; } - $path = "/v1/batch"; + $path = '/v1/batch'; $url = $protocol . $host . $path; - $cmd = "curl -u ${secret}: -X POST -H 'Content-Type: application/json'"; + $cmd = "curl -u $secret: -X POST -H 'Content-Type: application/json'"; - $tmpfname = ""; + $tmpfname = ''; if ($this->compress_request) { - // Compress request to file - $tmpfname = tempnam("/tmp", "forkcurl_"); - $cmd2 = "echo " . $payload . " | gzip > " . $tmpfname; + // Compress request to file + $tmpfname = tempnam('/tmp', 'forkcurl_'); + $cmd2 = 'echo ' . $payload . ' | gzip > ' . $tmpfname; exec($cmd2, $output, $exit); - if (0 != $exit) { + if ($exit !== 0) { $this->handleError($exit, $output); return false; } @@ -68,39 +50,39 @@ public function flushBatch($messages) $cmd .= " --data-binary '@" . $tmpfname . "'"; } else { - $cmd .= " -d " . $payload; + $cmd .= ' -d ' . $payload; } $cmd .= " '" . $url . "'"; - // Verify message size is below than 32KB + // Verify message size is below than 32KB if (strlen($payload) >= 32 * 1024) { - $msg = "Message size is larger than 32KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); + $msg = 'Message size is larger than 32KB'; + error_log('[Analytics][' . $this->type . '] ' . $msg); return false; } - // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. + // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. $library = $messages[0]['context']['library']; $libName = $library['name']; $libVersion = $library['version']; - $cmd .= " -H 'User-Agent: ${libName}/${libVersion}'"; + $cmd .= " -H 'User-Agent: $libName/$libVersion'"; if (!$this->debug()) { - $cmd .= " > /dev/null 2>&1 &"; + $cmd .= ' > /dev/null 2>&1 &'; } exec($cmd, $output, $exit); - if (0 != $exit) { + if ($exit !== 0) { $this->handleError($exit, $output); } - if ($tmpfname != "") { + if ($tmpfname !== '') { unlink($tmpfname); } - return 0 == $exit; + return $exit === 0; } } diff --git a/lib/Consumer/LibCurl.php b/lib/Consumer/LibCurl.php index 7291354..ffd6bee 100644 --- a/lib/Consumer/LibCurl.php +++ b/lib/Consumer/LibCurl.php @@ -1,38 +1,21 @@ type; - } - - /** - * Make a sync request to our API. If debug is - * enabled, we wait for the response - * and retry once to diminish impact on performance. - * @param array $messages array of all the messages to send - * @return boolean whether the request succeeded - */ - public function flushBatch($messages) + protected string $type = 'LibCurl'; + + /** + * Make a sync request to our API. If debug is + * enabled, we wait for the response + * and retry once to diminish impact on performance. + * @param array $messages array of all the messages to send + * @return bool whether the request succeeded + */ + public function flushBatch(array $messages): bool { $body = $this->payload($messages); $payload = json_encode($body); @@ -42,79 +25,75 @@ public function flushBatch($messages) $payload = gzencode($payload); } - $protocol = $this->ssl() ? "https://" : "http://"; + $protocol = $this->ssl() ? 'https://' : 'http://'; if ($this->host) { $host = $this->host; } else { - $host = "api.segment.io"; + $host = 'api.segment.io'; } - $path = "/v1/batch"; + $path = '/v1/batch'; $url = $protocol . $host . $path; - $backoff = 100; // Set initial waiting time to 100ms + $backoff = 100; // Set initial waiting time to 100ms while ($backoff < $this->maximum_backoff_duration) { - $start_time = microtime(true); - - // open connection + // open connection $ch = curl_init(); - // set the url, number of POST vars, POST data + // set the url, number of POST vars, POST data curl_setopt($ch, CURLOPT_USERPWD, $secret . ':'); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); - // set variables for headers - $header = array(); + // set variables for headers + $header = []; $header[] = 'Content-Type: application/json'; if ($this->compress_request) { $header[] = 'Content-Encoding: gzip'; } - // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. + // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. $library = $messages[0]['context']['library']; $libName = $library['name']; $libVersion = $library['version']; - $header[] = "User-Agent: ${libName}/${libVersion}"; + $header[] = "User-Agent: $libName/$libVersion"; curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - // retry failed requests just once to diminish impact on performance + // retry failed requests just once to diminish impact on performance $responseContent = curl_exec($ch); $err = curl_error($ch); if ($err) { $this->handleError(curl_errno($ch), $err); - return; + return false; } - $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $responseCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE); - //close connection + //close connection curl_close($ch); - $elapsed_time = microtime(true) - $start_time; - - if (200 != $responseCode) { - // log error + if ($responseCode !== 200) { + // log error $this->handleError($responseCode, $responseContent); - if (($responseCode >= 500 && $responseCode <= 600) || 429 == $responseCode) { - // If status code is greater than 500 and less than 600, it indicates server error - // Error code 429 indicates rate limited. - // Retry uploading in these cases. + if (($responseCode >= 500 && $responseCode <= 600) || $responseCode === 429) { + // If status code is greater than 500 and less than 600, it indicates server error + // Error code 429 indicates rate limited. + // Retry uploading in these cases. usleep($backoff * 1000); $backoff *= 2; } elseif ($responseCode >= 400) { break; } } else { - break; // no error + break; // no error } } - return $responseCode; + return true; } } diff --git a/lib/Consumer/QueueConsumer.php b/lib/Consumer/QueueConsumer.php index 0220922..c597715 100644 --- a/lib/Consumer/QueueConsumer.php +++ b/lib/Consumer/QueueConsumer.php @@ -1,153 +1,90 @@ + */ + protected array $queue; + protected int $max_queue_size = 10000; + protected int $max_queue_size_bytes = 33554432; //32M + protected int $flush_at = 100; + protected int $max_batch_size_bytes = 512000; //500kb + protected int $max_item_size_bytes = 32000; // 32kb + protected int $maximum_backoff_duration = 10000; // Set maximum waiting limit to 10s + protected string $host = ''; + protected bool $compress_request = false; + protected int $flush_interval_in_mills = 10000; //frequency in milliseconds to send data, default 10 + + /** + * Store our secret and options as part of this consumer + * @param string $secret + * @param array $options + */ + public function __construct(string $secret, array $options = []) { parent::__construct($secret, $options); - if (isset($options["max_queue_size"])) { - $this->max_queue_size = $options["max_queue_size"]; + if (isset($options['max_queue_size'])) { + $this->max_queue_size = $options['max_queue_size']; } - if (isset($options["batch_size"])) { - if ($options["batch_size"] < 1) { - $msg = "Batch Size must not be less than 1"; - error_log("[Analytics][" . $this->type . "] " . $msg); + if (isset($options['batch_size'])) { + if ($options['batch_size'] < 1) { + $msg = 'Batch Size must not be less than 1'; + error_log('[Analytics][' . $this->type . '] ' . $msg); } else { - $msg = "WARNING: batch_size option to be depricated soon, please use new option flush_at"; - error_log("[Analytics][" . $this->type . "] " . $msg); - $this->flush_at = $options["batch_size"]; + $msg = 'WARNING: batch_size option to be depricated soon, please use new option flush_at'; + error_log('[Analytics][' . $this->type . '] ' . $msg); + $this->flush_at = $options['batch_size']; } } - if (isset($options["flush_at"])) { - if ($options["flush_at"] < 1) { - $msg = "Flush at Size must not be less than 1"; - error_log("[Analytics][" . $this->type . "] " . $msg); + if (isset($options['flush_at'])) { + if ($options['flush_at'] < 1) { + $msg = 'Flush at Size must not be less than 1'; + error_log('[Analytics][' . $this->type . '] ' . $msg); } else { - $this->flush_at = $options["flush_at"]; + $this->flush_at = $options['flush_at']; } } - if (isset($options["host"])) { - $this->host = $options["host"]; + if (isset($options['host'])) { + $this->host = $options['host']; } - if (isset($options["compress_request"])) { - $this->compress_request = json_decode($options["compress_request"]); + if (isset($options['compress_request'])) { + $this->compress_request = (bool)$options['compress_request']; } - if (isset($options["flush_interval"])) { - if ($options["flush_interval"] < 1000) { - $msg = "Flush interval must not be less than 1 second"; - error_log("[Analytics][" . $this->type . "] " . $msg); + if (isset($options['flush_interval'])) { + if ($options['flush_interval'] < 1000) { + $msg = 'Flush interval must not be less than 1 second'; + error_log('[Analytics][' . $this->type . '] ' . $msg); } else { - $this->flush_interval_in_mills = $options["flush_interval"]; + $this->flush_interval_in_mills = $options['flush_interval']; } } - $this->queue = array(); + $this->queue = []; } public function __destruct() { - // Flush our queue on destruction + // Flush our queue on destruction $this->flush(); } - /** - * Tracks a user action - * - * @param array $message - * @return boolean whether the track call succeeded - */ - public function track(array $message) - { - return $this->enqueue($message); - } - - /** - * Tags traits about the user. - * - * @param array $message - * @return boolean whether the identify call succeeded - */ - public function identify(array $message) - { - return $this->enqueue($message); - } - - /** - * Tags traits about the group. - * - * @param array $message - * @return boolean whether the group call succeeded - */ - public function group(array $message) - { - return $this->enqueue($message); - } - - /** - * Tracks a page view. - * - * @param array $message - * @return boolean whether the page call succeeded - */ - public function page(array $message) - { - return $this->enqueue($message); - } - - /** - * Tracks a screen view. - * - * @param array $message - * @return boolean whether the screen call succeeded - */ - public function screen(array $message) - { - return $this->enqueue($message); - } - - /** - * Aliases from one user id to another - * - * @param array $message - * @return boolean whether the alias call succeeded - */ - public function alias(array $message) - { - return $this->enqueue($message); - } - - /** - * Flushes our queue of messages by batching them to the server - */ - public function flush() + /** + * Flushes our queue of messages by batching them to the server + */ + public function flush(): bool { $count = count($this->queue); $success = true; @@ -156,8 +93,8 @@ public function flush() $batch = array_splice($this->queue, 0, min($this->flush_at, $count)); if (mb_strlen(serialize($batch), '8bit') >= $this->max_batch_size_bytes) { - $msg = "Batch size is larger than 500KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); + $msg = 'Batch size is larger than 500KB'; + error_log('[Analytics][' . $this->type . '] ' . $msg); return false; } @@ -174,12 +111,23 @@ public function flush() return $success; } - /** - * Adds an item to our queue. - * @param mixed $item - * @return boolean whether call has succeeded - */ - protected function enqueue($item) + /** + * Tracks a user action + * + * @param array $message + * @return bool whether the track call succeeded + */ + public function track(array $message): bool + { + return $this->enqueue($message); + } + + /** + * Adds an item to our queue. + * @param mixed $item + * @return bool whether call has succeeded + */ + protected function enqueue($item): bool { $count = count($this->queue); @@ -187,42 +135,96 @@ protected function enqueue($item) return false; } - if (mb_strlen(serialize((array)$this->queue), '8bit') >= $this->max_queue_size_bytes) { - $msg = "Queue size is larger than 32MB"; - error_log("[Analytics][" . $this->type . "] " . $msg); + if (mb_strlen(serialize($this->queue), '8bit') >= $this->max_queue_size_bytes) { + $msg = 'Queue size is larger than 32MB'; + error_log('[Analytics][' . $this->type . '] ' . $msg); return false; } if (mb_strlen(json_encode($item), '8bit') >= $this->max_item_size_bytes) { - $msg = "Item size is larger than 32KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); + $msg = 'Item size is larger than 32KB'; + error_log('[Analytics][' . $this->type . '] ' . $msg); return false; } $count = array_push($this->queue, $item); - if ($count >= $this->flush_at) { - return $this->flush(); // return ->flush() result: true on success + return $this->flush(); } return true; } - /** - * Given a batch of messages the method returns - * a valid payload. - * - * @param {Array} $batch - * @return {Array} - */ - protected function payload($batch) + /** + * Tags traits about the user. + * + * @param array $message + * @return bool whether the identify call succeeded + */ + public function identify(array $message): bool + { + return $this->enqueue($message); + } + + /** + * Tags traits about the group. + * + * @param array $message + * @return bool whether the group call succeeded + */ + public function group(array $message): bool + { + return $this->enqueue($message); + } + + /** + * Tracks a page view. + * + * @param array $message + * @return bool whether the page call succeeded + */ + public function page(array $message): bool + { + return $this->enqueue($message); + } + + /** + * Tracks a screen view. + * + * @param array $message + * @return bool whether the screen call succeeded + */ + public function screen(array $message): bool + { + return $this->enqueue($message); + } + + /** + * Aliases from one user id to another + * + * @param array $message + * @return bool whether the alias call succeeded + */ + public function alias(array $message): bool + { + return $this->enqueue($message); + } + + /** + * Given a batch of messages the method returns + * a valid payload. + * + * @param array $batch + * @return array + */ + protected function payload(array $batch): array { - return array( - "batch" => $batch, - "sentAt" => date("c"), - ); + return [ + 'batch' => $batch, + 'sentAt' => date('c'), + ]; } } diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index ffef645..64651dd 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -1,82 +1,81 @@ type; - } - - public function flushBatch($batch) + public function flushBatch($batch): bool { $socket = $this->createSocket(); if (!$socket) { - return; + return false; } $payload = $this->payload($batch); $payload = json_encode($payload); - $body = $this->createBody($this->options["host"], $payload); - if (false === $body) { + $body = $this->createBody($this->options['host'], $payload); + if ($body === false) { return false; } return $this->makeRequest($socket, $body); } + /** + * @return false|resource + */ private function createSocket() { if ($this->socket_failed) { return false; } - $protocol = $this->ssl() ? "ssl" : "tcp"; - $host = $this->options["host"]; + $protocol = $this->ssl() ? 'ssl' : 'tcp'; + $host = $this->options['host']; $port = $this->ssl() ? 443 : 80; - $timeout = $this->options["timeout"]; + $timeout = $this->options['timeout']; try { - // Open our socket to the API Server. - // Since we're try catch'ing prevent PHP logs. + // Open our socket to the API Server. + // Since we're try catch'ing prevent PHP logs. $socket = @pfsockopen( - $protocol . "://" . $host, + $protocol . '://' . $host, $port, $errno, $errstr, $timeout ); - // If we couldn't open the socket, handle the error. - if (false === $socket) { + // If we couldn't open the socket, handle the error. + if ($socket === false) { $this->handleError($errno, $errstr); $this->socket_failed = true; @@ -84,7 +83,7 @@ private function createSocket() } return $socket; - } catch (Exception $e) { + } catch (\Exception $e) { $this->handleError($e->getCode(), $e->getMessage()); $this->socket_failed = true; @@ -92,31 +91,74 @@ private function createSocket() } } - /** - * Attempt to write the request to the socket, wait for response if debug - * mode is enabled. - * @param stream $socket the handle for the socket - * @param string $req request body - * @param boolean $retry - * @return boolean $success - */ - private function makeRequest($socket, $req, $retry = true) + /** + * Create the body to send as the post request. + * @param string $host + * @param string $content + * @return string body + */ + private function createBody(string $host, string $content) + { + $req = "POST /v1/batch HTTP/1.1\r\n"; + $req .= 'Host: ' . $host . "\r\n"; + $req .= "Content-Type: application/json\r\n"; + $req .= 'Authorization: Basic ' . base64_encode($this->secret . ':') . "\r\n"; + $req .= "Accept: application/json\r\n"; + + // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. + $content_json = json_decode($content, true); + $library = $content_json['batch'][0]['context']['library']; + $libName = $library['name']; + $libVersion = $library['version']; + $req .= "User-Agent: $libName/$libVersion\r\n"; + + // Compress content if compress_request is true + if ($this->compress_request) { + $content = gzencode($content); + + $req .= "Content-Encoding: gzip\r\n"; + } + + $req .= 'Content-length: ' . strlen($content) . "\r\n"; + $req .= "\r\n"; + $req .= $content; + + // Verify message size is below than 32KB + if (strlen($req) >= 32 * 1024) { + $msg = 'Message size is larger than 32KB'; + error_log('[Analytics][' . $this->type . '] ' . $msg); + + return false; + } + + return $req; + } + + /** + * Attempt to write the request to the socket, wait for response if debug + * mode is enabled. + * @param resource $socket the handle for the socket + * @param string $req request body + * @param bool $retry + * @return bool + */ + private function makeRequest($socket, string $req, bool $retry = true): bool { $bytes_written = 0; $bytes_total = strlen($req); $closed = false; $success = true; - // Retries with exponential backoff until success - $backoff = 100; // Set initial waiting time to 100ms + // Retries with exponential backoff until success + $backoff = 100; // Set initial waiting time to 100ms while (true) { - // Send request to server + // Send request to server while (!$closed && $bytes_written < $bytes_total) { try { - // Since we're try catch'ing prevent PHP logs. + // Since we're try catch'ing prevent PHP logs. $written = @fwrite($socket, substr($req, $bytes_written)); - } catch (Exception $e) { + } catch (\Exception $e) { $this->handleError($e->getCode(), $e->getMessage()); $closed = true; } @@ -127,26 +169,24 @@ private function makeRequest($socket, $req, $retry = true) } } - // Get response for request + // Get response for request $statusCode = 0; - $errorMessage = ""; if (!$closed) { $res = $this->parseResponse(fread($socket, 2048)); - $statusCode = (int)$res["status"]; - $errorMessage = $res["message"]; + $statusCode = (int)$res['status']; } fclose($socket); - // If status code is 200, return true - if (200 == $statusCode) { + // If status code is 200, return true + if ($statusCode === 200) { return true; } - // If status code is greater than 500 and less than 600, it indicates server error - // Error code 429 indicates rate limited. - // Retry uploading in these cases. - if (($statusCode >= 500 && $statusCode <= 600) || 429 == $statusCode || 0 == $statusCode) { + // If status code is greater than 500 and less than 600, it indicates server error + // Error code 429 indicates rate limited. + // Retry uploading in these cases. + if (($statusCode >= 500 && $statusCode <= 600) || $statusCode === 429 || $statusCode === 0) { if ($backoff >= $this->maximum_backoff_duration) { break; } @@ -154,13 +194,13 @@ private function makeRequest($socket, $req, $retry = true) usleep($backoff * 1000); } elseif ($statusCode >= 400) { if ($this->debug()) { - $this->handleError($res["status"], $res["message"]); + $this->handleError($res['status'], $res['message']); } break; } - // Retry uploading... + // Retry uploading... $backoff *= 2; $socket = $this->createSocket(); } @@ -168,69 +208,25 @@ private function makeRequest($socket, $req, $retry = true) return $success; } - /** - * Create the body to send as the post request. - * @param string $host - * @param string $content - * @return string body - */ - private function createBody($host, $content) - { - $req = ""; - $req .= "POST /v1/batch HTTP/1.1\r\n"; - $req .= "Host: " . $host . "\r\n"; - $req .= "Content-Type: application/json\r\n"; - $req .= "Authorization: Basic " . base64_encode($this->secret . ":") . "\r\n"; - $req .= "Accept: application/json\r\n"; - - // Send user agent in the form of {library_name}/{library_version} as per RFC 7231. - $content_json = json_decode($content, true); - $library = $content_json['batch'][0]['context']['library']; - $libName = $library['name']; - $libVersion = $library['version']; - $req .= "User-Agent: ${libName}/${libVersion}\r\n"; - - // Compress content if compress_request is true - if ($this->compress_request) { - $content = gzencode($content); - - $req .= "Content-Encoding: gzip\r\n"; - } - - $req .= "Content-length: " . strlen($content) . "\r\n"; - $req .= "\r\n"; - $req .= $content; - - // Verify message size is below than 32KB - if (strlen($req) >= 32 * 1024) { - $msg = "Message size is larger than 32KB"; - error_log("[Analytics][" . $this->type . "] " . $msg); - - return false; - } - - return $req; - } - - /** - * Parse our response from the server, check header and body. - * @param string $res - * @return array - * string $status HTTP code, e.g. "200" - * string $message JSON response from the api - */ - private function parseResponse($res) + /** + * Parse our response from the server, check header and body. + * @param string $res + * @return array + * string $status HTTP code, e.g. "200" + * string $message JSON response from the api + */ + private function parseResponse(string $res): array { $contents = explode("\n", $res); - // Response comes back as HTTP/1.1 200 OK - // Final line contains HTTP response. - $status = explode(" ", $contents[0], 3); + // Response comes back as HTTP/1.1 200 OK + // Final line contains HTTP response. + $status = explode(' ', $contents[0], 3); $result = $contents[count($contents) - 1]; - return array( - "status" => isset($status[1]) ? $status[1] : null, - "message" => $result - ); + return [ + 'status' => $status[1] ?? null, + 'message' => $result, + ]; } } diff --git a/lib/Version.php b/lib/Version.php index 7a06e70..fc4eb08 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -1,4 +1,6 @@ The coding standard for analytics-php project. - + ./lib/ ./test/ + + */test/* + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/send.php b/send.php index 7b92349..4b14ad8 100755 --- a/send.php +++ b/send.php @@ -1,5 +1,7 @@ true, - "error_handler" => function($code, $msg){ - print("$code: $msg\n"); - exit(1); - } -)); +Segment::init($args['secret'], [ + 'debug' => true, + 'error_handler' => static function ($code, $msg) { + print("$code: $msg\n"); + exit(1); + }, +]); /** * Payloads @@ -69,26 +77,28 @@ $total = 0; $successful = 0; foreach ($lines as $line) { - if (!trim($line)) continue; + if (!trim($line)) { + continue; + } $total++; $payload = json_decode($line, true); - $dt = new DateTime($payload["timestamp"]); - $ts = floatval($dt->getTimestamp() . "." . $dt->format("u")); - $payload["timestamp"] = date("c", (int) $ts); - $type = $payload["type"]; + $dt = new DateTime($payload['timestamp']); + $ts = (float)($dt->getTimestamp() . '.' . $dt->format('u')); + $payload['timestamp'] = date('c', (int)$ts); + $type = $payload['type']; $currentBatch[] = $payload; // flush before batch gets too big - if (mb_strlen((json_encode(array('batch' => $currentBatch, 'sentAt' => date("c")))), '8bit') >= 512000) { + if (mb_strlen((json_encode(['batch' => $currentBatch, 'sentAt' => date('c')])), '8bit') >= 512000) { $libCurlResponse = Segment::flush(); if ($libCurlResponse) { $successful += count($currentBatch) - 1; - } else { + //} else { // todo: maybe write batch to analytics-error.log for more controlled errorhandling } - $currentBatch = array(); + $currentBatch = []; } - $payload["timestamp"] = $ts; - call_user_func_array(array("Segment\Segment", $type), array($payload)); + $payload['timestamp'] = $ts; + call_user_func([Segment::class, $type], $payload); } $libCurlResponse = Segment::flush(); @@ -108,14 +118,17 @@ * Parse arguments */ -function parse($argv){ - $ret = array(); +function parse($argv): array +{ + $ret = []; - for ($i = 0; $i < count($argv); ++$i) { - $arg = $argv[$i]; - if ('--' != substr($arg, 0, 2)) continue; - $ret[substr($arg, 2, strlen($arg))] = trim($argv[++$i]); - } + foreach ($argv as $i => $iValue) { + $arg = $iValue; + if (strpos($arg, '--') !== 0) { + continue; + } + $ret[substr($arg, 2, strlen($arg))] = trim($argv[++$i]); + } - return $ret; + return $ret; } diff --git a/test/AnalyticsTest.php b/test/AnalyticsTest.php index 0e71619..f04e76b 100644 --- a/test/AnalyticsTest.php +++ b/test/AnalyticsTest.php @@ -1,7 +1,10 @@ true)); + date_default_timezone_set('UTC'); + Segment::init('oq0vdlg7yi', ['debug' => true]); } - public function testTrack() + public function testTrack(): void { - $this->assertTrue(Segment::track(array( - "userId" => "john", - "event" => "Module PHP Event", - ))); + self::assertTrue( + Segment::track( + [ + 'userId' => 'john', + 'event' => 'Module PHP Event', + ] + ) + ); } - public function testGroup() + public function testGroup(): void { - $this->assertTrue(Segment::group(array( - "groupId" => "group-id", - "userId" => "user-id", - "traits" => array( - "plan" => "startup", - ), - ))); + self::assertTrue( + Segment::group( + [ + 'groupId' => 'group-id', + 'userId' => 'user-id', + 'traits' => [ + 'plan' => 'startup', + ], + ] + ) + ); } - public function testGroupAnonymous() + public function testGroupAnonymous(): void { - $this->assertTrue(Segment::group(array( - "groupId" => "group-id", - "anonymousId" => "anonymous-id", - "traits" => array( - "plan" => "startup", - ), - ))); + self::assertTrue( + Segment::group( + [ + 'groupId' => 'group-id', + 'anonymousId' => 'anonymous-id', + 'traits' => [ + 'plan' => 'startup', + ], + ] + ) + ); } - /** - * @expectedException \Exception - * @expectedExceptionMessage Segment::group() requires userId or anonymousId - */ public function testGroupNoUser(): void { - $this->expectException(\Exception::class); - Segment::group(array( - "groupId" => "group-id", - "traits" => array( - "plan" => "startup", - ), - )); + $this->expectExceptionMessage('Segment::group() requires userId or anonymousId'); + $this->expectException(Exception::class); + Segment::group( + [ + 'groupId' => 'group-id', + 'traits' => [ + 'plan' => 'startup', + ], + ] + ); } - public function testMicrotime() + public function testMicrotime(): void { - $this->assertTrue(Segment::page(array( - "anonymousId" => "anonymous-id", - "name" => "analytics-php-microtime", - "category" => "docs", - "timestamp" => microtime(true), - "properties" => array( - "path" => "/docs/libraries/php/", - "url" => "https://segment.io/docs/libraries/php/", - ), - ))); + self::assertTrue( + Segment::page( + [ + 'anonymousId' => 'anonymous-id', + 'name' => 'analytics-php-microtime', + 'category' => 'docs', + 'timestamp' => microtime(true), + 'properties' => [ + 'path' => '/docs/libraries/php/', + 'url' => 'https://segment.io/docs/libraries/php/', + ], + ] + ) + ); } - public function testPage() + public function testPage(): void { - $this->assertTrue(Segment::page(array( - "anonymousId" => "anonymous-id", - "name" => "analytics-php", - "category" => "docs", - "properties" => array( - "path" => "/docs/libraries/php/", - "url" => "https://segment.io/docs/libraries/php/", - ), - ))); + self::assertTrue( + Segment::page( + [ + 'anonymousId' => 'anonymous-id', + 'name' => 'analytics-php', + 'category' => 'docs', + 'properties' => [ + 'path' => '/docs/libraries/php/', + 'url' => 'https://segment.io/docs/libraries/php/', + ], + ] + ) + ); } - public function testBasicPage() + public function testBasicPage(): void { - $this->assertTrue(Segment::page(array( - "anonymousId" => "anonymous-id", - ))); + self::assertTrue(Segment::page(['anonymousId' => 'anonymous-id'])); } - public function testScreen() + public function testScreen(): void { - $this->assertTrue(Segment::screen(array( - "anonymousId" => "anonymous-id", - "name" => "2048", - "category" => "game built with php :)", - "properties" => array( - "points" => 300 - ), - ))); + self::assertTrue( + Segment::screen( + [ + 'anonymousId' => 'anonymous-id', + 'name' => '2048', + 'category' => 'game built with php :)', + 'properties' => [ + 'points' => 300, + ], + ] + ) + ); } - public function testBasicScreen() + public function testBasicScreen(): void { - $this->assertTrue(Segment::screen(array( - "anonymousId" => "anonymous-id" - ))); + self::assertTrue(Segment::screen(['anonymousId' => 'anonymous-id'])); } - public function testIdentify() + public function testIdentify(): void { - $this->assertTrue(Segment::identify(array( - "userId" => "doe", - "traits" => array( - "loves_php" => false, - "birthday" => time(), - ), - ))); + self::assertTrue( + Segment::identify( + [ + 'userId' => 'doe', + 'traits' => [ + 'loves_php' => false, + 'birthday' => time(), + ], + ] + ) + ); } - public function testEmptyTraits() + public function testEmptyTraits(): void { - $this->assertTrue(Segment::identify(array( - "userId" => "empty-traits", - ))); + self::assertTrue(Segment::identify(['userId' => 'empty-traits'])); - $this->assertTrue(Segment::group(array( - "userId" => "empty-traits", - "groupId" => "empty-traits", - ))); + self::assertTrue( + Segment::group( + [ + 'userId' => 'empty-traits', + 'groupId' => 'empty-traits', + ] + ) + ); } - public function testEmptyArrayTraits() + public function testEmptyArrayTraits(): void { - $this->assertTrue(Segment::identify(array( - "userId" => "empty-traits", - "traits" => array(), - ))); + self::assertTrue( + Segment::identify( + [ + 'userId' => 'empty-traits', + 'traits' => [], + ] + ) + ); - $this->assertTrue(Segment::group(array( - "userId" => "empty-traits", - "groupId" => "empty-traits", - "traits" => array(), - ))); + self::assertTrue( + Segment::group( + [ + 'userId' => 'empty-traits', + 'groupId' => 'empty-traits', + 'traits' => [], + ] + ) + ); } - public function testEmptyProperties() + public function testEmptyProperties(): void { - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "empty-properties", - ))); + self::assertTrue( + Segment::track( + [ + 'userId' => 'user-id', + 'event' => 'empty-properties', + ] + ) + ); - $this->assertTrue(Segment::page(array( - "category" => "empty-properties", - "name" => "empty-properties", - "userId" => "user-id", - ))); + self::assertTrue( + Segment::page( + [ + 'category' => 'empty-properties', + 'name' => 'empty-properties', + 'userId' => 'user-id', + ] + ) + ); } - public function testEmptyArrayProperties() + public function testEmptyArrayProperties(): void { - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "empty-properties", - "properties" => array(), - ))); + self::assertTrue( + Segment::track( + [ + 'userId' => 'user-id', + 'event' => 'empty-properties', + 'properties' => [], + ] + ) + ); - $this->assertTrue(Segment::page(array( - "category" => "empty-properties", - "name" => "empty-properties", - "userId" => "user-id", - "properties" => array(), - ))); + self::assertTrue( + Segment::page( + [ + 'category' => 'empty-properties', + 'name' => 'empty-properties', + 'userId' => 'user-id', + 'properties' => [], + ] + ) + ); } - public function testAlias() + public function testAlias(): void { - $this->assertTrue(Segment::alias(array( - "previousId" => "previous-id", - "userId" => "user-id", - ))); + self::assertTrue( + Segment::alias( + [ + 'previousId' => 'previous-id', + 'userId' => 'user-id', + ] + ) + ); } - public function testContextEmpty() + public function testContextEmpty(): void { - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "Context Test", - "context" => array(), - ))); + self::assertTrue( + Segment::track( + [ + 'userId' => 'user-id', + 'event' => 'Context Test', + 'context' => [], + ] + ) + ); } - public function testContextCustom() + public function testContextCustom(): void { - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "Context Test", - "context" => array( - "active" => false, - ), - ))); + self::assertTrue( + Segment::track( + [ + 'userId' => 'user-id', + 'event' => 'Context Test', + 'context' => ['active' => false], + ] + ) + ); } - public function testTimestamps() + public function testTimestamps(): void { - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "integer-timestamp", - "timestamp" => (int) mktime(0, 0, 0, date('n'), 1, date('Y')), - ))); + self::assertTrue( + Segment::track( + [ + 'userId' => 'user-id', + 'event' => 'integer-timestamp', + 'timestamp' => (int)mktime(0, 0, 0, (int)date('n'), 1, (int)date('Y')), + ] + ) + ); + + self::assertTrue( + Segment::track( + [ + 'userId' => 'user-id', + 'event' => 'string-integer-timestamp', + 'timestamp' => (string)mktime(0, 0, 0, (int)date('n'), 1, (int)date('Y')), + ] + ) + ); - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "string-integer-timestamp", - "timestamp" => (string) mktime(0, 0, 0, date('n'), 1, date('Y')), - ))); + self::assertTrue( + Segment::track( + [ + 'userId' => 'user-id', + 'event' => 'iso8630-timestamp', + 'timestamp' => date(DATE_ATOM, mktime(0, 0, 0, (int)date('n'), 1, (int)date('Y'))), + ] + ) + ); - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "iso8630-timestamp", - "timestamp" => date(DATE_ATOM, mktime(0, 0, 0, date('n'), 1, date('Y'))), - ))); + self::assertTrue( + Segment::track( + [ + 'userId' => 'user-id', + 'event' => 'iso8601-timestamp', + 'timestamp' => date(DATE_ATOM, mktime(0, 0, 0, (int)date('n'), 1, (int)date('Y'))), + ] + ) + ); - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "iso8601-timestamp", - "timestamp" => date(DATE_ATOM, mktime(0, 0, 0, date('n'), 1, date('Y'))), - ))); + self::assertTrue( + Segment::track( + [ + 'userId' => 'user-id', + 'event' => 'strtotime-timestamp', + 'timestamp' => strtotime('1 week ago'), + ] + ) + ); - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "strtotime-timestamp", - "timestamp" => strtotime('1 week ago'), - ))); - - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "microtime-timestamp", - "timestamp" => microtime(true), - ))); + self::assertTrue( + Segment::track( + [ + 'userId' => 'user-id', + 'event' => 'microtime-timestamp', + 'timestamp' => microtime(true), + ] + ) + ); - $this->assertTrue(Segment::track(array( - "userId" => "user-id", - "event" => "invalid-float-timestamp", - "timestamp" => ((string) mktime(0, 0, 0, date('n'), 1, date('Y'))) . '.', - ))); + self::assertTrue( + Segment::track( + [ + 'userId' => 'user-id', + 'event' => 'invalid-float-timestamp', + 'timestamp' => ((string)mktime(0, 0, 0, (int)date('n'), 1, (int)date('Y'))) . '.', + ] + ) + ); } } diff --git a/test/ClientTest.php b/test/ClientTest.php index 30076e5..23af9c9 100644 --- a/test/ClientTest.php +++ b/test/ClientTest.php @@ -1,37 +1,36 @@ assertInstanceOf(LibCurl::class, $client->getConsumer()); + self::assertInstanceOf(LibCurl::class, $client->getConsumer()); } /** @test */ - public function can_provide_the_consumer_configuration_as_string() + public function can_provide_the_consumer_configuration_as_string(): void { - $client = new Client('foobar', [ - 'consumer' => 'fork_curl', - ]); - $this->assertInstanceOf(ForkCurl::class, $client->getConsumer()); + $client = new Client('foobar', ['consumer' => 'fork_curl']); + self::assertInstanceOf(ForkCurl::class, $client->getConsumer()); } /** @test */ - public function can_provide_a_class_namespace_as_consumer_configuration() + public function can_provide_a_class_namespace_as_consumer_configuration(): void { $client = new Client('foobar', [ 'consumer' => ForkCurl::class, ]); - $this->assertInstanceOf(ForkCurl::class, $client->getConsumer()); + self::assertInstanceOf(ForkCurl::class, $client->getConsumer()); } } diff --git a/test/ConsumerFileTest.php b/test/ConsumerFileTest.php index 990fd85..ceee663 100644 --- a/test/ConsumerFileTest.php +++ b/test/ConsumerFileTest.php @@ -1,5 +1,7 @@ filename())) { unlink($this->filename()); } $this->client = new Client( - "oq0vdlg7yi", - array( - "consumer" => "file", - "filename" => $this->filename, - ) + 'oq0vdlg7yi', + [ + 'consumer' => 'file', + 'filename' => $this->filename, + ] ); } + public function filename(): string + { + return '/tmp/analytics.log'; + } + public function tearDown(): void { if (file_exists($this->filename)) { @@ -33,139 +40,132 @@ public function tearDown(): void } } - public function testTrack() + public function testTrack(): void { - $this->assertTrue($this->client->track(array( - "userId" => "some-user", - "event" => "File PHP Event - Microtime", - "timestamp" => microtime(true), - ))); - $this->checkWritten("track"); + self::assertTrue($this->client->track([ + 'userId' => 'some-user', + 'event' => 'File PHP Event - Microtime', + 'timestamp' => microtime(true), + ])); + $this->checkWritten('track'); + } + + public function checkWritten($type): void + { + exec('wc -l ' . $this->filename, $output); + $out = trim($output[0]); + self::assertSame($out, '1 ' . $this->filename); + $str = file_get_contents($this->filename); + $json = json_decode(trim($str), false); + self::assertSame($type, $json->type); + unlink($this->filename); } - public function testIdentify() + public function testIdentify(): void { - $this->assertTrue($this->client->identify(array( - "userId" => "Calvin", - "traits" => array( - "loves_php" => false, - "type" => "analytics.log", - "birthday" => time(), - ), - ))); - $this->checkWritten("identify"); + self::assertTrue($this->client->identify([ + 'userId' => 'Calvin', + 'traits' => [ + 'loves_php' => false, + 'type' => 'analytics.log', + 'birthday' => time(), + ], + ])); + $this->checkWritten('identify'); } - public function testGroup() + public function testGroup(): void { - $this->assertTrue($this->client->group(array( - "userId" => "user-id", - "groupId" => "group-id", - "traits" => array( - "type" => "consumer analytics.log test", - ), - ))); + self::assertTrue($this->client->group([ + 'userId' => 'user-id', + 'groupId' => 'group-id', + 'traits' => [ + 'type' => 'consumer analytics.log test', + ], + ])); } - public function testPage() + public function testPage(): void { - $this->assertTrue($this->client->page(array( - "userId" => "user-id", - "name" => "analytics-php", - "category" => "analytics.log", - "properties" => array( - "url" => "https://a.url/", - ), - ))); + self::assertTrue($this->client->page([ + 'userId' => 'user-id', + 'name' => 'analytics-php', + 'category' => 'analytics.log', + 'properties' => ['url' => 'https://a.url/'], + ])); } - public function testScreen() + public function testScreen(): void { - $this->assertTrue($this->client->screen(array( - "userId" => "userId", - "name" => "grand theft auto", - "category" => "analytics.log", - "properties" => array(), - ))); + self::assertTrue($this->client->screen([ + 'userId' => 'userId', + 'name' => 'grand theft auto', + 'category' => 'analytics.log', + 'properties' => [], + ])); } - public function testAlias() + public function testAlias(): void { - $this->assertTrue($this->client->alias(array( - "previousId" => "previous-id", - "userId" => "user-id", - ))); - $this->checkWritten("alias"); + self::assertTrue($this->client->alias([ + 'previousId' => 'previous-id', + 'userId' => 'user-id', + ])); + $this->checkWritten('alias'); } - public function testSend() + public function testSend(): void { for ($i = 0; $i < 200; ++$i) { - $this->client->track(array( - "userId" => "userId", - "event" => "event", - )); + $this->client->track([ + 'userId' => 'userId', + 'event' => 'event', + ]); } - exec("php --define date.timezone=UTC send.php --secret oq0vdlg7yi --file /tmp/analytics.log", $output); - $this->assertSame("sent 200 from 200 requests successfully", trim($output[0])); - $this->assertFileDoesNotExist($this->filename()); + exec('php --define date.timezone=UTC send.php --secret oq0vdlg7yi --file /tmp/analytics.log', $output); + self::assertSame('sent 200 from 200 requests successfully', trim($output[0])); + self::assertFileDoesNotExist($this->filename()); } - public function testProductionProblems() + public function testProductionProblems(): void { - // Open to a place where we should not have write access. + // Open to a place where we should not have write access. $client = new Client( - "oq0vdlg7yi", - array( - "consumer" => "file", - "filename" => "/dev/xxxxxxx", - ) + 'oq0vdlg7yi', + [ + 'consumer' => 'file', + 'filename' => '/dev/xxxxxxx', + ] ); - $tracked = $client->track(array("userId" => "some-user", "event" => "my event")); - $this->assertFalse($tracked); + $tracked = $client->track(['userId' => 'some-user', 'event' => 'my event']); + self::assertFalse($tracked); } - public function testFileSecurityCustom() + public function testFileSecurityCustom(): void { $client = new Client( - "testsecret", - array( - "consumer" => "file", - "filename" => $this->filename, - "filepermissions" => 0700 - ) + 'testsecret', + [ + 'consumer' => 'file', + 'filename' => $this->filename, + 'filepermissions' => 0700, + ] ); - $tracked = $client->track(array("userId" => "some_user", "event" => "File PHP Event")); - $this->assertEquals(0700, (fileperms($this->filename) & 0777)); + $client->track(['userId' => 'some_user', 'event' => 'File PHP Event']); + self::assertEquals(0700, (fileperms($this->filename) & 0777)); } - public function testFileSecurityDefaults() + public function testFileSecurityDefaults(): void { $client = new Client( - "testsecret", - array( - "consumer" => "file", - "filename" => $this->filename - ) + 'testsecret', + [ + 'consumer' => 'file', + 'filename' => $this->filename, + ] ); - $tracked = $client->track(array("userId" => "some_user", "event" => "File PHP Event")); - $this->assertEquals(0777, (fileperms($this->filename) & 0777)); - } - - public function checkWritten($type) - { - exec("wc -l " . $this->filename, $output); - $out = trim($output[0]); - $this->assertSame($out, "1 " . $this->filename); - $str = file_get_contents($this->filename); - $json = json_decode(trim($str)); - $this->assertSame($type, $json->type); - unlink($this->filename); - } - - public function filename() - { - return '/tmp/analytics.log'; + $client->track(['userId' => 'some_user', 'event' => 'File PHP Event']); + self::assertEquals(0777, (fileperms($this->filename) & 0777)); } } diff --git a/test/ConsumerForkCurlTest.php b/test/ConsumerForkCurlTest.php index 6ab3929..9d5f203 100644 --- a/test/ConsumerForkCurlTest.php +++ b/test/ConsumerForkCurlTest.php @@ -1,5 +1,7 @@ client = new Client( - "OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", - array( - "consumer" => "fork_curl", - "debug" => true, - ) + 'OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn', + [ + 'consumer' => 'fork_curl', + 'debug' => true, + ] ); } - public function testTrack() + public function testTrack(): void { - $this->assertTrue($this->client->track(array( - "userId" => "some-user", - "event" => "PHP Fork Curl'd\" Event", - ))); + self::assertTrue($this->client->track([ + 'userId' => 'some-user', + 'event' => "PHP Fork Curl'd\" Event", + ])); } - public function testIdentify() + public function testIdentify(): void { - $this->assertTrue($this->client->identify(array( - "userId" => "user-id", - "traits" => array( - "loves_php" => false, - "type" => "consumer fork-curl test", - "birthday" => time(), - ), - ))); + self::assertTrue($this->client->identify([ + 'userId' => 'user-id', + 'traits' => [ + 'loves_php' => false, + 'type' => 'consumer fork-curl test', + 'birthday' => time(), + ], + ])); } - public function testGroup() + public function testGroup(): void { - $this->assertTrue($this->client->group(array( - "userId" => "user-id", - "groupId" => "group-id", - "traits" => array( - "type" => "consumer fork-curl test", - ), - ))); + self::assertTrue($this->client->group([ + 'userId' => 'user-id', + 'groupId' => 'group-id', + 'traits' => [ + 'type' => 'consumer fork-curl test', + ], + ])); } - public function testPage() + public function testPage(): void { - $this->assertTrue($this->client->page(array( - "userId" => "userId", - "name" => "analytics-php", - "category" => "fork-curl", - "properties" => array( - "url" => "https://a.url/", - ), - ))); + self::assertTrue($this->client->page([ + 'userId' => 'userId', + 'name' => 'analytics-php', + 'category' => 'fork-curl', + 'properties' => ['url' => 'https://a.url/'], + ])); } - public function testScreen() + public function testScreen(): void { - $this->assertTrue($this->client->page(array( - "anonymousId" => "anonymous-id", - "name" => "grand theft auto", - "category" => "fork-curl", - "properties" => array(), - ))); + self::assertTrue($this->client->page([ + 'anonymousId' => 'anonymous-id', + 'name' => 'grand theft auto', + 'category' => 'fork-curl', + 'properties' => [], + ])); } - public function testAlias() + public function testAlias(): void { - $this->assertTrue($this->client->alias(array( - "previousId" => "previous-id", - "userId" => "user-id", - ))); + self::assertTrue($this->client->alias([ + 'previousId' => 'previous-id', + 'userId' => 'user-id', + ])); } - public function testRequestCompression() + public function testRequestCompression(): void { - $options = array( - "compress_request" => true, - "consumer" => "fork_curl", - "debug" => true, - ); + $options = [ + 'compress_request' => true, + 'consumer' => 'fork_curl', + 'debug' => true, + ]; - // Create client and send Track message - $client = new Client("OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn", $options); - $result = $client->track(array( - "userId" => "some-user", - "event" => "PHP Fork Curl'd\" Event with compression", - )); + // Create client and send Track message + $client = new Client('OnMMoZ6YVozrgSBeZ9FpkC0ixH0ycYZn', $options); + $result = $client->track([ + 'userId' => 'some-user', + 'event' => "PHP Fork Curl'd\" Event with compression", + ]); $client->__destruct(); - $this->assertTrue($result); + self::assertTrue($result); } } diff --git a/test/ConsumerLibCurlTest.php b/test/ConsumerLibCurlTest.php index c5480eb..ed07eb4 100644 --- a/test/ConsumerLibCurlTest.php +++ b/test/ConsumerLibCurlTest.php @@ -1,126 +1,123 @@ client = new Client( - "oq0vdlg7yi", - array( - "consumer" => "lib_curl", - "debug" => true, - ) + 'oq0vdlg7yi', + [ + 'consumer' => 'lib_curl', + 'debug' => true, + ] ); } - public function testTrack() + public function testTrack(): void { - $this->assertTrue($this->client->track(array( - "userId" => "lib-curl-track", - "event" => "PHP Lib Curl'd\" Event", - ))); + self::assertTrue($this->client->track([ + 'userId' => 'lib-curl-track', + 'event' => "PHP Lib Curl'd\" Event", + ])); } - public function testIdentify() + public function testIdentify(): void { - $this->assertTrue($this->client->identify(array( - "userId" => "lib-curl-identify", - "traits" => array( - "loves_php" => false, - "type" => "consumer lib-curl test", - "birthday" => time(), - ), - ))); + self::assertTrue($this->client->identify([ + 'userId' => 'lib-curl-identify', + 'traits' => [ + 'loves_php' => false, + 'type' => 'consumer lib-curl test', + 'birthday' => time(), + ], + ])); } - public function testGroup() + public function testGroup(): void { - $this->assertTrue($this->client->group(array( - "userId" => "lib-curl-group", - "groupId" => "group-id", - "traits" => array( - "type" => "consumer lib-curl test", - ), - ))); + self::assertTrue($this->client->group([ + 'userId' => 'lib-curl-group', + 'groupId' => 'group-id', + 'traits' => [ + 'type' => 'consumer lib-curl test', + ], + ])); } - public function testPage() + public function testPage(): void { - $this->assertTrue($this->client->page(array( - "userId" => "lib-curl-page", - "name" => "analytics-php", - "category" => "fork-curl", - "properties" => array( - "url" => "https://a.url/", - ), - ))); + self::assertTrue($this->client->page([ + 'userId' => 'lib-curl-page', + 'name' => 'analytics-php', + 'category' => 'fork-curl', + 'properties' => ['url' => 'https://a.url/'], + ])); } - public function testScreen() + public function testScreen(): void { - $this->assertTrue($this->client->page(array( - "anonymousId" => "lib-curl-screen", - "name" => "grand theft auto", - "category" => "fork-curl", - "properties" => array(), - ))); + self::assertTrue($this->client->page([ + 'anonymousId' => 'lib-curl-screen', + 'name' => 'grand theft auto', + 'category' => 'fork-curl', + 'properties' => [], + ])); } - public function testAlias() + public function testAlias(): void { - $this->assertTrue($this->client->alias(array( - "previousId" => "lib-curl-alias", - "userId" => "user-id", - ))); + self::assertTrue($this->client->alias([ + 'previousId' => 'lib-curl-alias', + 'userId' => 'user-id', + ])); } - public function testRequestCompression() + public function testRequestCompression(): void { - $options = array( - "compress_request" => true, - "consumer" => "lib_curl", - "error_handler" => function ($errno, $errmsg) { - throw new \RuntimeException($errmsg, $errno); - }, - ); - - $client = new Client("x", $options); - - # Should error out with debug on. - $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); + $options = [ + 'compress_request' => true, + 'consumer' => 'lib_curl', + 'error_handler' => function ($errno, $errmsg) { + throw new RuntimeException($errmsg, $errno); + }, + ]; + + $client = new Client('x', $options); + + # Should error out with debug on. + self::assertTrue($client->track(['user_id' => 'some-user', 'event' => 'Socket PHP Event'])); $client->__destruct(); } - public function testLargeMessageSizeError() + public function testLargeMessageSizeError(): void { - $options = array( - "debug" => true, - "consumer" => "lib_curl", - ); - - $client = new Client("testlargesize", $options); + $options = [ + 'debug' => true, + 'consumer' => 'lib_curl', + ]; - $big_property = ""; + $client = new Client('testlargesize', $options); - for ($i = 0; $i < 32 * 1024; ++$i) { - $big_property .= "a"; - } + $big_property = str_repeat('a', 32 * 1024); - $this->assertFalse( + self::assertFalse( $client->track( - array( - "userId" => "some-user", - "event" => "Super Large PHP Event", - "properties" => array("big_property" => $big_property), - ) + [ + 'userId' => 'some-user', + 'event' => 'Super Large PHP Event', + 'properties' => ['big_property' => $big_property], + ] ) && $client->flush() ); diff --git a/test/ConsumerSocketTest.php b/test/ConsumerSocketTest.php index 1778e56..2c0e860 100644 --- a/test/ConsumerSocketTest.php +++ b/test/ConsumerSocketTest.php @@ -1,226 +1,256 @@ client = new Client( - "oq0vdlg7yi", - array("consumer" => "socket") + 'oq0vdlg7yi', + ['consumer' => 'socket'] ); } - public function testTrack() + public function testTrack(): void { - $this->assertTrue($this->client->track(array( - "userId" => "some-user", - "event" => "Socket PHP Event", - ))); + self::assertTrue( + $this->client->track( + [ + 'userId' => 'some-user', + 'event' => 'Socket PHP Event', + ] + ) + ); } - public function testIdentify() + public function testIdentify(): void { - $this->assertTrue($this->client->identify(array( - "userId" => "Calvin", - "traits" => array( - "loves_php" => false, - "birthday" => time(), - ), - ))); + self::assertTrue( + $this->client->identify( + [ + 'userId' => 'Calvin', + 'traits' => [ + 'loves_php' => false, + 'birthday' => time(), + ], + ] + ) + ); } - public function testGroup() + public function testGroup(): void { - $this->assertTrue($this->client->group(array( - "userId" => "user-id", - "groupId" => "group-id", - "traits" => array( - "type" => "consumer socket test", - ), - ))); + self::assertTrue( + $this->client->group( + [ + 'userId' => 'user-id', + 'groupId' => 'group-id', + 'traits' => [ + 'type' => 'consumer socket test', + ], + ] + ) + ); } - public function testPage() + public function testPage(): void { - $this->assertTrue($this->client->page(array( - "userId" => "user-id", - "name" => "analytics-php", - "category" => "socket", - "properties" => array( - "url" => "https://a.url/", - ), - ))); + self::assertTrue( + $this->client->page( + [ + 'userId' => 'user-id', + 'name' => 'analytics-php', + 'category' => 'socket', + 'properties' => ['url' => 'https://a.url/'], + ] + ) + ); } - public function testScreen() + public function testScreen(): void { - $this->assertTrue($this->client->screen(array( - "anonymousId" => "anonymousId", - "name" => "grand theft auto", - "category" => "socket", - "properties" => array(), - ))); + self::assertTrue( + $this->client->screen( + [ + 'anonymousId' => 'anonymousId', + 'name' => 'grand theft auto', + 'category' => 'socket', + 'properties' => [], + ] + ) + ); } - public function testAlias() + public function testAlias(): void { - $this->assertTrue($this->client->alias(array( - "previousId" => "some-socket", - "userId" => "new-socket", - ))); + self::assertTrue( + $this->client->alias( + [ + 'previousId' => 'some-socket', + 'userId' => 'new-socket', + ] + ) + ); } - public function testShortTimeout() + public function testShortTimeout(): void { $client = new Client( - "oq0vdlg7yi", - array( - "timeout" => 0.01, - "consumer" => "socket", - ) + 'oq0vdlg7yi', + [ + 'timeout' => 0.01, + 'consumer' => 'socket', + ] ); - $this->assertTrue($client->track(array( - "userId" => "some-user", - "event" => "Socket PHP Event", - ))); + self::assertTrue( + $client->track( + [ + 'userId' => 'some-user', + 'event' => 'Socket PHP Event', + ] + ) + ); - $this->assertTrue($client->identify(array( - "userId" => "some-user", - "traits" => array(), - ))); + self::assertTrue( + $client->identify( + [ + 'userId' => 'some-user', + 'traits' => [], + ] + ) + ); $client->__destruct(); } - public function testProductionProblems() + public function testProductionProblems(): void { $client = new Client( - "x", - array( - "consumer" => "socket", - "error_handler" => function () { - throw new \Exception("Was called"); - }, - ) + 'x', + [ + 'consumer' => 'socket', + 'error_handler' => function () { + throw new Exception('Was called'); + }, + ] ); - // Shouldn't error out without debug on. - $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Production Problems"))); + // Shouldn't error out without debug on. + self::assertTrue($client->track(['user_id' => 'some-user', 'event' => 'Production Problems'])); $client->__destruct(); } - public function testDebugProblems() + public function testDebugProblems(): void { - $options = array( - "debug" => true, - "consumer" => "socket", - "error_handler" => function ($errno, $errmsg) { - if (400 != $errno) { - throw new \Exception("Response is not 400"); - } - }, - ); + $options = [ + 'debug' => true, + 'consumer' => 'socket', + 'error_handler' => function ($errno, $errmsg) { + if ($errno !== 400) { + throw new Exception('Response is not 400'); + } + }, + ]; - $client = new Client("x", $options); + $client = new Client('x', $options); - // Should error out with debug on. - $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); + // Should error out with debug on. + self::assertTrue($client->track(['user_id' => 'some-user', 'event' => 'Socket PHP Event'])); $client->__destruct(); } - public function testLargeMessage() + public function testLargeMessage(): void { - $options = array( - "debug" => true, - "consumer" => "socket", - ); - - $client = new Client("testsecret", $options); + $options = [ + 'debug' => true, + 'consumer' => 'socket', + ]; - $big_property = ""; + $client = new Client('testsecret', $options); - for ($i = 0; $i < 10000; ++$i) { - $big_property .= "a"; - } + $big_property = str_repeat('a', 10000); - $this->assertTrue($client->track(array( - "userId" => "some-user", - "event" => "Super Large PHP Event", - "properties" => array("big_property" => $big_property), - ))); + self::assertTrue( + $client->track( + [ + 'userId' => 'some-user', + 'event' => 'Super Large PHP Event', + 'properties' => ['big_property' => $big_property], + ] + ) + ); $client->__destruct(); } - public function testLargeMessageSizeError() + public function testLargeMessageSizeError(): void { - $options = array( - "debug" => true, - "consumer" => "socket", - ); + $options = [ + 'debug' => true, + 'consumer' => 'socket', + ]; - $client = new Client("testlargesize", $options); + $client = new Client('testlargesize', $options); - $big_property = ""; + $big_property = str_repeat('a', 32 * 1024); - for ($i = 0; $i < 32 * 1024; ++$i) { - $big_property .= "a"; - } - - $this->assertFalse( + self::assertFalse( $client->track( - array( - "userId" => "some-user", - "event" => "Super Large PHP Event", - "properties" => array("big_property" => $big_property), - ) + [ + 'userId' => 'some-user', + 'event' => 'Super Large PHP Event', + 'properties' => ['big_property' => $big_property], + ] ) && $client->flush() ); $client->__destruct(); } - /** - * @expectedException \RuntimeException - */ - public function testConnectionError() + public function testConnectionError(): void { - $this->expectException(\RuntimeException::class); - $client = new Client("x", array( - "consumer" => "socket", - "host" => "api.segment.ioooooo", - "error_handler" => function ($errno, $errmsg) { - throw new \RuntimeException($errmsg, $errno); - }, - )); - - $client->track(array("user_id" => "some-user", "event" => "Event")); + $this->expectException(RuntimeException::class); + $client = new Client( + 'x', + [ + 'consumer' => 'socket', + 'host' => 'api.segment.ioooooo', + 'error_handler' => function ($errno, $errmsg) { + throw new RuntimeException($errmsg, $errno); + }, + ] + ); + + $client->track(['user_id' => 'some-user', 'event' => 'Event']); $client->__destruct(); } - public function testRequestCompression() + public function testRequestCompression(): void { - $options = array( - "compress_request" => true, - "consumer" => "socket", - "error_handler" => function ($errno, $errmsg) { - throw new \RuntimeException($errmsg, $errno); - }, - ); + $options = [ + 'compress_request' => true, + 'consumer' => 'socket', + 'error_handler' => function ($errno, $errmsg) { + throw new RuntimeException($errmsg, $errno); + }, + ]; - $client = new Client("x", $options); + $client = new Client('x', $options); - # Should error out with debug on. - $this->assertTrue($client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"))); + # Should error out with debug on. + self::assertTrue($client->track(['user_id' => 'some-user', 'event' => 'Socket PHP Event'])); $client->__destruct(); } } From f4659e412c1db1eeab55781123c6dd824cfc08ea Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 11:33:19 +0200 Subject: [PATCH 084/151] Use our own exception --- lib/Client.php | 3 +-- lib/Consumer/Socket.php | 2 +- lib/Segment.php | 8 ++++---- lib/SegmentException.php | 8 ++++++++ 4 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 lib/SegmentException.php diff --git a/lib/Client.php b/lib/Client.php index b757d91..7d326ed 100644 --- a/lib/Client.php +++ b/lib/Client.php @@ -4,7 +4,6 @@ namespace Segment; -use Exception; use Segment\Consumer\Consumer; use Segment\Consumer\File; use Segment\Consumer\ForkCurl; @@ -38,7 +37,7 @@ public function __construct(string $secret, array $options = []) if (!array_key_exists($consumer_type, $consumers) && class_exists($consumer_type)) { if (!is_subclass_of($consumer_type, Consumer::class)) { - throw new Exception('Consumers must extend the Segment/Consumer/Consumer abstract class'); + throw new SegmentException('Consumers must extend the Segment/Consumer/Consumer abstract class'); } // Try to resolve it by class name $this->consumer = new $consumer_type($secret, $options); diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index 64651dd..cef3e56 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -158,7 +158,7 @@ private function makeRequest($socket, string $req, bool $retry = true): bool try { // Since we're try catch'ing prevent PHP logs. $written = @fwrite($socket, substr($req, $bytes_written)); - } catch (\Exception $e) { + } catch (Exception $e) { $this->handleError($e->getCode(), $e->getMessage()); $closed = true; } diff --git a/lib/Segment.php b/lib/Segment.php index b638a58..fc356da 100644 --- a/lib/Segment.php +++ b/lib/Segment.php @@ -27,12 +27,12 @@ public static function init(string $secret, array $options = []): void * * @param mixed $value * @param string $msg - * @throws Exception + * @throws SegmentException */ private static function assert($value, string $msg): void { if (empty($value)) { - throw new Exception($msg); + throw new SegmentException($msg); } } @@ -55,7 +55,7 @@ public static function track(array $message): bool /** * Check the client. * - * @throws Exception + * @throws SegmentException */ private static function checkClient(): void { @@ -63,7 +63,7 @@ private static function checkClient(): void return; } - throw new Exception('Segment::init() must be called before any other tracking method.'); + throw new SegmentException('Segment::init() must be called before any other tracking method.'); } /** diff --git a/lib/SegmentException.php b/lib/SegmentException.php new file mode 100644 index 0000000..c566604 --- /dev/null +++ b/lib/SegmentException.php @@ -0,0 +1,8 @@ + Date: Fri, 24 Sep 2021 11:33:52 +0200 Subject: [PATCH 085/151] Use our own exception --- test/AnalyticsTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/AnalyticsTest.php b/test/AnalyticsTest.php index f04e76b..039d917 100644 --- a/test/AnalyticsTest.php +++ b/test/AnalyticsTest.php @@ -4,9 +4,9 @@ namespace Segment\Test; -use Exception; use PHPUnit\Framework\TestCase; use Segment\Segment; +use Segment\SegmentException; class AnalyticsTest extends TestCase { @@ -61,7 +61,7 @@ public function testGroupAnonymous(): void public function testGroupNoUser(): void { $this->expectExceptionMessage('Segment::group() requires userId or anonymousId'); - $this->expectException(Exception::class); + $this->expectException(SegmentException::class); Segment::group( [ 'groupId' => 'group-id', From 6bc3d4014cf425e48d7a783296e63215680bfc16 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 11:34:30 +0200 Subject: [PATCH 086/151] Nothing this block throws exceptions, so trap errors in another way --- lib/Consumer/File.php | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/Consumer/File.php b/lib/Consumer/File.php index a390a39..7e31780 100644 --- a/lib/Consumer/File.php +++ b/lib/Consumer/File.php @@ -4,8 +4,6 @@ namespace Segment\Consumer; -use Exception; - class File extends Consumer { protected string $type = 'File'; @@ -29,15 +27,15 @@ public function __construct(string $secret, array $options = []) parent::__construct($secret, $options); - try { - $this->file_handle = fopen($options['filename'], 'ab'); - if (isset($options['filepermissions'])) { - chmod($options['filename'], $options['filepermissions']); - } else { - chmod($options['filename'], 0777); - } - } catch (Exception $e) { - $this->handleError($e->getCode(), $e->getMessage()); + $this->file_handle = fopen($options['filename'], 'ab'); + if ($this->file_handle === false) { + $this->handleError(13, 'Failed to open analytics.log file'); + return; + } + if (isset($options['filepermissions'])) { + chmod($options['filename'], $options['filepermissions']); + } else { + chmod($options['filename'], 0664); } } From 1fe7a2c12a037ba6e3807fc8340e887189147a27 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 12:04:16 +0200 Subject: [PATCH 087/151] Nothing in this block throws exceptions --- lib/Consumer/Socket.php | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index cef3e56..8478b94 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -63,32 +63,23 @@ private function createSocket() $port = $this->ssl() ? 443 : 80; $timeout = $this->options['timeout']; - try { - // Open our socket to the API Server. - // Since we're try catch'ing prevent PHP logs. - $socket = @pfsockopen( - $protocol . '://' . $host, - $port, - $errno, - $errstr, - $timeout - ); - - // If we couldn't open the socket, handle the error. - if ($socket === false) { - $this->handleError($errno, $errstr); - $this->socket_failed = true; - - return false; - } - - return $socket; - } catch (\Exception $e) { - $this->handleError($e->getCode(), $e->getMessage()); + // Open our socket to the API Server. + // Since we're try catch'ing prevent PHP logs. + $socket = @pfsockopen( + $protocol . '://' . $host, + $port, + $errno, + $errstr, + $timeout + ); + + // If we couldn't open the socket, handle the error. + if ($socket === false) { + $this->handleError($errno, $errstr); $this->socket_failed = true; - - return false; } + + return $socket; } /** From 1d729d36b7a2b06d13ccb87998d362ae6a68c982 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 12:04:58 +0200 Subject: [PATCH 088/151] CS --- lib/Consumer/Socket.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index 8478b94..3c23cdd 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -10,7 +10,8 @@ class Socket extends QueueConsumer private bool $socket_failed = false; /** - * Creates a new socket consumer for dispatching async requests immediately + * Creates a new socket consumer for dispatching async requests immediately. + * * @param string $secret * @param array $options * number "timeout" - the timeout for connecting @@ -50,6 +51,8 @@ public function flushBatch($batch): bool } /** + * Open a connection to the target host. + * * @return false|resource */ private function createSocket() @@ -83,7 +86,8 @@ private function createSocket() } /** - * Create the body to send as the post request. + * Create the request body. + * * @param string $host * @param string $content * @return string body @@ -117,6 +121,7 @@ private function createBody(string $host, string $content) // Verify message size is below than 32KB if (strlen($req) >= 32 * 1024) { $msg = 'Message size is larger than 32KB'; + /** @noinspection ForgottenDebugOutputInspection */ error_log('[Analytics][' . $this->type . '] ' . $msg); return false; @@ -128,7 +133,8 @@ private function createBody(string $host, string $content) /** * Attempt to write the request to the socket, wait for response if debug * mode is enabled. - * @param resource $socket the handle for the socket + * + * @param resource|false $socket the handle for the socket * @param string $req request body * @param bool $retry * @return bool From abba668f1b41ce44d2cff4dfafea4de78045f33d Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 12:05:15 +0200 Subject: [PATCH 089/151] Remove unused param --- lib/Consumer/Socket.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index 3c23cdd..54244b0 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -136,10 +136,9 @@ private function createBody(string $host, string $content) * * @param resource|false $socket the handle for the socket * @param string $req request body - * @param bool $retry * @return bool */ - private function makeRequest($socket, string $req, bool $retry = true): bool + private function makeRequest($socket, string $req): bool { $bytes_written = 0; $bytes_total = strlen($req); From 16a4f7710586cadb8e14c55dc8e53925d45ba37a Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 12:05:41 +0200 Subject: [PATCH 090/151] This can be static --- lib/Consumer/Socket.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index 54244b0..536f018 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -169,7 +169,7 @@ private function makeRequest($socket, string $req): bool $statusCode = 0; if (!$closed) { - $res = $this->parseResponse(fread($socket, 2048)); + $res = self::parseResponse(fread($socket, 2048)); $statusCode = (int)$res['status']; } fclose($socket); @@ -211,7 +211,7 @@ private function makeRequest($socket, string $req): bool * string $status HTTP code, e.g. "200" * string $message JSON response from the api */ - private function parseResponse(string $res): array + private static function parseResponse(string $res): array { $contents = explode("\n", $res); From ed659be6eb765377b7aed819ce4b11fd9e5987e3 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 12:14:39 +0200 Subject: [PATCH 091/151] Simplify syntax --- lib/Consumer/Socket.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index 536f018..5f77c12 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -213,16 +213,15 @@ private function makeRequest($socket, string $req): bool */ private static function parseResponse(string $res): array { - $contents = explode("\n", $res); + [$first,] = explode("\n", $res, 2); // Response comes back as HTTP/1.1 200 OK // Final line contains HTTP response. - $status = explode(' ', $contents[0], 3); - $result = $contents[count($contents) - 1]; + [, $status, $message] = explode(' ', $first, 3); return [ - 'status' => $status[1] ?? null, - 'message' => $result, + 'status' => $status ?? null, + 'message' => $message, ]; } } From 12f2cb47bd4f0b5d12cb3f3072172aa8ce1d7fba Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 12:20:32 +0200 Subject: [PATCH 092/151] fwrite and substr don't throw exceptions --- lib/Consumer/Socket.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index 5f77c12..aa7c880 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -151,14 +151,9 @@ private function makeRequest($socket, string $req): bool while (true) { // Send request to server while (!$closed && $bytes_written < $bytes_total) { - try { - // Since we're try catch'ing prevent PHP logs. - $written = @fwrite($socket, substr($req, $bytes_written)); - } catch (Exception $e) { - $this->handleError($e->getCode(), $e->getMessage()); - $closed = true; - } - if (!isset($written) || !$written) { + $written = @fwrite($socket, substr($req, $bytes_written)); + if ($written === false) { + $this->handleError(13, 'Failed to write to socket.'); $closed = true; } else { $bytes_written += $written; From e1e4389c14044d75db9209a3b2d4ed74a838b358 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 12:20:43 +0200 Subject: [PATCH 093/151] Comment --- lib/Consumer/Socket.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index aa7c880..cb71357 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -67,7 +67,6 @@ private function createSocket() $timeout = $this->options['timeout']; // Open our socket to the API Server. - // Since we're try catch'ing prevent PHP logs. $socket = @pfsockopen( $protocol . '://' . $host, $port, From 785bac7deb6890ab6e19866ccfa15a2f6721c7c3 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 12:22:38 +0200 Subject: [PATCH 094/151] Remove unused alias --- lib/Segment.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/Segment.php b/lib/Segment.php index fc356da..98e09e2 100644 --- a/lib/Segment.php +++ b/lib/Segment.php @@ -4,8 +4,6 @@ namespace Segment; -use Exception; - class Segment { private static Client $client; From f8a95f8bf3facb782146a8d597787d69e5821734 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 12:22:48 +0200 Subject: [PATCH 095/151] Strict types --- lib/SegmentException.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/SegmentException.php b/lib/SegmentException.php index c566604..0c0a135 100644 --- a/lib/SegmentException.php +++ b/lib/SegmentException.php @@ -1,8 +1,10 @@ Date: Fri, 24 Sep 2021 13:53:59 +0200 Subject: [PATCH 096/151] Use safer default permissions, no need for a log file to be executable --- lib/Consumer/File.php | 2 +- test/ConsumerFileTest.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Consumer/File.php b/lib/Consumer/File.php index 7e31780..42cdcbc 100644 --- a/lib/Consumer/File.php +++ b/lib/Consumer/File.php @@ -35,7 +35,7 @@ public function __construct(string $secret, array $options = []) if (isset($options['filepermissions'])) { chmod($options['filename'], $options['filepermissions']); } else { - chmod($options['filename'], 0664); + chmod($options['filename'], 0644); } } diff --git a/test/ConsumerFileTest.php b/test/ConsumerFileTest.php index ceee663..33087ff 100644 --- a/test/ConsumerFileTest.php +++ b/test/ConsumerFileTest.php @@ -149,11 +149,11 @@ public function testFileSecurityCustom(): void [ 'consumer' => 'file', 'filename' => $this->filename, - 'filepermissions' => 0700, + 'filepermissions' => 0600, ] ); $client->track(['userId' => 'some_user', 'event' => 'File PHP Event']); - self::assertEquals(0700, (fileperms($this->filename) & 0777)); + self::assertEquals(0600, (fileperms($this->filename) & 0777)); } public function testFileSecurityDefaults(): void @@ -166,6 +166,6 @@ public function testFileSecurityDefaults(): void ] ); $client->track(['userId' => 'some_user', 'event' => 'File PHP Event']); - self::assertEquals(0777, (fileperms($this->filename) & 0777)); + self::assertEquals(0644, (fileperms($this->filename) & 0777)); } } From 15bde42050a0b23569c0ed3f150d943e48038c8f Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 13:54:21 +0200 Subject: [PATCH 097/151] OK to suppress the error because we're checking teh result --- lib/Consumer/File.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Consumer/File.php b/lib/Consumer/File.php index 42cdcbc..278b53a 100644 --- a/lib/Consumer/File.php +++ b/lib/Consumer/File.php @@ -27,7 +27,7 @@ public function __construct(string $secret, array $options = []) parent::__construct($secret, $options); - $this->file_handle = fopen($options['filename'], 'ab'); + $this->file_handle = @fopen($options['filename'], 'ab'); if ($this->file_handle === false) { $this->handleError(13, 'Failed to open analytics.log file'); return; From fda59b04f875e73538325fb2cd4d56c7acaa0c42 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 24 Sep 2021 13:54:33 +0200 Subject: [PATCH 098/151] Fix test namespace mapping --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 72628bc..3a38478 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ }, "autoload": { "psr-4": { + "Segment\\Test\\": "test/", "Segment\\": "lib/" } }, From a0ee801dd595e325b06c9c1225835e8bb269c7cf Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Wed, 6 Oct 2021 22:55:56 +0200 Subject: [PATCH 099/151] Refactor log file access and cleanup --- test/ConsumerFileTest.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/test/ConsumerFileTest.php b/test/ConsumerFileTest.php index 33087ff..1c8cba0 100644 --- a/test/ConsumerFileTest.php +++ b/test/ConsumerFileTest.php @@ -15,9 +15,7 @@ class ConsumerFileTest extends TestCase public function setUp(): void { date_default_timezone_set('UTC'); - if (file_exists($this->filename())) { - unlink($this->filename()); - } + $this->clearLog(); $this->client = new Client( 'oq0vdlg7yi', @@ -28,16 +26,21 @@ public function setUp(): void ); } + private function clearLog(): void + { + if (file_exists($this->filename)) { + unlink($this->filename); + } + } + public function filename(): string { - return '/tmp/analytics.log'; + return $this->filename; } public function tearDown(): void { - if (file_exists($this->filename)) { - unlink($this->filename); - } + $this->clearLog(); } public function testTrack(): void @@ -124,7 +127,7 @@ public function testSend(): void } exec('php --define date.timezone=UTC send.php --secret oq0vdlg7yi --file /tmp/analytics.log', $output); self::assertSame('sent 200 from 200 requests successfully', trim($output[0])); - self::assertFileDoesNotExist($this->filename()); + self::assertFileDoesNotExist($this->filename); } public function testProductionProblems(): void From f06a36fe614419e5593fee46abe7d5b63384716d Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Wed, 6 Oct 2021 23:01:48 +0200 Subject: [PATCH 100/151] Add composer scripts, CS --- composer.json | 93 ++++++++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 41 deletions(-) diff --git a/composer.json b/composer.json index 3a38478..520c8a3 100644 --- a/composer.json +++ b/composer.json @@ -1,43 +1,54 @@ { - "name": "segmentio/analytics-php", - "version": "2.0.0", - "description": "Segment Analytics PHP Library", - "keywords": [ - "analytics", - "segmentio", - "segment", - "analytics.js" - ], - "homepage": "https://segment.com/libraries/php", - "license": "MIT", - "authors": [ - { - "name": "Segment.io ", - "homepage": "https://segment.com/" - } - ], - "require": { - "php": "^7.4 || ^8.0", - "ext-json": "*" - }, - "require-dev": { - "phpunit/phpunit": "~9.5", - "overtrue/phplint": "^3.0", - "squizlabs/php_codesniffer": "^3.6", - "roave/security-advisories": "dev-latest", - "slevomat/coding-standard": "^7.0" - }, - "suggest": { - "ext-curl": "For using the curl HTTP client", - "ext-zlib": "For using compression" - }, - "autoload": { - "psr-4": { - "Segment\\Test\\": "test/", - "Segment\\": "lib/" - } - }, - "bin": [ - "bin/analytics" - ] + "name": "segmentio/analytics-php", + "version": "2.0.0", + "description": "Segment Analytics PHP Library", + "keywords": [ + "analytics", + "segmentio", + "segment", + "analytics.js" + ], + "homepage": "https://segment.com/libraries/php", + "license": "MIT", + "authors": [ + { + "name": "Segment.io ", + "homepage": "https://segment.com/" + } + ], + "require": { + "php": "^7.4 || ^8.0", + "ext-json": "*" + }, + "require-dev": { + "phpunit/phpunit": "~9.5", + "overtrue/phplint": "^3.0", + "squizlabs/php_codesniffer": "^3.6", + "roave/security-advisories": "dev-latest", + "slevomat/coding-standard": "^7.0" + }, + "suggest": { + "ext-curl": "For using the curl HTTP client", + "ext-zlib": "For using compression" + }, + "autoload": { + "psr-4": { + "Segment\\Test\\": "test/", + "Segment\\": "lib/" + } + }, + "bin": [ + "bin/analytics" + ], + "scripts": { + "test": [ + "./vendor/bin/phpunit" + ], + "cs": [ + "./vendor/bin/phpcs" + ], + "cf": [ + "./vendor/bin/phpcbf" + ] + } } From 8afea59ec24c84eb3357a6306ebd9186d536e81c Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Thu, 14 Oct 2021 15:45:14 -0500 Subject: [PATCH 101/151] Update Version.php --- lib/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Version.php b/lib/Version.php index fc4eb08..e89ae87 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -3,4 +3,4 @@ declare(strict_types=1); global $SEGMENT_VERSION; -$SEGMENT_VERSION = '2.0.0'; +$SEGMENT_VERSION = '3.0.0'; From 37c790d78e5eb574e758035ce09ffe8fac1c8c5b Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Thu, 14 Oct 2021 15:45:31 -0500 Subject: [PATCH 102/151] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 520c8a3..c5e2627 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "segmentio/analytics-php", - "version": "2.0.0", + "version": "3.0.0", "description": "Segment Analytics PHP Library", "keywords": [ "analytics", From c3c2a3806376d6e8d328937203fdc84206229dfb Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Thu, 14 Oct 2021 15:58:00 -0500 Subject: [PATCH 103/151] Update History --- HISTORY.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 14fe3bb..b44bcf5 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,16 @@ +3.0.0 / 2021-10-14 +================== + + * PSR-12 coding standard with Slevomat and phcs extensions + * Namespace and file rearrangement to follow PSR-4 naming scheme and more logical separation + * Provide strict types for all properties, parameters, and return values + * Add an exception class so we can have segment-specific exceptions + * Add dependencies on JSON extension + * Add dependency on the Roave security checker + * Since the library already required a minimum of PHP 7.4, make use of PHP 7.4+ features, and avoid compat issues with 8.0 + * More sensible error handling, don't try to catch exceptions that are never thrown + * Extensive linting and static analysis using phpcs, psalm, phpstan, and PHPStorm to spot issues + 2.0.0 / 2021-07-16 ================== From 3d8e9543903f8f5105d143a585be8e864056aa21 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 14 Oct 2021 16:38:27 -0500 Subject: [PATCH 104/151] Add consumer available options for curl timeouts and socket tls connections --- lib/Consumer/LibCurl.php | 2 ++ lib/Consumer/Socket.php | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Consumer/LibCurl.php b/lib/Consumer/LibCurl.php index ffd6bee..ad40549 100644 --- a/lib/Consumer/LibCurl.php +++ b/lib/Consumer/LibCurl.php @@ -43,6 +43,8 @@ public function flushBatch(array $messages): bool // set the url, number of POST vars, POST data curl_setopt($ch, CURLOPT_USERPWD, $secret . ':'); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); + curl_setopt($ch, CURLOPT_TIMEOUT, $this->options['curl_timeout']); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->options['curl_connecttimeout']); // set variables for headers $header = []; diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index cb71357..50f0147 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -61,7 +61,7 @@ private function createSocket() return false; } - $protocol = $this->ssl() ? 'ssl' : 'tcp'; + $protocol = $this->options['tls'] ? 'tls' : ($this->ssl() ? 'ssl' : 'tcp'); $host = $this->options['host']; $port = $this->ssl() ? 443 : 80; $timeout = $this->options['timeout']; From 2e2c9c134ce38f92bedd756fdb9a2cc945088ca7 Mon Sep 17 00:00:00 2001 From: Marcus Bointon Date: Fri, 15 Oct 2021 09:03:19 +0200 Subject: [PATCH 105/151] Add GitHub actions for CI Add composer scripts Add parallel lint Add badges PHPUnit logging Update PHPUnit config --- .codecov.yml | 21 ++++++ .github/workflows/tests.yml | 147 ++++++++++++++++++++++++++++++++++++ .gitignore | 2 + README.md | 3 +- composer.json | 17 ++--- phpunit.xml | 10 ++- 6 files changed, 187 insertions(+), 13 deletions(-) create mode 100644 .codecov.yml create mode 100644 .github/workflows/tests.yml diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..193a7e7 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,21 @@ +codecov: + notify: + after_n_builds: 2 + +coverage: + round: nearest + # Status will be green when coverage is between 70 and 100%. + range: "70...100" + status: + project: + default: + threshold: 2% + paths: + - "src" + patch: + default: + threshold: 0% + paths: + - "src" + +comment: false diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..1bf9b9e --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,147 @@ +name: "Tests" + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + + coding-standard: + runs-on: ubuntu-20.04 + name: Coding standards + + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + coverage: none + tools: cs2pr + + # Install dependencies and handle caching in one go. + # @link https://github.com/marketplace/actions/install-composer-dependencies + - name: Install Composer dependencies + uses: "ramsey/composer-install@v1" + + - name: Check coding standards + continue-on-error: true + run: ./vendor/bin/phpcs -s --report-full --report-checkstyle=./phpcs-report.xml + + - name: Show PHPCS results in PR + run: cs2pr ./phpcs-report.xml + + lint: + runs-on: ubuntu-20.04 + strategy: + matrix: + php: ['7.4', '8.0'] + experimental: [false] + include: + - php: '8.1' + experimental: true + + name: "Lint: PHP ${{ matrix.php }}" + continue-on-error: ${{ matrix.experimental }} + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + tools: cs2pr + + # Install dependencies and handle caching in one go. + # @link https://github.com/marketplace/actions/install-composer-dependencies + - name: Install Composer dependencies + uses: "ramsey/composer-install@v1" + + - name: Lint against parse errors + if: ${{ matrix.php != '8.1' }} + run: composer lint -- --checkstyle | cs2pr + + - name: Lint against parse errors (PHP 8.1) + if: ${{ matrix.php == '8.1' }} + run: composer lint + + test: + needs: ['coding-standard', 'lint'] + runs-on: ubuntu-20.04 + strategy: + matrix: + php: ['7.4', '8.0'] + coverage: [false] + experimental: [false] + include: + # Run code coverage on high/low PHP. + - php: '7.4' + coverage: true + experimental: false + - php: '8.0' + coverage: true + experimental: false + + # Experimental builds. These are allowed to fail. + - php: '8.1' + coverage: false + experimental: true + + name: "Test: PHP ${{ matrix.php }}" + + continue-on-error: ${{ matrix.experimental }} + + steps: + - name: Check out code + uses: actions/checkout@v2 + + - name: Set coverage variable + id: set_cov + run: | + if [ ${{ matrix.coverage }} == "true" ]; then + echo '::set-output name=COV::xdebug' + else + echo '::set-output name=COV::none' + fi + + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: ${{ steps.set_cov.outputs.COV }} + ini-values: sendmail_path=/usr/sbin/sendmail -t -i, error_reporting=E_ALL, display_errors=On + extensions: imap, mbstring, intl, ctype, filter, hash + + # Install dependencies and handle caching in one go. + # @link https://github.com/marketplace/actions/install-composer-dependencies + - name: Install PHP packages - normal + if: ${{ matrix.php != '8.1' }} + uses: "ramsey/composer-install@v1" + + - name: Install PHP packages - ignore-platform-reqs + if: ${{ matrix.php == '8.1' }} + uses: "ramsey/composer-install@v1" + with: + composer-options: --ignore-platform-reqs + + - name: Run tests, no code coverage + if: ${{ matrix.coverage == false }} + run: ./vendor/bin/phpunit --no-coverage + + - name: Run tests with code coverage + if: ${{ matrix.coverage == true }} + run: vendor/bin/phpunit + + - name: Send coverage report to Codecov + if: ${{ success() && matrix.coverage == true }} + uses: codecov/codecov-action@v1 + with: + files: ./build/logs/clover.xml + fail_ci_if_error: true + verbose: true diff --git a/.gitignore b/.gitignore index a3a88b5..5c16b2f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ test/analytics.log .phplint-cache /.idea clover.xml +junit.xml .phpunit.result.cache +/build/* diff --git a/README.md b/README.md index f25cf00..e450d32 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ analytics-php ============== [![Segmentio](https://circleci.com/gh/segmentio/analytics-php.svg?style=shield&circle-token=a20a4b1daa865d164e62ca59a65d6753d286c1c1)](https://app.circleci.com/pipelines/github/segmentio/analytics-php) - +[![Test status](https://github.com/Synchro/analytics-php/workflows/Tests/badge.svg)](https://github.com/Synchro/analytics-php/actions) +[![codecov.io](https://codecov.io/gh/Synchro/analytics-php/branch/master/graph/badge.svg?token=iORZpwmYmM)](https://codecov.io/gh/Synchro/analytics-php) analytics-php is a php client for [Segment](https://segment.com)
diff --git a/composer.json b/composer.json index 520c8a3..be461da 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,8 @@ "overtrue/phplint": "^3.0", "squizlabs/php_codesniffer": "^3.6", "roave/security-advisories": "dev-latest", - "slevomat/coding-standard": "^7.0" + "slevomat/coding-standard": "^7.0", + "php-parallel-lint/php-parallel-lint": "^1.3" }, "suggest": { "ext-curl": "For using the curl HTTP client", @@ -41,14 +42,12 @@ "bin/analytics" ], "scripts": { - "test": [ - "./vendor/bin/phpunit" - ], - "cs": [ - "./vendor/bin/phpcs" - ], - "cf": [ - "./vendor/bin/phpcbf" + "test": "./vendor/bin/phpunit --no-coverage", + "check": "./vendor/bin/phpcs", + "cf": "./vendor/bin/phpcbf", + "coverage": "./vendor/bin/phpunit", + "lint": [ + "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php,phps --exclude vendor --exclude .git --exclude build" ] } } diff --git a/phpunit.xml b/phpunit.xml index 4885233..e2f6999 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,6 +1,6 @@ ./lib - + @@ -32,5 +32,9 @@ test - + + + + + \ No newline at end of file From 572e61e4c9ba8e68c5301ca3182886a6c757fdb3 Mon Sep 17 00:00:00 2001 From: Pooya Jaferian Date: Wed, 27 Oct 2021 16:26:19 -0700 Subject: [PATCH 106/151] Update badges --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e450d32..7a5661c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ analytics-php ============== -[![Segmentio](https://circleci.com/gh/segmentio/analytics-php.svg?style=shield&circle-token=a20a4b1daa865d164e62ca59a65d6753d286c1c1)](https://app.circleci.com/pipelines/github/segmentio/analytics-php) -[![Test status](https://github.com/Synchro/analytics-php/workflows/Tests/badge.svg)](https://github.com/Synchro/analytics-php/actions) -[![codecov.io](https://codecov.io/gh/Synchro/analytics-php/branch/master/graph/badge.svg?token=iORZpwmYmM)](https://codecov.io/gh/Synchro/analytics-php) +[![Test status](https://github.com/segmentio/analytics-php/workflows/Tests/badge.svg)](https://github.com/Synchro/analytics-php/actions) +[![codecov.io](https://codecov.io/gh/segmentio/analytics-php/branch/master/graph/badge.svg?token=iORZpwmYmM)](https://codecov.io/gh/Synchro/analytics-php) + analytics-php is a php client for [Segment](https://segment.com)
From a3a7652708d8ef18973121d69fd83ad17910c45b Mon Sep 17 00:00:00 2001 From: Pooya Jaferian Date: Wed, 27 Oct 2021 16:27:33 -0700 Subject: [PATCH 107/151] Fix badges links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7a5661c..31bee97 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ analytics-php ============== -[![Test status](https://github.com/segmentio/analytics-php/workflows/Tests/badge.svg)](https://github.com/Synchro/analytics-php/actions) -[![codecov.io](https://codecov.io/gh/segmentio/analytics-php/branch/master/graph/badge.svg?token=iORZpwmYmM)](https://codecov.io/gh/Synchro/analytics-php) +[![Test status](https://github.com/segmentio/analytics-php/workflows/Tests/badge.svg)](https://github.com/segmentio/analytics-php/actions) +[![codecov.io](https://codecov.io/gh/segmentio/analytics-php/branch/master/graph/badge.svg?token=iORZpwmYmM)](https://codecov.io/gh/segmentio/analytics-php) analytics-php is a php client for [Segment](https://segment.com) From 81e7f9d4ee75c8c6d02d3813335216356eda7098 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 10 Nov 2021 10:15:33 -0600 Subject: [PATCH 108/151] Update new option defaults --- lib/Consumer/LibCurl.php | 4 ++-- lib/Consumer/QueueConsumer.php | 10 ++++++++++ lib/Consumer/Socket.php | 4 ++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/Consumer/LibCurl.php b/lib/Consumer/LibCurl.php index ad40549..87407ec 100644 --- a/lib/Consumer/LibCurl.php +++ b/lib/Consumer/LibCurl.php @@ -43,8 +43,8 @@ public function flushBatch(array $messages): bool // set the url, number of POST vars, POST data curl_setopt($ch, CURLOPT_USERPWD, $secret . ':'); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); - curl_setopt($ch, CURLOPT_TIMEOUT, $this->options['curl_timeout']); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->options['curl_connecttimeout']); + curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->curl_connecttimeout); // set variables for headers $header = []; diff --git a/lib/Consumer/QueueConsumer.php b/lib/Consumer/QueueConsumer.php index c597715..a7f75bc 100644 --- a/lib/Consumer/QueueConsumer.php +++ b/lib/Consumer/QueueConsumer.php @@ -21,6 +21,8 @@ abstract class QueueConsumer extends Consumer protected string $host = ''; protected bool $compress_request = false; protected int $flush_interval_in_mills = 10000; //frequency in milliseconds to send data, default 10 + protected int $curl_timeout = 0; // by default this is infinite + protected int $curl_connecttimeout = 300; /** * Store our secret and options as part of this consumer @@ -72,6 +74,14 @@ public function __construct(string $secret, array $options = []) } } + if (isset($options['curl_timeout'])) { + $this->curl_timeout = $options['curl_timeout']; + } + + if (isset($options['curl_connecttimeout'])) { + $this->curl_connecttimeout = $options['curl_connecttimeout']; + } + $this->queue = []; } diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index 50f0147..de12469 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -28,6 +28,10 @@ public function __construct(string $secret, array $options = []) $options['host'] = 'api.segment.io'; } + if (!isset($options['tls'])) { + $options['tls'] = ''; + } + parent::__construct($secret, $options); } From 0ac22027d3b967b635e6094e372d76a9d02062fc Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 10 Nov 2021 10:20:26 -0600 Subject: [PATCH 109/151] Fix coding standards issues --- lib/Consumer/LibCurl.php | 2 +- lib/Consumer/QueueConsumer.php | 6 +++--- lib/Consumer/Socket.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/Consumer/LibCurl.php b/lib/Consumer/LibCurl.php index 87407ec..06a12ca 100644 --- a/lib/Consumer/LibCurl.php +++ b/lib/Consumer/LibCurl.php @@ -98,4 +98,4 @@ public function flushBatch(array $messages): bool return true; } -} +} \ No newline at end of file diff --git a/lib/Consumer/QueueConsumer.php b/lib/Consumer/QueueConsumer.php index a7f75bc..6460eca 100644 --- a/lib/Consumer/QueueConsumer.php +++ b/lib/Consumer/QueueConsumer.php @@ -76,11 +76,11 @@ public function __construct(string $secret, array $options = []) if (isset($options['curl_timeout'])) { $this->curl_timeout = $options['curl_timeout']; - } + } if (isset($options['curl_connecttimeout'])) { $this->curl_connecttimeout = $options['curl_connecttimeout']; - } + } $this->queue = []; } @@ -237,4 +237,4 @@ protected function payload(array $batch): array 'sentAt' => date('c'), ]; } -} +} \ No newline at end of file diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index de12469..03420e9 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -30,7 +30,7 @@ public function __construct(string $secret, array $options = []) if (!isset($options['tls'])) { $options['tls'] = ''; - } + } parent::__construct($secret, $options); } @@ -222,4 +222,4 @@ private static function parseResponse(string $res): array 'message' => $message, ]; } -} +} \ No newline at end of file From 05a8f52008b06041da758afd2523b31aabb7ccc1 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 10 Nov 2021 10:22:34 -0600 Subject: [PATCH 110/151] Coding standards updates --- lib/Consumer/LibCurl.php | 2 +- lib/Consumer/QueueConsumer.php | 2 +- lib/Consumer/Socket.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Consumer/LibCurl.php b/lib/Consumer/LibCurl.php index 06a12ca..87407ec 100644 --- a/lib/Consumer/LibCurl.php +++ b/lib/Consumer/LibCurl.php @@ -98,4 +98,4 @@ public function flushBatch(array $messages): bool return true; } -} \ No newline at end of file +} diff --git a/lib/Consumer/QueueConsumer.php b/lib/Consumer/QueueConsumer.php index 6460eca..dace566 100644 --- a/lib/Consumer/QueueConsumer.php +++ b/lib/Consumer/QueueConsumer.php @@ -237,4 +237,4 @@ protected function payload(array $batch): array 'sentAt' => date('c'), ]; } -} \ No newline at end of file +} diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index 03420e9..f9bddc1 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -222,4 +222,4 @@ private static function parseResponse(string $res): array 'message' => $message, ]; } -} \ No newline at end of file +} From 67357d105e2c2827f6d9932ea0c23cff1ae3301a Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Tue, 14 Dec 2021 15:36:13 -0600 Subject: [PATCH 111/151] Remove consumer option to disable SSL --- lib/Consumer/Consumer.php | 11 ----------- lib/Consumer/ForkCurl.php | 3 +-- lib/Consumer/LibCurl.php | 3 +-- lib/Consumer/QueueConsumer.php | 1 + lib/Consumer/Socket.php | 4 ++-- 5 files changed, 5 insertions(+), 17 deletions(-) diff --git a/lib/Consumer/Consumer.php b/lib/Consumer/Consumer.php index b9b87f1..e863e90 100644 --- a/lib/Consumer/Consumer.php +++ b/lib/Consumer/Consumer.php @@ -83,17 +83,6 @@ public function getConsumer(): string return $this->type; } - /** - * Check whether we should connect to the API using SSL. This is enabled by - * default with connections which make batching requests. For connections - * which can save on round-trip times, you may disable it. - * @return bool - */ - protected function ssl(): bool - { - return $this->options['ssl'] ?? true; - } - /** * On an error, try and call the error handler, if debugging output to * error_log as well. diff --git a/lib/Consumer/ForkCurl.php b/lib/Consumer/ForkCurl.php index 0466efc..271397e 100644 --- a/lib/Consumer/ForkCurl.php +++ b/lib/Consumer/ForkCurl.php @@ -23,14 +23,13 @@ public function flushBatch(array $messages): bool $payload = escapeshellarg($payload); $secret = escapeshellarg($this->secret); - $protocol = $this->ssl() ? 'https://' : 'http://'; if ($this->host) { $host = $this->host; } else { $host = 'api.segment.io'; } $path = '/v1/batch'; - $url = $protocol . $host . $path; + $url = $this->protocol . $host . $path; $cmd = "curl -u $secret: -X POST -H 'Content-Type: application/json'"; diff --git a/lib/Consumer/LibCurl.php b/lib/Consumer/LibCurl.php index 87407ec..405467b 100644 --- a/lib/Consumer/LibCurl.php +++ b/lib/Consumer/LibCurl.php @@ -25,14 +25,13 @@ public function flushBatch(array $messages): bool $payload = gzencode($payload); } - $protocol = $this->ssl() ? 'https://' : 'http://'; if ($this->host) { $host = $this->host; } else { $host = 'api.segment.io'; } $path = '/v1/batch'; - $url = $protocol . $host . $path; + $url = $this->protocol . $host . $path; $backoff = 100; // Set initial waiting time to 100ms diff --git a/lib/Consumer/QueueConsumer.php b/lib/Consumer/QueueConsumer.php index dace566..b147067 100644 --- a/lib/Consumer/QueueConsumer.php +++ b/lib/Consumer/QueueConsumer.php @@ -7,6 +7,7 @@ abstract class QueueConsumer extends Consumer { protected string $type = 'QueueConsumer'; + protected string $protocol = 'https://'; /** * @var array diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index f9bddc1..4fd2fac 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -65,9 +65,9 @@ private function createSocket() return false; } - $protocol = $this->options['tls'] ? 'tls' : ($this->ssl() ? 'ssl' : 'tcp'); + $protocol = $this->options['tls'] ? 'tls' : 'ssl'); $host = $this->options['host']; - $port = $this->ssl() ? 443 : 80; + $port = 443; $timeout = $this->options['timeout']; // Open our socket to the API Server. From 5de2bc4f6e73d6921a2db91cfc262e6b235768f2 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Tue, 14 Dec 2021 15:40:05 -0600 Subject: [PATCH 112/151] Resolve Syntax error --- lib/Consumer/Socket.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index 4fd2fac..9aa50c6 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -65,7 +65,7 @@ private function createSocket() return false; } - $protocol = $this->options['tls'] ? 'tls' : 'ssl'); + $protocol = $this->options['tls'] ? 'tls' : 'ssl'; $host = $this->options['host']; $port = 443; $timeout = $this->options['timeout']; From 4516a72a375512ebc6746238cbf51fefe9f9c5b2 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Tue, 14 Dec 2021 15:52:39 -0600 Subject: [PATCH 113/151] Update coding standards --- lib/Consumer/QueueConsumer.php | 4 ++-- lib/SegmentException.php | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/Consumer/QueueConsumer.php b/lib/Consumer/QueueConsumer.php index b147067..fe7ee39 100644 --- a/lib/Consumer/QueueConsumer.php +++ b/lib/Consumer/QueueConsumer.php @@ -7,8 +7,8 @@ abstract class QueueConsumer extends Consumer { protected string $type = 'QueueConsumer'; - protected string $protocol = 'https://'; - + protected string $protocol = 'https://'; + /** * @var array */ diff --git a/lib/SegmentException.php b/lib/SegmentException.php index 0c0a135..158416d 100644 --- a/lib/SegmentException.php +++ b/lib/SegmentException.php @@ -6,5 +6,4 @@ class SegmentException extends \Exception { - } From 89aaf87df071a5791184ebafea3feecee3393c05 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Tue, 14 Dec 2021 15:54:43 -0600 Subject: [PATCH 114/151] Coding standards update --- lib/Consumer/QueueConsumer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Consumer/QueueConsumer.php b/lib/Consumer/QueueConsumer.php index fe7ee39..debe0c5 100644 --- a/lib/Consumer/QueueConsumer.php +++ b/lib/Consumer/QueueConsumer.php @@ -8,7 +8,7 @@ abstract class QueueConsumer extends Consumer { protected string $type = 'QueueConsumer'; protected string $protocol = 'https://'; - + /** * @var array */ From 67e0854b2136645fa5f0099ed9fddc6dfdeae65c Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 21 Jul 2022 17:48:16 -0500 Subject: [PATCH 115/151] Correct payload size from 32kb to 512kb; update history and prepare for new release --- HISTORY.md | 8 ++++++++ lib/Consumer/ForkCurl.php | 6 +++--- lib/Consumer/Socket.php | 6 +++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b44bcf5..a416000 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,11 @@ +3.5.0 / 2022-07-21 +================== + + * Correct Payload size check of 512kb (#199) + * Add new consumer configurable options: curl_timeout, curl_connecttimeout, max_item_size_bytes, max_queue_size_bytes (#192, #197, #198) + * Depricate HTTP Option (#194 & #195) + + 3.0.0 / 2021-10-14 ================== diff --git a/lib/Consumer/ForkCurl.php b/lib/Consumer/ForkCurl.php index 271397e..8c7a3b5 100644 --- a/lib/Consumer/ForkCurl.php +++ b/lib/Consumer/ForkCurl.php @@ -54,9 +54,9 @@ public function flushBatch(array $messages): bool $cmd .= " '" . $url . "'"; - // Verify message size is below than 32KB - if (strlen($payload) >= 32 * 1024) { - $msg = 'Message size is larger than 32KB'; + // Verify payload size is below 512KB + if (strlen($payload) >= 500 * 1024) { + $msg = 'Payload size is larger than 512KB'; error_log('[Analytics][' . $this->type . '] ' . $msg); return false; diff --git a/lib/Consumer/Socket.php b/lib/Consumer/Socket.php index 9aa50c6..c339575 100644 --- a/lib/Consumer/Socket.php +++ b/lib/Consumer/Socket.php @@ -121,9 +121,9 @@ private function createBody(string $host, string $content) $req .= "\r\n"; $req .= $content; - // Verify message size is below than 32KB - if (strlen($req) >= 32 * 1024) { - $msg = 'Message size is larger than 32KB'; + // Verify payload size is below 512KB + if (strlen($req) >= 500 * 1024) { + $msg = 'Payload size is larger than 512KB'; /** @noinspection ForgottenDebugOutputInspection */ error_log('[Analytics][' . $this->type . '] ' . $msg); From c11d317b65db1c89b4d6170088d24fbbccd59930 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Thu, 21 Jul 2022 17:50:42 -0500 Subject: [PATCH 116/151] Update HISTORY.md --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index a416000..c46c57e 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,7 +1,7 @@ 3.5.0 / 2022-07-21 ================== - * Correct Payload size check of 512kb (#199) + * Correct Payload size check of 512kb (#202) * Add new consumer configurable options: curl_timeout, curl_connecttimeout, max_item_size_bytes, max_queue_size_bytes (#192, #197, #198) * Depricate HTTP Option (#194 & #195) From 804d9c358e13b1b8a86ed73ed68c40291ab9ac6c Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 21 Jul 2022 18:05:52 -0500 Subject: [PATCH 117/151] Configuration changes for Lint test --- Makefile | 2 ++ composer.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7903cb5..9f5ef2c 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,8 @@ lint: dependencies then \ php ./composer.phar require overtrue/phplint --dev; \ php ./composer.phar require squizlabs/php_codesniffer --dev; \ + php ./composer.phar require dealerdirect/phpcodesniffer-composer-installer --dev; \ + ./vendor/bin/phplint; \ ./vendor/bin/phpcs; \ else \ diff --git a/composer.json b/composer.json index 3df5c96..ea77fde 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "segmentio/analytics-php", - "version": "3.0.0", + "version": "3.5.0", "description": "Segment Analytics PHP Library", "keywords": [ "analytics", From c5541eeb4b6e24d4cc2e5f1aa5b040b9c01e9e7d Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 21 Jul 2022 18:09:17 -0500 Subject: [PATCH 118/151] Configuration Change --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ea77fde..c8169f0 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "squizlabs/php_codesniffer": "^3.6", "roave/security-advisories": "dev-latest", "slevomat/coding-standard": "^7.0", - "php-parallel-lint/php-parallel-lint": "^1.3" + "php-parallel-lint/php-parallel-lint": "^1.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7" }, "suggest": { "ext-curl": "For using the curl HTTP client", From 6875f1142f459ce0f78023f0c4f711474e6fafff Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Thu, 21 Jul 2022 18:23:10 -0500 Subject: [PATCH 119/151] Version GLOBAL update --- lib/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Version.php b/lib/Version.php index e89ae87..080a18a 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -3,4 +3,4 @@ declare(strict_types=1); global $SEGMENT_VERSION; -$SEGMENT_VERSION = '3.0.0'; +$SEGMENT_VERSION = '3.5.0'; From 8f463e8b3546568c631d27f34046cae8140de664 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Wed, 27 Jul 2022 08:02:11 -0500 Subject: [PATCH 120/151] update config for lint test error --- composer.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c8169f0..ef4fd8d 100644 --- a/composer.json +++ b/composer.json @@ -50,5 +50,13 @@ "lint": [ "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php,phps --exclude vendor --exclude .git --exclude build" ] - } + }, + "config": { + "allow-plugins": { + "third-party/required-plugin": true, + "segmentio/*": true, + "dealerdirect/phpcodesniffer-composer-installer": true, + "unnecessary/plugin": false + } + } } From 4e6226ab5f4d3c00f04e9063256a1c264c7c0f27 Mon Sep 17 00:00:00 2001 From: Shane Duvall Date: Tue, 16 Aug 2022 13:04:59 -0500 Subject: [PATCH 121/151] Update version file location --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9f5ef2c..02030f8 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ lint: dependencies release: @printf "releasing ${VERSION}..." - @printf ' ./lib/Segment/Version.php + @printf ' ./lib/Version.php @node -e "var fs = require('fs'), pkg = require('./composer'); pkg.version = '${VERSION}'; fs.writeFileSync('./composer.json', JSON.stringify(pkg, null, '\t'));" @git changelog -t ${VERSION} @git release ${VERSION} From 2ed390d1c1e03328f3d5b6c43a72a4ce6a8e7f85 Mon Sep 17 00:00:00 2001 From: Pooya Jaferian Date: Wed, 17 Aug 2022 17:09:18 -0700 Subject: [PATCH 122/151] Release 3.5.0 --- HISTORY.md | 3 +- composer.json | 122 ++++++++++++++++++++++++------------------------ lib/Version.php | 5 +- 3 files changed, 64 insertions(+), 66 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index c46c57e..10f89ea 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,5 @@ -3.5.0 / 2022-07-21 + +3.5.0 / 2022-08-17 ================== * Correct Payload size check of 512kb (#202) diff --git a/composer.json b/composer.json index ef4fd8d..9d97518 100644 --- a/composer.json +++ b/composer.json @@ -1,62 +1,62 @@ { - "name": "segmentio/analytics-php", - "version": "3.5.0", - "description": "Segment Analytics PHP Library", - "keywords": [ - "analytics", - "segmentio", - "segment", - "analytics.js" - ], - "homepage": "https://segment.com/libraries/php", - "license": "MIT", - "authors": [ - { - "name": "Segment.io ", - "homepage": "https://segment.com/" - } - ], - "require": { - "php": "^7.4 || ^8.0", - "ext-json": "*" - }, - "require-dev": { - "phpunit/phpunit": "~9.5", - "overtrue/phplint": "^3.0", - "squizlabs/php_codesniffer": "^3.6", - "roave/security-advisories": "dev-latest", - "slevomat/coding-standard": "^7.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7" - }, - "suggest": { - "ext-curl": "For using the curl HTTP client", - "ext-zlib": "For using compression" - }, - "autoload": { - "psr-4": { - "Segment\\Test\\": "test/", - "Segment\\": "lib/" - } - }, - "bin": [ - "bin/analytics" - ], - "scripts": { - "test": "./vendor/bin/phpunit --no-coverage", - "check": "./vendor/bin/phpcs", - "cf": "./vendor/bin/phpcbf", - "coverage": "./vendor/bin/phpunit", - "lint": [ - "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php,phps --exclude vendor --exclude .git --exclude build" - ] - }, - "config": { - "allow-plugins": { - "third-party/required-plugin": true, - "segmentio/*": true, - "dealerdirect/phpcodesniffer-composer-installer": true, - "unnecessary/plugin": false - } - } -} + "name": "segmentio/analytics-php", + "version": "3.5.0", + "description": "Segment Analytics PHP Library", + "keywords": [ + "analytics", + "segmentio", + "segment", + "analytics.js" + ], + "homepage": "https://segment.com/libraries/php", + "license": "MIT", + "authors": [ + { + "name": "Segment.io ", + "homepage": "https://segment.com/" + } + ], + "require": { + "php": "^7.4 || ^8.0", + "ext-json": "*" + }, + "require-dev": { + "phpunit/phpunit": "~9.5", + "overtrue/phplint": "^3.0", + "squizlabs/php_codesniffer": "^3.6", + "roave/security-advisories": "dev-latest", + "slevomat/coding-standard": "^7.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7" + }, + "suggest": { + "ext-curl": "For using the curl HTTP client", + "ext-zlib": "For using compression" + }, + "autoload": { + "psr-4": { + "Segment\\Test\\": "test/", + "Segment\\": "lib/" + } + }, + "bin": [ + "bin/analytics" + ], + "scripts": { + "test": "./vendor/bin/phpunit --no-coverage", + "check": "./vendor/bin/phpcs", + "cf": "./vendor/bin/phpcbf", + "coverage": "./vendor/bin/phpunit", + "lint": [ + "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php,phps --exclude vendor --exclude .git --exclude build" + ] + }, + "config": { + "allow-plugins": { + "third-party/required-plugin": true, + "segmentio/*": true, + "dealerdirect/phpcodesniffer-composer-installer": true, + "unnecessary/plugin": false + } + } +} \ No newline at end of file diff --git a/lib/Version.php b/lib/Version.php index 080a18a..c0cf444 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -1,6 +1,3 @@ Date: Fri, 23 Sep 2022 20:37:41 -0500 Subject: [PATCH 123/151] Fix missing version information (#207) * Issue #206 fix missing version information * Update ci config to retrieve repo key * resolve github action coding standards errors * Update github actions coding standard errors --- .circleci/config.yml | 1 + lib/Client.php | 2 ++ lib/Version.php | 6 +++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7cb947c..4462f6f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -12,6 +12,7 @@ jobs: XDEBUG_MODE: coverage steps: - checkout + - run: wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - - run: sudo apt update - run: sudo apt install zlib1g-dev - run: sudo docker-php-ext-install zip diff --git a/lib/Client.php b/lib/Client.php index 7d326ed..51ea526 100644 --- a/lib/Client.php +++ b/lib/Client.php @@ -10,6 +10,8 @@ use Segment\Consumer\LibCurl; use Segment\Consumer\Socket; +require __DIR__ . '/Version.php'; + class Client { protected Consumer $consumer; diff --git a/lib/Version.php b/lib/Version.php index c0cf444..e4b50ae 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -1,3 +1,7 @@ Date: Wed, 26 Oct 2022 12:54:37 -0500 Subject: [PATCH 124/151] Issue #208 Correct autoload require statement (#209) --- send.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/send.php b/send.php index 4b14ad8..a418c0f 100755 --- a/send.php +++ b/send.php @@ -8,7 +8,7 @@ * require client */ -require './vendor/autoload.php'; +require __DIR__ . '/vendor/autoload.php'; /** * Args From d6c445e8fa8e69999eaa97f7916b4fa1e02c52c0 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Fri, 10 Mar 2023 16:31:38 -0600 Subject: [PATCH 125/151] Issue #210 test update (#211) * Initial ci update, determine if core images will work without refactoring source * github actions script update, move 8.1 from exp to supported; add 8.2 as exp * Fix coding standards and add 8.2 as supported. * Correct error * Coding standards * stds * Update CI config * Update config image * force file creation * adding command * undo prev changes * updating image * Move require statement * Fix coding stds error * coding stds --- .circleci/config.yml | 17 +++++++++++++++-- .github/workflows/tests.yml | 18 ++++++++---------- HISTORY.md | 5 +++++ lib/Client.php | 4 ++-- lib/Version.php | 2 +- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4462f6f..73cdca2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,3 +1,4 @@ + # PHP CircleCI 2.0 configuration file # # Check https://circleci.com/docs/2.0/language-php/ for more details @@ -16,6 +17,8 @@ jobs: - run: sudo apt update - run: sudo apt install zlib1g-dev - run: sudo docker-php-ext-install zip + - run: php --version + - run: node --version - restore_cache: keys: @@ -51,10 +54,20 @@ jobs: test-php8.0: <<: *multi-test docker: - - image: circleci/php:8.0-node + - image: circleci/php:8.0-browsers + test-php8.1: + <<: *multi-test + docker: + - image: cimg/php:8.1-browsers + test-php8.2: + <<: *multi-test + docker: + - image: cimg/php:8.2-browsers workflows: version: 2 multi-test: jobs: - test-php7.4 - - test-php8.0 \ No newline at end of file + - test-php8.0 + - test-php8.1 + - test-php8.2 \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1bf9b9e..6a6f1d3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ jobs: - name: Set up PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.0' + php-version: '8.1' coverage: none tools: cs2pr @@ -38,11 +38,8 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - php: ['7.4', '8.0'] + php: ['7.4', '8.0', '8.1', '8.2'] experimental: [false] - include: - - php: '8.1' - experimental: true name: "Lint: PHP ${{ matrix.php }}" continue-on-error: ${{ matrix.experimental }} @@ -76,7 +73,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - php: ['7.4', '8.0'] + php: ['7.4', '8.0', '8.1', '8.2'] coverage: [false] experimental: [false] include: @@ -87,11 +84,12 @@ jobs: - php: '8.0' coverage: true experimental: false - - # Experimental builds. These are allowed to fail. - php: '8.1' - coverage: false - experimental: true + coverage: true + experimental: false + - php: '8.2' + coverage: true + experimental: false name: "Test: PHP ${{ matrix.php }}" diff --git a/HISTORY.md b/HISTORY.md index 10f89ea..f73bd0b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,8 @@ +3.5.1 / 2023-01-31 +================== + + * Include support for 8.1 * 8.2 (#210) + 3.5.0 / 2022-08-17 ================== diff --git a/lib/Client.php b/lib/Client.php index 51ea526..bfae6ea 100644 --- a/lib/Client.php +++ b/lib/Client.php @@ -10,8 +10,6 @@ use Segment\Consumer\LibCurl; use Segment\Consumer\Socket; -require __DIR__ . '/Version.php'; - class Client { protected Consumer $consumer; @@ -110,6 +108,8 @@ private function message(array $msg, string $def = ''): array */ private function getDefaultContext(): array { + require __DIR__ . '/Version.php'; + global $SEGMENT_VERSION; return [ diff --git a/lib/Version.php b/lib/Version.php index e4b50ae..330b560 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -4,4 +4,4 @@ global $SEGMENT_VERSION; -$SEGMENT_VERSION = '3.5.0'; +$SEGMENT_VERSION = '3.5.1'; From 776e0157958b4754fc232b779302b0133f8c18c5 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Tue, 28 Mar 2023 12:55:19 -0400 Subject: [PATCH 126/151] Release 2.1.0 --- HISTORY.md | 8 ++++++++ composer.json | 2 +- lib/Version.php | 6 +----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index f73bd0b..d995818 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,11 @@ + +2.1.0 / 2023-03-28 +================== + + * Issue #210 test update (#211) + * Issue #208 Correct autoload require statement (#209) + * Fix missing version information (#207) + 3.5.1 / 2023-01-31 ================== diff --git a/composer.json b/composer.json index 9d97518..57c7a06 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "segmentio/analytics-php", - "version": "3.5.0", + "version": "2.1.0", "description": "Segment Analytics PHP Library", "keywords": [ "analytics", diff --git a/lib/Version.php b/lib/Version.php index 330b560..9cff808 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -1,7 +1,3 @@ Date: Tue, 28 Mar 2023 13:29:58 -0400 Subject: [PATCH 127/151] Release 3.6.0 --- HISTORY.md | 7 +------ composer.json | 2 +- lib/Version.php | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index d995818..0c23ad8 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,14 +1,9 @@ -2.1.0 / 2023-03-28 +3.6.0 / 2023-03-28 ================== - * Issue #210 test update (#211) * Issue #208 Correct autoload require statement (#209) * Fix missing version information (#207) - -3.5.1 / 2023-01-31 -================== - * Include support for 8.1 * 8.2 (#210) diff --git a/composer.json b/composer.json index 57c7a06..967a278 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "segmentio/analytics-php", - "version": "2.1.0", + "version": "3.6.0", "description": "Segment Analytics PHP Library", "keywords": [ "analytics", diff --git a/lib/Version.php b/lib/Version.php index 9cff808..8821c82 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -1,3 +1,3 @@ Date: Wed, 5 Jul 2023 19:33:16 +0200 Subject: [PATCH 128/151] Fallback to global project's autoloader (#215) --- bin/analytics | 2 +- bootstrap.php | 23 +++++++++++++++++++++++ phpunit.xml | 2 +- send.php | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 bootstrap.php diff --git a/bin/analytics b/bin/analytics index f08aa39..b50ae59 100755 --- a/bin/analytics +++ b/bin/analytics @@ -3,7 +3,7 @@ use Segment\Segment; -require __DIR__ . '/../vendor/autoload.php'; +require __DIR__ . '/../bootstrap.php'; if (in_array('--help', $argv, true)) { print(usage()); diff --git a/bootstrap.php b/bootstrap.php new file mode 100644 index 0000000..e236436 --- /dev/null +++ b/bootstrap.php @@ -0,0 +1,23 @@ + Date: Wed, 5 Jul 2023 19:36:57 +0200 Subject: [PATCH 129/151] Fix coding style issues (#217) --- lib/Version.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Version.php b/lib/Version.php index 8821c82..36c15f3 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -1,3 +1,6 @@ Date: Wed, 26 Jul 2023 12:34:55 -0500 Subject: [PATCH 130/151] Remove circleci config and prepare for release (#219) --- .circleci/config.yml | 73 -------------------------------------------- HISTORY.md | 7 +++++ 2 files changed, 7 insertions(+), 73 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 73cdca2..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,73 +0,0 @@ - -# PHP CircleCI 2.0 configuration file -# -# Check https://circleci.com/docs/2.0/language-php/ for more details -# -version: 2 -jobs: - - multi-test: &multi-test - docker: - - image: php - environment: - XDEBUG_MODE: coverage - steps: - - checkout - - run: wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - - - run: sudo apt update - - run: sudo apt install zlib1g-dev - - run: sudo docker-php-ext-install zip - - run: php --version - - run: node --version - - - restore_cache: - keys: - - v1-dependencies-{{ checksum "composer.json" }} - - v1-dependencies- - - - run: composer install -n --prefer-dist - - - save_cache: - key: v1-dependencies-{{ checksum "composer.json" }} - paths: - - ./vendor - - restore_cache: - keys: - - node-v1-{{ checksum "composer.json" }} - - node-v1- - - run: yarn install - - save_cache: - key: node-v1-{{ checksum "composer.json" }} - paths: - - node_modules - - run: - name: 'Running unit tests' - command: './vendor/bin/phpunit test' - - run: - name: 'Running E2E tests' - command: '.buildscript/e2e.sh' - - test-php7.4: - <<: *multi-test - docker: - - image: circleci/php:7.4-node-browsers - test-php8.0: - <<: *multi-test - docker: - - image: circleci/php:8.0-browsers - test-php8.1: - <<: *multi-test - docker: - - image: cimg/php:8.1-browsers - test-php8.2: - <<: *multi-test - docker: - - image: cimg/php:8.2-browsers -workflows: - version: 2 - multi-test: - jobs: - - test-php7.4 - - test-php8.0 - - test-php8.1 - - test-php8.2 \ No newline at end of file diff --git a/HISTORY.md b/HISTORY.md index 0c23ad8..e37a54c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,11 @@ +3.7.0 / 2023-07-24 +================== + + * Convert to github actions + * Remove circleci config/files + * Update autoloader + 3.6.0 / 2023-03-28 ================== From cf74bb7ba9c8ecd622339d1772b1bb5de298bcc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Wed, 9 Aug 2023 17:45:11 +0200 Subject: [PATCH 131/151] Remove version from composer.json (#221) * Remove version from composer.json * Remove version update from Makefile --- Makefile | 1 - composer.json | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 02030f8..db11162 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,6 @@ lint: dependencies release: @printf "releasing ${VERSION}..." @printf ' ./lib/Version.php - @node -e "var fs = require('fs'), pkg = require('./composer'); pkg.version = '${VERSION}'; fs.writeFileSync('./composer.json', JSON.stringify(pkg, null, '\t'));" @git changelog -t ${VERSION} @git release ${VERSION} diff --git a/composer.json b/composer.json index 967a278..708088d 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,5 @@ { "name": "segmentio/analytics-php", - "version": "3.6.0", "description": "Segment Analytics PHP Library", "keywords": [ "analytics", @@ -59,4 +58,4 @@ "unnecessary/plugin": false } } -} \ No newline at end of file +} From 4e24126784a558b82375f6f22c638f60a681b25c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Wed, 9 Aug 2023 17:46:02 +0200 Subject: [PATCH 132/151] Remove unused log file (#223) --- test/file.log | 6 ------ 1 file changed, 6 deletions(-) delete mode 100755 test/file.log diff --git a/test/file.log b/test/file.log deleted file mode 100755 index 11791af..0000000 --- a/test/file.log +++ /dev/null @@ -1,6 +0,0 @@ -{"userId":"some-user","event":"File PHP Event","context":{"library":{"name":"analytics-php","version":"1.0.0"}},"timestamp":"2014-05-13T16:19:17+00:00","messageId":"f8c9cda1-f21b-4d40-8198-085eaa99dedb","type":"track"} -{"userId":"Calvin","traits":{"loves_php":false,"type":"analytics.log","birthday":1399997957},"context":{"library":{"name":"analytics-php","version":"1.0.0"}},"timestamp":"2014-05-13T16:19:17+00:00","messageId":"fe37073d-40fa-41e1-b826-8659fe74a199","type":"identify"} -{"userId":"user-id","groupId":"group-id","traits":{"type":"consumer analytics.log test"},"context":{"library":{"name":"analytics-php","version":"1.0.0"}},"timestamp":"2014-05-13T16:19:17+00:00","messageId":"8e6bb92a-7347-424a-94d6-a4234617b070","type":"group"} -{"userId":"user-id","name":"analytics-php","category":"analytics.log","properties":{"url":"https:\/\/a.url\/"},"context":{"library":{"name":"analytics-php","version":"1.0.0"}},"timestamp":"2014-05-13T16:19:17+00:00","messageId":"b3db73cd-337b-4b50-8967-f653f9183e02","type":"page"} -{"userId":"userId","name":"grand theft auto","category":"analytics.log","properties":[],"context":{"library":{"name":"analytics-php","version":"1.0.0"}},"timestamp":"2014-05-13T16:19:17+00:00","messageId":"2527e1a0-e631-47b1-872e-f575f0722547","type":"page"} -{"previousId":"previous-id","userId":"user-id","context":{"library":{"name":"analytics-php","version":"1.0.0"}},"timestamp":"2014-05-13T16:19:17+00:00","messageId":"5f883f43-c15c-49c6-b4e6-59d5cb6d657b","type":"alias"} From 8ce2ad28970873e092ff81ba87da4535f436a0bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Wed, 9 Aug 2023 17:48:44 +0200 Subject: [PATCH 133/151] Fix typos (#220) --- HISTORY.md | 10 +++++----- Makefile | 2 +- lib/Consumer/QueueConsumer.php | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index e37a54c..6b02e7f 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -19,7 +19,7 @@ * Correct Payload size check of 512kb (#202) * Add new consumer configurable options: curl_timeout, curl_connecttimeout, max_item_size_bytes, max_queue_size_bytes (#192, #197, #198) - * Depricate HTTP Option (#194 & #195) + * Deprecate HTTP Option (#194 & #195) 3.0.0 / 2021-10-14 @@ -62,10 +62,10 @@ * Update Tests [Improvement] (#132) * Updtate Readme Status Badging (#139) * Bump e2e tests to latest version [Improvement] (#142) - * Add Limits to message, batch and memeory usage [Feature] (#137) + * Add Limits to message, batch and memory usage [Feature] (#137) * Add Configurable flush parameters [Feature] (#135) * Add ability to use custom consumer [Feature] (#61) - * Add ability to set file permmissions [Feature] (#122) + * Add ability to set file permissions [Feature] (#122) * Fix curl error handler [Improvement] (#97) * Fix timestamp implementation for microseconds (#94) * Modify max queue size setting to match requirements (#153, #146) @@ -105,7 +105,7 @@ 1.5.1 / 2017-04-06 ================== - * Use require_once() instead of require(). Fixes issue where seperate plugins in systems such as Moodle break because of class redeclaration when using seperate internal versions of Segment.io. + * Use require_once() instead of require(). Fixes issue where separate plugins in systems such as Moodle break because of class redeclaration when using separate internal versions of Segment.io. 1.5.0 / 2017-03-03 ================== @@ -113,7 +113,7 @@ * Adding context.library.consumer to all PHP events * libcurl consumer will retry once if http response is not 200 * update link to php docs - * improve portability and reliability of Makefile accros different platforms (#74) + * improve portability and reliability of Makefile across different platforms (#74) 1.4.2 / 2016-07-11 ================== diff --git a/Makefile b/Makefile index db11162..3819410 100644 --- a/Makefile +++ b/Makefile @@ -38,4 +38,4 @@ clean: vendor \ composer.lock -.PHONY: boostrap release clean +.PHONY: bootstrap release clean diff --git a/lib/Consumer/QueueConsumer.php b/lib/Consumer/QueueConsumer.php index debe0c5..60f7e43 100644 --- a/lib/Consumer/QueueConsumer.php +++ b/lib/Consumer/QueueConsumer.php @@ -43,7 +43,7 @@ public function __construct(string $secret, array $options = []) $msg = 'Batch Size must not be less than 1'; error_log('[Analytics][' . $this->type . '] ' . $msg); } else { - $msg = 'WARNING: batch_size option to be depricated soon, please use new option flush_at'; + $msg = 'WARNING: batch_size option to be deprecated soon, please use new option flush_at'; error_log('[Analytics][' . $this->type . '] ' . $msg); $this->flush_at = $options['batch_size']; } From ce198816d73857c95ffb4e6ecd5ba174392d391a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Wed, 9 Aug 2023 17:51:43 +0200 Subject: [PATCH 134/151] Remove examples from composer.json (#224) Co-authored-by: Michael Grosse Huelsewiesche --- composer.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 708088d..2357724 100644 --- a/composer.json +++ b/composer.json @@ -52,10 +52,8 @@ }, "config": { "allow-plugins": { - "third-party/required-plugin": true, "segmentio/*": true, - "dealerdirect/phpcodesniffer-composer-installer": true, - "unnecessary/plugin": false + "dealerdirect/phpcodesniffer-composer-installer": true } } } From 2ab7c0fe3dc8a4c8194eb5f575f053085996cc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=B6ller?= Date: Wed, 9 Aug 2023 17:52:24 +0200 Subject: [PATCH 135/151] Fix: Reference schema as installed with composer (#225) --- phpunit.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 91c6d7b..3fcd1a6 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,6 +1,6 @@ - \ No newline at end of file + From dd47f608be47792695ebeadee7a7b6c89ee98322 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=B6ller?= Date: Wed, 16 Aug 2023 20:09:27 +0200 Subject: [PATCH 136/151] Fix: Property can be `null` (#226) * Enhancement: Assert that Segment throws SegmentException without initialization * Fix: Property can be null --- lib/Segment.php | 2 +- test/SegmentTest.php | 78 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 test/SegmentTest.php diff --git a/lib/Segment.php b/lib/Segment.php index 98e09e2..226a1d5 100644 --- a/lib/Segment.php +++ b/lib/Segment.php @@ -6,7 +6,7 @@ class Segment { - private static Client $client; + private static ?Client $client = null; /** * Initializes the default client to use. Uses the libcurl consumer by default. diff --git a/test/SegmentTest.php b/test/SegmentTest.php new file mode 100644 index 0000000..29794ab --- /dev/null +++ b/test/SegmentTest.php @@ -0,0 +1,78 @@ +expectException(SegmentException::class); + $this->expectExceptionMessage('Segment::init() must be called before any other tracking method.'); + + Segment::alias([]); + } + public function testFlushThrowsSegmentExceptionWhenClientHasNotBeenInitialized(): void + { + $this->expectException(SegmentException::class); + $this->expectExceptionMessage('Segment::init() must be called before any other tracking method.'); + + Segment::flush(); + } + public function testGroupThrowsSegmentExceptionWhenClientHasNotBeenInitialized(): void + { + $this->expectException(SegmentException::class); + $this->expectExceptionMessage('Segment::init() must be called before any other tracking method.'); + + Segment::group([]); + } + public function testIdentifyThrowsSegmentExceptionWhenClientHasNotBeenInitialized(): void + { + $this->expectException(SegmentException::class); + $this->expectExceptionMessage('Segment::init() must be called before any other tracking method.'); + + Segment::identify([]); + } + public function testPageThrowsSegmentExceptionWhenClientHasNotBeenInitialized(): void + { + $this->expectException(SegmentException::class); + $this->expectExceptionMessage('Segment::init() must be called before any other tracking method.'); + + Segment::page([]); + } + public function testScreenThrowsSegmentExceptionWhenClientHasNotBeenInitialized(): void + { + $this->expectException(SegmentException::class); + $this->expectExceptionMessage('Segment::init() must be called before any other tracking method.'); + + Segment::screen([]); + } + public function testTrackThrowsSegmentExceptionWhenClientHasNotBeenInitialized(): void + { + $this->expectException(SegmentException::class); + $this->expectExceptionMessage('Segment::init() must be called before any other tracking method.'); + + Segment::track([]); + } + + private static function resetSegment(): void + { + $property = new \ReflectionProperty( + Segment::class, + 'client' + ); + + $property->setAccessible(true); + $property->setValue(null); + } +} From 30988c33585777dff694f13e7812212899b550e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Thu, 17 Aug 2023 16:17:54 +0200 Subject: [PATCH 137/151] Move autoloading tests to autoload-dev (#222) --- composer.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2357724..4fd9172 100644 --- a/composer.json +++ b/composer.json @@ -34,10 +34,14 @@ }, "autoload": { "psr-4": { - "Segment\\Test\\": "test/", "Segment\\": "lib/" } }, + "autoload-dev": { + "psr-4": { + "Segment\\Test\\": "test/" + } + }, "bin": [ "bin/analytics" ], From 269ebe62984bac0e6c038f041964fb10e05cc280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Sz=C3=A9pe?= Date: Thu, 17 Aug 2023 16:20:19 +0200 Subject: [PATCH 138/151] Upgrade Tests workflow by Vependabot (#227) * Upgrade Tests workflow * Upgrade codecov/codecov-action --------- Co-authored-by: Michael Grosse Huelsewiesche --- .github/workflows/tests.yml | 51 +++++++++++++++---------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6a6f1d3..1e10eaa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,12 +8,12 @@ on: jobs: coding-standard: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: Coding standards steps: - - name: Check out code - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v3 - name: Set up PHP uses: shivammathur/setup-php@v2 @@ -25,7 +25,7 @@ jobs: # Install dependencies and handle caching in one go. # @link https://github.com/marketplace/actions/install-composer-dependencies - name: Install Composer dependencies - uses: "ramsey/composer-install@v1" + uses: "ramsey/composer-install@v2" - name: Check coding standards continue-on-error: true @@ -35,7 +35,7 @@ jobs: run: cs2pr ./phpcs-report.xml lint: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: php: ['7.4', '8.0', '8.1', '8.2'] @@ -45,8 +45,8 @@ jobs: continue-on-error: ${{ matrix.experimental }} steps: - - name: Checkout code - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v3 - name: Install PHP uses: shivammathur/setup-php@v2 @@ -58,7 +58,7 @@ jobs: # Install dependencies and handle caching in one go. # @link https://github.com/marketplace/actions/install-composer-dependencies - name: Install Composer dependencies - uses: "ramsey/composer-install@v1" + uses: "ramsey/composer-install@v2" - name: Lint against parse errors if: ${{ matrix.php != '8.1' }} @@ -70,7 +70,7 @@ jobs: test: needs: ['coding-standard', 'lint'] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: matrix: php: ['7.4', '8.0', '8.1', '8.2'] @@ -96,49 +96,40 @@ jobs: continue-on-error: ${{ matrix.experimental }} steps: - - name: Check out code - uses: actions/checkout@v2 - - - name: Set coverage variable - id: set_cov - run: | - if [ ${{ matrix.coverage }} == "true" ]; then - echo '::set-output name=COV::xdebug' - else - echo '::set-output name=COV::none' - fi + - name: Checkout repository + uses: actions/checkout@v3 - name: Set up PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - coverage: ${{ steps.set_cov.outputs.COV }} + coverage: ${{ matrix.coverage && 'xdebug' || 'none' }} ini-values: sendmail_path=/usr/sbin/sendmail -t -i, error_reporting=E_ALL, display_errors=On extensions: imap, mbstring, intl, ctype, filter, hash # Install dependencies and handle caching in one go. # @link https://github.com/marketplace/actions/install-composer-dependencies - - name: Install PHP packages - normal + - name: Install dependencies if: ${{ matrix.php != '8.1' }} - uses: "ramsey/composer-install@v1" + uses: "ramsey/composer-install@v2" - - name: Install PHP packages - ignore-platform-reqs + - name: Install dependencies - ignore-platform-reqs if: ${{ matrix.php == '8.1' }} - uses: "ramsey/composer-install@v1" + uses: "ramsey/composer-install@v2" with: composer-options: --ignore-platform-reqs - name: Run tests, no code coverage - if: ${{ matrix.coverage == false }} + if: ${{ matrix.coverage }} run: ./vendor/bin/phpunit --no-coverage - name: Run tests with code coverage - if: ${{ matrix.coverage == true }} - run: vendor/bin/phpunit + if: ${{ matrix.coverage }} + run: ./vendor/bin/phpunit - name: Send coverage report to Codecov - if: ${{ success() && matrix.coverage == true }} - uses: codecov/codecov-action@v1 + if: ${{ success() && matrix.coverage }} + uses: codecov/codecov-action@v3 with: files: ./build/logs/clover.xml fail_ci_if_error: true From 26fb52afdf18be40cfcb6744cb57e9b324f6bc05 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Mon, 11 Sep 2023 09:51:12 -0400 Subject: [PATCH 139/151] Release 3.7.0 --- HISTORY.md | 3 ++- lib/Version.php | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 6b02e7f..0c326bf 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,5 @@ -3.7.0 / 2023-07-24 + +3.7.0 / 2023-09-11 ================== * Convert to github actions diff --git a/lib/Version.php b/lib/Version.php index 36c15f3..65c7be2 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -1,6 +1,3 @@ Date: Wed, 31 Jan 2024 11:34:55 -0600 Subject: [PATCH 140/151] Update to PHP 8.3 (#231) * Add addition php version for tests; modify file for coding standards update * Update missing declaration for coding standards --- .github/workflows/tests.yml | 7 +++++-- lib/Version.php | 6 +++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1e10eaa..751f6e0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -38,7 +38,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - php: ['7.4', '8.0', '8.1', '8.2'] + php: ['7.4', '8.0', '8.1', '8.2', '8.3'] experimental: [false] name: "Lint: PHP ${{ matrix.php }}" @@ -73,7 +73,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - php: ['7.4', '8.0', '8.1', '8.2'] + php: ['7.4', '8.0', '8.1', '8.2', '8.3'] coverage: [false] experimental: [false] include: @@ -90,6 +90,9 @@ jobs: - php: '8.2' coverage: true experimental: false + - php: '8.3' + coverage: true + experimental: false name: "Test: PHP ${{ matrix.php }}" diff --git a/lib/Version.php b/lib/Version.php index 65c7be2..9c5eb4b 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -1,3 +1,7 @@ Date: Tue, 13 Feb 2024 09:54:24 -0700 Subject: [PATCH 141/151] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 31bee97..40d8a28 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ analytics-php analytics-php is a php client for [Segment](https://segment.com) +### ⚠️ Maintenance ⚠️ +This library is in maintenance mode. It will send data as intended, but receive no new feature support and only critical maintenance updates from Segment. +

You can't fix what you can't measure

From 84424df6431fea6dff58ebad57b0a1bf1d19b3b8 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Thu, 15 Feb 2024 10:01:29 -0600 Subject: [PATCH 142/151] Updated files to prep for release (#233) * Updated files to prep for release * Fix issue with node update * Update codecov node requirements * Revert "Update codecov node requirements" This reverts commit e3d6a2827e547536f905d47f6ca4fc702eac7bca. --- .github/workflows/tests.yml | 6 +++--- HISTORY.md | 5 +++++ lib/Version.php | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 751f6e0..4c3bf43 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up PHP uses: shivammathur/setup-php@v2 @@ -46,7 +46,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install PHP uses: shivammathur/setup-php@v2 @@ -100,7 +100,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up PHP uses: shivammathur/setup-php@v2 diff --git a/HISTORY.md b/HISTORY.md index 0c326bf..8540ab7 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,8 @@ +3.8.0 / 2024-02-12 +================== + + * Include support for 8.3 (#231) + 3.7.0 / 2023-09-11 ================== diff --git a/lib/Version.php b/lib/Version.php index 9c5eb4b..a362c6e 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -4,4 +4,4 @@ global $SEGMENT_VERSION; -$SEGMENT_VERSION = '3.7.0'; +$SEGMENT_VERSION = '3.8.0'; From 22882f89322b8b110ca449ad1c1425a99dad6bcc Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Thu, 15 Feb 2024 11:08:05 -0500 Subject: [PATCH 143/151] Release 3.8.0 --- HISTORY.md | 3 ++- lib/Version.php | 6 +----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 8540ab7..f0f203c 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,5 @@ -3.8.0 / 2024-02-12 + +3.8.0 / 2024-02-15 ================== * Include support for 8.3 (#231) diff --git a/lib/Version.php b/lib/Version.php index a362c6e..942542c 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -1,7 +1,3 @@ Date: Wed, 11 Dec 2024 13:23:12 -0500 Subject: [PATCH 144/151] Update codecov with token (#237) --- .github/workflows/tests.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4c3bf43..53bf512 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -132,8 +132,10 @@ jobs: - name: Send coverage report to Codecov if: ${{ success() && matrix.coverage }} - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 with: files: ./build/logs/clover.xml fail_ci_if_error: true verbose: true + env: + CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}} From 54b41ebc850274a7091ae77be314acdf32758765 Mon Sep 17 00:00:00 2001 From: "Shane L. Duvall" Date: Wed, 11 Dec 2024 12:25:00 -0600 Subject: [PATCH 145/151] Issue 235 fix (#236) * Update secret(s) to pass multiple tests * codecov stds * Codecov stds --- lib/Version.php | 6 +++++- test/ConsumerLibCurlTest.php | 2 +- test/ConsumerSocketTest.php | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/Version.php b/lib/Version.php index 942542c..a362c6e 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -1,3 +1,7 @@ track(['user_id' => 'some-user', 'event' => 'Socket PHP Event'])); diff --git a/test/ConsumerSocketTest.php b/test/ConsumerSocketTest.php index 2c0e860..f4096c7 100644 --- a/test/ConsumerSocketTest.php +++ b/test/ConsumerSocketTest.php @@ -164,7 +164,7 @@ public function testDebugProblems(): void }, ]; - $client = new Client('x', $options); + $client = new Client('oq0vdlg7yi', $options); // Should error out with debug on. self::assertTrue($client->track(['user_id' => 'some-user', 'event' => 'Socket PHP Event'])); From 3fe22ead154e8ac4b0de595fe7a1b6daae29f041 Mon Sep 17 00:00:00 2001 From: Jeroen Date: Wed, 15 Jan 2025 19:24:35 +0100 Subject: [PATCH 146/151] Convert the exec output to string (#239) exec outputs an array of lines, handleError() only accepts strings. This causes a TypeError due to strict_types, and is very hard to debug. Co-authored-by: Jeroen Bakker --- lib/Consumer/ForkCurl.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Consumer/ForkCurl.php b/lib/Consumer/ForkCurl.php index 8c7a3b5..91b06ef 100644 --- a/lib/Consumer/ForkCurl.php +++ b/lib/Consumer/ForkCurl.php @@ -41,6 +41,7 @@ public function flushBatch(array $messages): bool exec($cmd2, $output, $exit); if ($exit !== 0) { + $output = implode(PHP_EOL, $output); $this->handleError($exit, $output); return false; } @@ -75,6 +76,7 @@ public function flushBatch(array $messages): bool exec($cmd, $output, $exit); if ($exit !== 0) { + $output = implode(PHP_EOL, $output); $this->handleError($exit, $output); } From 218d50ad244511672efe8e358fe17926078fe934 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Mon, 27 Jan 2025 13:34:28 -0500 Subject: [PATCH 147/151] Release 3.8.1 (#240) * Release 3.8.1 * Fixing code standard problems caused by release script --- HISTORY.md | 5 +++++ lib/Version.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index f0f203c..8437ee2 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,9 @@ +3.8.1 / 2025-01-27 +================== + + * Convert the exec output to string (#239) + 3.8.0 / 2024-02-15 ================== diff --git a/lib/Version.php b/lib/Version.php index a362c6e..56792c8 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -4,4 +4,4 @@ global $SEGMENT_VERSION; -$SEGMENT_VERSION = '3.8.0'; +$SEGMENT_VERSION = '3.8.1'; From 7c99324b97b9c27b340aafd341230f29948d2b1f Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Mon, 28 Apr 2025 15:24:32 -0400 Subject: [PATCH 148/151] track github issues in jira (#243) --- .github/workflows/create_jira.yml | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/create_jira.yml diff --git a/.github/workflows/create_jira.yml b/.github/workflows/create_jira.yml new file mode 100644 index 0000000..8180ac0 --- /dev/null +++ b/.github/workflows/create_jira.yml @@ -0,0 +1,39 @@ +name: Create Jira Ticket + +on: + issues: + types: + - opened + +jobs: + create_jira: + name: Create Jira Ticket + runs-on: ubuntu-latest + environment: IssueTracker + steps: + - name: Checkout + uses: actions/checkout@master + - name: Login + uses: atlassian/gajira-login@master + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_TOKEN }} + JIRA_EPIC_KEY: ${{ secrets.JIRA_EPIC_KEY }} + JIRA_PROJECT: ${{ secrets.JIRA_PROJECT }} + + - name: Create + id: create + uses: atlassian/gajira-create@master + with: + project: ${{ secrets.JIRA_PROJECT }} + issuetype: Bug + summary: | + [${{ github.event.repository.name }}] (${{ github.event.issue.number }}): ${{ github.event.issue.title }} + description: | + Github Link: ${{ github.event.issue.html_url }} + ${{ github.event.issue.body }} + fields: '{"parent": {"key": "${{ secrets.JIRA_EPIC_KEY }}"}}' + + - name: Log created issue + run: echo "Issue ${{ steps.create.outputs.issue }} was created" \ No newline at end of file From 2b4aa680f91b2ba38be3b1d9e9d9592284fbfab2 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Wed, 11 Mar 2026 14:14:31 -0400 Subject: [PATCH 149/151] Fix OS command injection in ForkCurl consumer (#246) The User-Agent header values (library name and version) from message context were interpolated directly into a shell command without escaping, allowing arbitrary command execution. Apply escapeshellarg() to the User-Agent header and URL, consistent with how payload and secret are already handled. --- lib/Consumer/ForkCurl.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Consumer/ForkCurl.php b/lib/Consumer/ForkCurl.php index 91b06ef..b38528c 100644 --- a/lib/Consumer/ForkCurl.php +++ b/lib/Consumer/ForkCurl.php @@ -53,7 +53,7 @@ public function flushBatch(array $messages): bool $cmd .= ' -d ' . $payload; } - $cmd .= " '" . $url . "'"; + $cmd .= ' ' . escapeshellarg($url); // Verify payload size is below 512KB if (strlen($payload) >= 500 * 1024) { @@ -67,7 +67,7 @@ public function flushBatch(array $messages): bool $library = $messages[0]['context']['library']; $libName = $library['name']; $libVersion = $library['version']; - $cmd .= " -H 'User-Agent: $libName/$libVersion'"; + $cmd .= ' -H ' . escapeshellarg("User-Agent: $libName/$libVersion"); if (!$this->debug()) { $cmd .= ' > /dev/null 2>&1 &'; From be1772821c17599173f09042cb35d747d48f6910 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Wed, 11 Mar 2026 15:08:11 -0400 Subject: [PATCH 150/151] Release/3.8.2 (#247) --- HISTORY.md | 10 ++++++++++ lib/Version.php | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 8437ee2..1642c3f 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,14 @@ +3.8.2 / 2026-03-11 +================== + + + +3.8.2 / 2026-03-11 +================== + + * Fix OS command injection in ForkCurl consumer (#246) + 3.8.1 / 2025-01-27 ================== diff --git a/lib/Version.php b/lib/Version.php index 56792c8..c74361c 100644 --- a/lib/Version.php +++ b/lib/Version.php @@ -4,4 +4,4 @@ global $SEGMENT_VERSION; -$SEGMENT_VERSION = '3.8.1'; +$SEGMENT_VERSION = '3.8.2'; From a910e173bbac4a70f93ac8ae25fb3c5ea37577f0 Mon Sep 17 00:00:00 2001 From: Michael Grosse Huelsewiesche Date: Mon, 11 May 2026 11:30:51 -0400 Subject: [PATCH 151/151] Add e2e-cli for E2E testing (#250) * Add e2e-cli for E2E testing * Fix e2e-cli: use E2eLibCurl subclass to support http:// test server * Add e2e workflows and restrict test suites to basic --- .github/workflows/e2e-tests.yml | 58 +++++ .github/workflows/publish-e2e-cli.yml | 39 ++++ e2e-cli/README.md | 131 +++++++++++ e2e-cli/composer.json | 13 ++ e2e-cli/e2e-config.json | 7 + e2e-cli/main.php | 319 ++++++++++++++++++++++++++ e2e-cli/run-e2e.sh | 27 +++ 7 files changed, 594 insertions(+) create mode 100644 .github/workflows/e2e-tests.yml create mode 100644 .github/workflows/publish-e2e-cli.yml create mode 100644 e2e-cli/README.md create mode 100644 e2e-cli/composer.json create mode 100644 e2e-cli/e2e-config.json create mode 100644 e2e-cli/main.php create mode 100755 e2e-cli/run-e2e.sh diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml new file mode 100644 index 0000000..9dca93b --- /dev/null +++ b/.github/workflows/e2e-tests.yml @@ -0,0 +1,58 @@ +name: E2E Tests + +on: + push: + branches: [master] + pull_request: + branches: [master] + workflow_dispatch: + inputs: + e2e_tests_ref: + description: 'Branch or ref of sdk-e2e-tests to use' + required: false + default: 'main' + +jobs: + e2e-tests: + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: ubuntu-latest + + steps: + - name: Checkout SDK + uses: actions/checkout@v4 + with: + path: sdk + + - name: Checkout sdk-e2e-tests + uses: actions/checkout@v4 + with: + repository: segmentio/sdk-e2e-tests + ref: ${{ inputs.e2e_tests_ref || 'main' }} + token: ${{ secrets.E2E_TESTS_TOKEN }} + path: sdk-e2e-tests + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + extensions: curl, json + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Run E2E tests + working-directory: sdk-e2e-tests + run: | + ./scripts/run-tests.sh \ + --sdk-dir "${{ github.workspace }}/sdk/e2e-cli" \ + --cli "php ${{ github.workspace }}/sdk/e2e-cli/main.php" + + - name: Upload test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: e2e-test-results + path: sdk-e2e-tests/test-results/ + if-no-files-found: ignore diff --git a/.github/workflows/publish-e2e-cli.yml b/.github/workflows/publish-e2e-cli.yml new file mode 100644 index 0000000..ec647f0 --- /dev/null +++ b/.github/workflows/publish-e2e-cli.yml @@ -0,0 +1,39 @@ +name: Publish E2E CLI + +on: + push: + branches: [master] + paths: + - 'e2e-cli/**' + - 'lib/**' + schedule: + - cron: '0 0 1 * *' + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout SDK + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + extensions: curl, json + + - name: Prepare artifact + run: | + mkdir -p artifact + cp e2e-cli/main.php artifact/ + cp e2e-cli/e2e-config.json artifact/ + cp e2e-cli/run-e2e.sh artifact/ + cp -r lib artifact/lib + + - name: Upload CLI artifact + uses: actions/upload-artifact@v4 + with: + name: e2e-cli-php + path: artifact/ + retention-days: 90 diff --git a/e2e-cli/README.md b/e2e-cli/README.md new file mode 100644 index 0000000..4126b08 --- /dev/null +++ b/e2e-cli/README.md @@ -0,0 +1,131 @@ +# analytics-php e2e-cli + +A small CLI tool that drives the analytics-php SDK in end-to-end tests. + +## Requirements + +- PHP 7.4 or 8.x + +No `composer install` is required — `main.php` uses a simple `spl_autoload_register` +to load the SDK classes directly from `../lib/`. + +## Usage + +```bash +php main.php --input '' +``` + +### Input JSON format + +```json +{ + "writeKey": "YOUR_WRITE_KEY", + "apiHost": "https://api.segment.io", + "sequences": [ + { + "delayMs": 0, + "events": [ + { + "type": "track", + "event": "Test Event", + "userId": "user-1", + "properties": { "plan": "pro" } + }, + { + "type": "identify", + "userId": "user-1", + "traits": { "email": "user@example.com" } + }, + { + "type": "page", + "userId": "user-1", + "name": "Home", + "category": "Nav" + }, + { + "type": "screen", + "userId": "user-1", + "name": "Main Screen" + }, + { + "type": "alias", + "userId": "new-id", + "previousId": "old-id" + }, + { + "type": "group", + "userId": "user-1", + "groupId": "group-1", + "traits": { "name": "Acme Corp" } + } + ] + } + ], + "config": { + "flushAt": 15, + "timeout": 10 + } +} +``` + +| Field | Description | +|---|---| +| `writeKey` | Segment write key | +| `apiHost` | Full URL of the Segment API host (scheme is stripped; PHP SDK always uses HTTPS) | +| `sequences[].delayMs` | Milliseconds to wait before processing this sequence | +| `sequences[].events` | List of events to send | +| `config.flushAt` | Number of events to accumulate before auto-flushing (maps to `flush_at`) | +| `config.timeout` | cURL timeout in seconds (maps to `curl_timeout`) | + +### Supported event types + +`track`, `identify`, `page`, `screen`, `alias`, `group` + +### Output JSON (stdout) + +On success: + +```json +{"success": true, "sentBatches": 1} +``` + +On failure: + +```json +{"success": false, "sentBatches": 0, "error": "HTTP 400: Bad Request"} +``` + +Exit code is `0` on success and `1` on failure. Debug logs are written to **stderr**. + +## Running E2E tests + +```bash +./run-e2e.sh +``` + +By default this expects the `sdk-e2e-tests` repo to be checked out alongside +the `analytics-php` repo: + +``` +parent/ + analytics-php/ + e2e-cli/ ← you are here + sdk-e2e-tests/ +``` + +Override the location with an environment variable: + +```bash +E2E_TESTS_DIR=/path/to/sdk-e2e-tests ./run-e2e.sh +``` + +Any additional arguments are forwarded to `sdk-e2e-tests/scripts/run-tests.sh`. + +## How it works + +1. `main.php` parses `--input` JSON from `$argv`. +2. A `Segment\Client` is created with `lib_curl` consumer and the provided options. +3. Each sequence is processed in order; `delayMs` introduces an optional pause. +4. After all events are enqueued, `flush()` is called to send them synchronously. +5. Errors captured by the `error_handler` callback or a `false` return from + `flush()` cause the script to exit with code `1`. diff --git a/e2e-cli/composer.json b/e2e-cli/composer.json new file mode 100644 index 0000000..cea3b6f --- /dev/null +++ b/e2e-cli/composer.json @@ -0,0 +1,13 @@ +{ + "name": "segment/analytics-php-e2e-cli", + "description": "E2E testing CLI for Segment Analytics PHP", + "type": "project", + "require": { + "php": "^7.4 || ^8.0" + }, + "autoload": { + "psr-4": { + "Segment\\": "../lib/" + } + } +} diff --git a/e2e-cli/e2e-config.json b/e2e-cli/e2e-config.json new file mode 100644 index 0000000..071d5fc --- /dev/null +++ b/e2e-cli/e2e-config.json @@ -0,0 +1,7 @@ +{ + "sdk": "php", + "test_suites": "basic", + "auto_settings": false, + "patch": null, + "env": {} +} diff --git a/e2e-cli/main.php b/e2e-cli/main.php new file mode 100644 index 0000000..4695981 --- /dev/null +++ b/e2e-cli/main.php @@ -0,0 +1,319 @@ +' + * + * Input JSON format: + * { + * "writeKey": "...", + * "apiHost": "https://...", + * "sequences": [ + * { + * "delayMs": 0, + * "events": [ + * {"type": "track", "event": "Test", "userId": "user-1", "properties": {...}}, + * ... + * ] + * } + * ], + * "config": { + * "flushAt": 15, + * "timeout": 10 + * } + * } + * + * Output JSON to stdout: + * {"success": true, "sentBatches": 1} + * {"success": false, "sentBatches": 0, "error": "..."} + * + * Exit code: 0 on success, 1 on failure. + */ + +// Autoload the Segment SDK from the parent lib/ directory. +spl_autoload_register(function (string $class): void { + $prefix = 'Segment\\'; + $baseDir = __DIR__ . '/../lib/'; + + if (strncmp($prefix, $class, strlen($prefix)) !== 0) { + return; + } + + $relative = substr($class, strlen($prefix)); + $file = $baseDir . str_replace('\\', '/', $relative) . '.php'; + + if (file_exists($file)) { + require $file; + } +}); + +/** + * LibCurl subclass that allows overriding the protocol (http:// vs https://). + * The base class hardcodes $protocol = 'https://', so we extend it to support + * plain-HTTP targets used by the mock test server. + */ +class E2eLibCurl extends \Segment\Consumer\LibCurl +{ + public function __construct(string $secret, array $options = []) + { + parent::__construct($secret, $options); + if (isset($options['protocol'])) { + $this->protocol = $options['protocol']; + } + } +} + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function debugLog(string $msg): void +{ + fwrite(STDERR, '[e2e-cli] ' . $msg . PHP_EOL); +} + +function outputResult(bool $success, int $sentBatches, string $error = ''): void +{ + $result = [ + 'success' => $success, + 'sentBatches' => $sentBatches, + ]; + if (!$success && $error !== '') { + $result['error'] = $error; + } + echo json_encode($result) . PHP_EOL; +} + +/** + * Parse the --input argument from $argv. + * + * @param array $argv + * @return string|null + */ +function parseInputArg(array $argv): ?string +{ + for ($i = 1, $iMax = count($argv); $i < $iMax; $i++) { + if ($argv[$i] === '--input' && isset($argv[$i + 1])) { + return $argv[$i + 1]; + } + if (strncmp($argv[$i], '--input=', 8) === 0) { + return substr($argv[$i], 8); + } + } + return null; +} + +/** + * Given a full URL like "https://api.segment.io" or "http://localhost:8080", + * return just the host[:port] portion that the PHP SDK expects. + * + * The PHP SDK prepends "https://" itself (hardcoded in QueueConsumer), so we + * strip the scheme here and only keep host + optional port. + * + * @param string $apiHost + * @return string + */ +function parseHost(string $apiHost): string +{ + // Remove trailing slash + $apiHost = rtrim($apiHost, '/'); + + // Strip scheme + $apiHost = preg_replace('#^https?://#', '', $apiHost); + + // Remove any path component — keep only host[:port] + $parts = explode('/', $apiHost, 2); + + return $parts[0]; +} + +/** + * Build the options array for Segment\Client. + * + * @param array $input + * @param array &$errors collected error messages + * @return array + */ +function buildClientOptions(array $input, array &$errors): array +{ + $config = $input['config'] ?? []; + $apiHost = $input['apiHost'] ?? ''; + + // Determine protocol from the apiHost scheme (default https://). + $scheme = 'https://'; + if (preg_match('#^(https?)://#i', $apiHost, $m)) { + $scheme = strtolower($m[1]) . '://'; + } + + $options = [ + // Use our subclass so we can inject a plain-http:// protocol for the + // mock test server (the base LibCurl hardcodes https://). + 'consumer' => E2eLibCurl::class, + 'protocol' => $scheme, + 'error_handler' => function (int $code, string $message) use (&$errors): void { + $msg = "HTTP {$code}: {$message}"; + debugLog('SDK error — ' . $msg); + $errors[] = $msg; + }, + ]; + + if ($apiHost !== '') { + $options['host'] = parseHost($apiHost); + debugLog('Using host: ' . $options['host'] . ' (protocol: ' . $scheme . ')'); + } + + if (isset($config['flushAt']) && is_numeric($config['flushAt'])) { + $options['flush_at'] = (int)$config['flushAt']; + debugLog('flush_at: ' . $options['flush_at']); + } + + if (isset($config['timeout']) && is_numeric($config['timeout'])) { + $options['curl_timeout'] = (int)$config['timeout']; + debugLog('curl_timeout: ' . $options['curl_timeout']); + } + + return $options; +} + +/** + * Map an event array from the input JSON to the array accepted by the SDK. + * Only passes fields that are set in the input event. + * + * @param array $event + * @return array + */ +function buildMessage(array $event): array +{ + $fieldMap = [ + 'userId', + 'anonymousId', + 'messageId', + 'timestamp', + 'traits', + 'properties', + 'name', + 'category', + 'groupId', + 'previousId', + 'context', + 'integrations', + 'event', + ]; + + $message = []; + foreach ($fieldMap as $field) { + if (array_key_exists($field, $event)) { + $message[$field] = $event[$field]; + } + } + + return $message; +} + +// --------------------------------------------------------------------------- +// Main +// --------------------------------------------------------------------------- + +$inputJson = parseInputArg($argv); + +if ($inputJson === null) { + outputResult(false, 0, 'Missing required --input argument'); + exit(1); +} + +$input = json_decode($inputJson, true); + +if (!is_array($input)) { + outputResult(false, 0, 'Failed to parse --input JSON: ' . json_last_error_msg()); + exit(1); +} + +$writeKey = $input['writeKey'] ?? ''; +$sequences = $input['sequences'] ?? []; + +if ($writeKey === '') { + outputResult(false, 0, 'Missing writeKey in input'); + exit(1); +} + +$errors = []; + +// Build client options (error_handler captures into $errors by reference) +$options = buildClientOptions($input, $errors); + +debugLog('Creating Segment\\Client with writeKey=' . substr($writeKey, 0, 4) . '...'); + +$client = new \Segment\Client($writeKey, $options); + +$sentBatches = 0; + +foreach ($sequences as $seqIndex => $sequence) { + $delayMs = (int)($sequence['delayMs'] ?? 0); + + if ($delayMs > 0) { + debugLog("Sequence {$seqIndex}: sleeping {$delayMs}ms"); + usleep($delayMs * 1000); + } + + $events = $sequence['events'] ?? []; + debugLog("Sequence {$seqIndex}: processing " . count($events) . ' event(s)'); + + foreach ($events as $eventIndex => $event) { + $type = $event['type'] ?? ''; + $message = buildMessage($event); + + debugLog(" [{$seqIndex}/{$eventIndex}] Enqueueing {$type}"); + + switch ($type) { + case 'track': + $client->track($message); + break; + case 'identify': + $client->identify($message); + break; + case 'page': + $client->page($message); + break; + case 'screen': + $client->screen($message); + break; + case 'alias': + $client->alias($message); + break; + case 'group': + $client->group($message); + break; + default: + $errors[] = "Unknown event type: {$type}"; + debugLog(" Unknown event type: {$type}"); + break; + } + } +} + +debugLog('Flushing...'); +$flushOk = $client->flush(); + +if ($flushOk) { + $sentBatches = 1; + debugLog('Flush succeeded'); +} else { + debugLog('Flush returned false'); + $errors[] = 'Flush failed'; +} + +$hasErrors = !empty($errors); +$success = $flushOk && !$hasErrors; + +if ($success) { + outputResult(true, $sentBatches); + exit(0); +} else { + $errorMsg = implode('; ', $errors); + outputResult(false, $sentBatches, $errorMsg); + exit(1); +} diff --git a/e2e-cli/run-e2e.sh b/e2e-cli/run-e2e.sh new file mode 100755 index 0000000..43afd98 --- /dev/null +++ b/e2e-cli/run-e2e.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Run E2E tests for analytics-php +# +# Prerequisites: Node.js 18+ and PHP 7.4+ +# +# Usage: +# ./run-e2e.sh [extra args passed to run-tests.sh] +# +# Override sdk-e2e-tests location: +# E2E_TESTS_DIR=../my-e2e-tests ./run-e2e.sh +# + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SDK_ROOT="$SCRIPT_DIR/.." +E2E_DIR="${E2E_TESTS_DIR:-$SDK_ROOT/../sdk-e2e-tests}" + +echo "=== Building analytics-php e2e-cli ===" + +# Run tests +cd "$E2E_DIR" +./scripts/run-tests.sh \ + --sdk-dir "$SCRIPT_DIR" \ + --cli "php $SCRIPT_DIR/main.php" \ + "$@"