first commit

This commit is contained in:
2024-12-17 13:43:22 +01:00
commit 8e6cd8b410
21292 changed files with 3514826 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
composer.lock
vendor
composer.phar
test/analytics.log

View File

@@ -0,0 +1,13 @@
language: php
php:
- 5.6
- 5.5
- 5.4
- 5.3
- hhvm
matrix:
allow_failures:
- php: hhvm
script: phpunit --colors test

View File

@@ -0,0 +1,226 @@
1.5.2 / 2017-08-18
==================
* Always set default context.
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.
1.5.0 / 2017-03-03
==================
* 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)
1.4.2 / 2016-07-11
==================
* remove the extra -e from echo in makefile
1.4.1 / 2016-07-11
==================
* use a more portable shebang
1.4.0 / 2016-07-11
==================
* adding a simple wrapper CLI
* explicitly declare library version in global scope during creating new release to allow using library with custom autoload (composer for example)
1.3.0 / 2016-04-05
==================
* Introducing libcurl consumer
* Change Consumer to protected instead of private
1.2.7 / 2016-03-04
==================
* adding global
1.2.6 / 2016-03-04
==================
* fix version
1.2.5 / 2016-03-04
==================
* Adding release script, fixing version
* Pass back ->flush() result to allow caller code know if flushed successfully
1.2.4 / 2016-02-17
=============
* core: fix error name
* send: make send.php executable
* socket: adding fix for FIN packets from remote
1.2.3 / 2016-02-01
==================
* instead of using just is_int and is_float for checking timestamp, use filter_var since that can detect string ints and floats - if its not a string or float, consider it might be a ISO8601 or some other string, so use strtotime() to support other strings
1.2.1 / 2015-12-29
==================
* socket open error checking fix
* Fix batch size check before flushing tracking queue
* Fix bug in send.php
1.2.0 / 2015-04-27
==================
* removing outdated test
* enabling ssl by default
* socket: bump timeout to 5s
1.1.3 / 2015-03-03
==================
* formatTime: use is_* and fix to support floats
1.1.2 / 2015-03-03
==================
* send.php: fix error handling
* client: fix float timestamp handling
1.1.1 / 2015-02-11
==================
* Add updated PHP version requirement for @phpunit
* add .sentAt
1.1.0 / 2015-01-07
==================
* support microtime
* Update README.md
* drop the io
1.0.3 / 2014-10-14
==================
* fix: empty array for traits and properties
1.0.2 / 2014-09-29
==================
* fix: identify(), group() with empty traits
* suppressing logs generated when attempting to write to a reset socket [j0ew00ds]
* Added PHP 5.6, 5.5 and HHVM to travis.yml [Nyholm]
1.0.1 / 2014-09-16
==================
* fixing validation for Segment::page() calls
* updating send.php error message
* fix send.php to exit gracefully when there is no log file to process
1.0.0 / 2014-06-16
==================
* update to work with new spec
* add ./composer.phar validation test
* better send.php output
* add validation
* use strtotime in send.php and support php5.3
* rename Analytics to Segment
* add send.php to replace file_reader.py
* add new methods implementation and tests
* implement spec changes
* change tests to reflect spec changes
* test changes:
* Fix typo in composer.json
0.4.8 / 8-21-2013
=============
* adding fix for socket requests which might complete in multiple fwrites
0.4.7 / 5-28-2013
=============
* `chmod` the log file to 0777 so that the file_reader.py can read it
0.4.6 / 5-25-2013
=============
* Check for status existing on response thanks to [@gmoreira](https://github.om/gmoreira)
0.4.5 / 5-20-2013
=============
* Check for empty secret thanks to [@mustela](https://github.com/mustela).
0.4.3 / 5-1-2013
=============
* Make file_reader rename to a file in the same directory as the log file thanks to [@marshally](https://github.com/marshally)
0.4.2 / 4-26-2013
=============
* Fix for $written var on connection error thanks to [@gmoreira](https://github.com/gmoreira)
0.4.1 / 4-25-2013
=============
* Adding fix to file_reader alias
0.4.0 / 4-8-2013
=============
* Full Autoloading an PEAR naming by [Cethy](https://github.com/Cethy)
* Adding alias call
0.3.0 / 3-22-2013
=============
* Adding try-catch around fwrite cal
0.2.7 / 3-17-2013
=============
* Adding file_reader.py fix
0.2.6 / 3-15-2013
=============
* Rename analytics.php -> Analytics.php to allow autoloading by [Cethy](https://github.com/Cethy)
0.2.5 / 2-22-2013
=============
* Trailing whitespace/end php tags fix by [jimrubenstein](https://github.com/jimrubenstein)
0.2.4 / 2-19-2013
=============
* Support fwrite retry on closed socket.
0.2.3 / 2-12-2013
=============
* Adding check for count in properties and traits length.
0.2.2 / 2-11-2013
=============
* Adding default args for properties
0.2.1 / 2-1-2013
=============
* Enabling pfsockopen for persistent connections
* Making socket default
0.2.0 / 2-1-2013
=============
* Updating consumer class to use shared functions.
* Removed *fork* consumer, renamed *fork_queue* to *fork_curl*.
0.1.1 / 1-30-2013
=============
* Adding fork consumer
* Adding fork_queue consumer
* Setting fork_queue consumer to be the default.
0.1.0 / 1-29-2013
=============
Initial version

View File

@@ -0,0 +1,27 @@
install: vendor
vendor: composer.phar
@php ./composer.phar install
composer.phar:
@curl -sS https://getcomposer.org/installer | php
test: install
@vendor/bin/phpunit --colors test/
@php ./composer.phar validate
release:
@printf "releasing ${VERSION}..."
@printf '<?php\nglobal $$SEGMENT_VERSION;\n$$SEGMENT_VERSION = "%b";\n?>' ${VERSION} > ./lib/Segment/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}
clean:
rm -rf \
composer.phar \
vendor \
composer.lock
.PHONY: test release

View File

@@ -0,0 +1,39 @@
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)
## Documentation
Documentation is available at [segment.com/docs/sources/server/php](https://segment.com/docs/sources/server/php/)
## Releasing
Run `make release VERSION=<version>`. It should automatically tag and release in composer
## License
```
WWWWWW||WWWWWW
W W W||W W W
||
( OO )__________
/ | \
/o o| MIT \
\___/||_||__||_|| *
|| || || ||
_||_|| _||_||
(__|__|(__|__|
```
(The MIT License)
Copyright (c) 2014 Segment Inc. <friends@segment.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,115 @@
#!/usr/bin/env php
<?php
require_once(__DIR__ . '/../lib/Segment.php');
if (in_array('--help', $argv)) {
print(usage());
exit;
}
if (empty($_ENV['SEGMENT_WRITE_KEY'])) {
error('$SEGMENT_WRITE_KEY environment variable required');
}
date_default_timezone_set('UTC');
Segment::init($_ENV['SEGMENT_WRITE_KEY']);
$options = getopt('', [
'method:', // T I P G A
'event::', // x
'userId::', // x x x x x
'groupId::', // x
'previousId::', // x
'anonymousId::', // x x x x x
'properties::', // x x
'name::', // x
'traits::', // x x
'context::', // x x x x x
'timestamp::' // x x x x x
]);
switch ($options['method']) {
case 'track':
Segment::track(array(
'userId' => $options['userId'],
'anonymousId' => $options['anonymousId'],
'event' => $options['event'],
'properties' => parse_json($options['properties']),
'timestamp' => parse_timestamp($options['timestamp']),
'context' => parse_json($options['context'])
));
break;
case 'identify':
Segment::identify(array(
'userId' => $options['userId'],
'anonymousId' => $options['anonymousId'],
'traits' => parse_json($options['traits']),
'timestamp' => parse_timestamp($options['timestamp']),
'context' => parse_json($options['context'])
));
break;
case 'page':
Segment::page(array(
'userId' => $options['userId'],
'anonymousId' => $options['anonymousId'],
'name' => $options['name'],
'category' => $options['category'],
'properties' => parse_json($options['properties']),
'timestamp' => parse_timestamp($options['timestamp']),
'context' => parse_json($options['context'])
));
break;
case 'group':
Segment::identify(array(
'userId' => $options['userId'],
'anonymousId' => $options['anonymousId'],
'groupId' => $options['groupId'],
'traits' => parse_json($options['traits']),
'timestamp' => parse_timestamp($options['timestamp']),
'context' => parse_json($options['context'])
));
break;
case 'alias':
Segment::alias(array(
'userId' => $options['userId'],
'previousId' => $options['previousId']
));
break;
default:
error(usage());
break;
}
Segment::flush();
function usage() {
return "\n Usage: analytics --method <track|identify|page|group|alias> [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 parse_timestamp($input) {
if (empty($input)) {
return null;
}
return strtotime($input);
}

View File

@@ -0,0 +1,11 @@
<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@@ -0,0 +1,33 @@
{
"name": "segmentio/analytics-php",
"version": "1.5.2",
"description": "Segment Analytics PHP Library",
"keywords": [
"analytics",
"segmentio",
"segment",
"analytics.js"
],
"homepage": "https://segment.com/libraries/php",
"license": "MIT",
"authors": [
{
"name": "Segment.io <friends@segment.com>",
"homepage": "https://segment.com/"
}
],
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"autoload": {
"files": [
"lib/Segment.php"
]
},
"bin": [
"bin/analytics"
]
}

View File

@@ -0,0 +1,11 @@
<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@@ -0,0 +1,145 @@
<?php
if (!function_exists('json_encode')) {
throw new Exception('Segment needs the JSON PHP extension.');
}
require_once(dirname(__FILE__) . '/Segment/Client.php');
class Segment {
private static $client;
/**
* Initializes the default client to use. Uses the libcurl consumer by default.
* @param string $secret your project's secret key
* @param array $options passed straight to the client
*/
public static function init($secret, $options = array()) {
self::assert($secret, "Segment::init() requires secret");
self::$client = new Segment_Client($secret, $options);
}
/**
* Tracks a user action
*
* @param array $message
* @return boolean whether the track call succeeded
*/
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.
*
* @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");
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) {
self::checkClient();
$groupId = !empty($message["groupId"]);
$userId = !empty($message["userId"]);
self::assert($groupId && $userId, "Segment::group() expects userId and groupId");
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) {
self::checkClient();
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) {
self::checkClient();
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) {
self::checkClient();
$userId = !empty($message["userId"]);
$previousId = !empty($message["previousId"]);
self::assert($userId && $previousId, "Segment::alias() requires both userId and previousId");
return self::$client->alias($message);
}
/**
* Validate common properties.
*
* @param array $msg
* @param string $type
*/
public static function validate($msg, $type){
$userId = !empty($msg["userId"]);
$anonId = !empty($msg["anonymousId"]);
self::assert($userId || $anonId, "Segment::$type() requires userId or anonymousId");
}
/**
* Flush the client
*/
public static function flush(){
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);
}
}

View File

@@ -0,0 +1,225 @@
<?php
require_once(__DIR__ . '/Consumer.php');
require_once(__DIR__ . '/QueueConsumer.php');
require_once(__DIR__ . '/Consumer/File.php');
require_once(__DIR__ . '/Consumer/ForkCurl.php');
require_once(__DIR__ . '/Consumer/LibCurl.php');
require_once(__DIR__ . '/Consumer/Socket.php');
require_once(__DIR__ . '/Version.php');
class Segment_Client {
protected $consumer;
/**
* Create a new analytics object with your app's secret
* key
*
* @param string $secret
* @param array $options array of consumer options [optional]
* @param string Consumer constructor to use, libcurl by default.
*
*/
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];
$this->consumer = new $Consumer($secret, $options);
}
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) {
$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);
}
/**
* Tracks a screen view.
*
* @param [array] $message
* @return [boolean] whether the screen call succeeded
*/
public function screen(array $message) {
$message = $this->message($message, "properties");
$message["type"] = "screen";
return $this->consumer->screen($message);
}
/**
* 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";
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();
}
return true;
}
/**
* 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)`.
* 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 time $timestamp - time in seconds (time())
*/
private function formatTime($ts) {
// time()
if ($ts == null || !$ts) $ts = time();
if (filter_var($ts, FILTER_VALIDATE_INT) !== false) return date("c", (int) $ts);
// anything else try to strtotime the date.
if (filter_var($ts, FILTER_VALIDATE_FLOAT) === false) {
if (is_string($ts)) {
return date("c", strtotime($ts));
} else {
return date("c");
}
}
// fix for floatval casting in send.php
$parts = explode(".", (string)$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);
return date($fmt, (int)$sec);
}
/**
* Add common fields to the gvien `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"]);
$msg["messageId"] = self::messageId();
return $msg;
}
/**
* Generate a random messageId.
*
* https://gist.github.com/dahnielson/508447#file-uuid-php-L74
*
* @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));
}
/**
* Add the segment.io context to the request
* @return array additional context
*/
private function getDefaultContext () {
global $SEGMENT_VERSION;
return array(
"library" => array(
"name" => "analytics-php",
"version" => $SEGMENT_VERSION,
"consumer" => $this->consumer->getConsumer()
)
);
}
}

View File

@@ -0,0 +1,104 @@
<?php
abstract class Segment_Consumer {
protected $type = "Consumer";
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;
}
/**
* 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() {
return isset($this->options["debug"]) ? $this->options["debug"] : false;
}
/**
* 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() {
return isset($this->options["ssl"]) ? $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) {
if (isset($this->options['error_handler'])) {
$handler = $this->options['error_handler'];
$handler($code, $msg);
}
if ($this->debug()) {
error_log("[Analytics][" . $this->type . "] " . $msg);
}
}
}

View File

@@ -0,0 +1,116 @@
<?php
class Segment_Consumer_File extends Segment_Consumer {
private $file_handle;
protected $type = "File";
//define getter method for consumer type
public function getConsumer() {
return $this->type;
}
/**
* The file consumer writes track and identify calls to a file.
* @param string $secret
* @param array $options
* string "filename" - where to log the analytics calls
*/
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");
chmod($options["filename"], 0777);
} catch (Exception $e) {
$this->handleError($e->getCode(), $e->getMessage());
}
}
public function __destruct() {
if ($this->file_handle &&
get_resource_type($this->file_handle) != "Unknown") {
fclose($this->file_handle);
}
}
/**
* Tracks a user action
*
* @param array $message
* @return [boolean] whether the track call succeeded
*/
public function track(array $message) {
return $this->write($message);
}
/**
* Tags traits about the user.
*
* @param array $message
* @return [boolean] whether the identify call succeeded
*/
public function identify(array $message) {
return $this->write($message);
}
/**
* Tags traits about the group.
*
* @param array $message
* @return [boolean] whether the group call succeeded
*/
public function group(array $message) {
return $this->write($message);
}
/**
* Tracks a page view.
*
* @param array $message
* @return [boolean] whether the page call succeeded
*/
public function page(array $message) {
return $this->write($message);
}
/**
* Tracks a screen view.
*
* @param array $message
* @return [boolean] whether the screen call succeeded
*/
public function screen(array $message) {
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) {
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;
$content = json_encode($body);
$content.= "\n";
return fwrite($this->file_handle, $content) == strlen($content);
}
}

View File

@@ -0,0 +1,61 @@
<?php
class Segment_Consumer_ForkCurl extends Segment_QueueConsumer {
protected $type = "ForkCurl";
//define getter method for consumer type
public function getConsumer() {
return $this->type;
}
/**
* Creates a new queued fork consumer which queues fork and identify
* calls before adding them to
* @param string $secret
* @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
*/
public function __construct($secret, $options = array()) {
parent::__construct($secret, $options);
}
/**
* 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) {
$body = $this->payload($messages);
$payload = json_encode($body);
# Escape for shell usage.
$payload = escapeshellarg($payload);
$secret = $this->secret;
$protocol = $this->ssl() ? "https://" : "http://";
$host = "api.segment.io";
$path = "/v1/import";
$url = $protocol . $host . $path;
$cmd = "curl -u $secret: -X POST -H 'Content-Type: application/json'";
$cmd.= " -d " . $payload . " '" . $url . "'";
if (!$this->debug()) {
$cmd .= " > /dev/null 2>&1 &";
}
exec($cmd, $output, $exit);
if ($exit != 0) {
$this->handleError($exit, $output);
}
return $exit == 0;
}
}

View File

@@ -0,0 +1,83 @@
<?php
class Segment_Consumer_LibCurl extends Segment_QueueConsumer {
protected $type = "LibCurl";
//define getter method for consumer type
public function getConsumer() {
return $this->type;
}
/**
* Creates a new queued libcurl consumer
* @param string $secret
* @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
*/
public function __construct($secret, $options = array()) {
parent::__construct($secret, $options);
}
/**
* 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) {
$body = $this->payload($messages);
$payload = json_encode($body);
$secret = $this->secret;
$protocol = $this->ssl() ? "https://" : "http://";
$host = "api.segment.io";
$path = "/v1/import";
$url = $protocol . $host . $path;
// 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';
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
$httpResponse = $this->executePost($ch);
if ($httpResponse != 200) {
// log error
$this->handleError($ch, $httpResponse);
// try to post one more time
$secondHttpResponse = $this->executePost($ch);
$this->handleError($ch, $secondHttpResponse);
}
//close connection
curl_close($ch);
return $httpResponse;
}
public function executePost($ch) {
curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
return $httpCode;
}
}

View File

@@ -0,0 +1,186 @@
<?php
class Segment_Consumer_Socket extends Segment_QueueConsumer {
protected $type = "Socket";
private $socket_failed;
//define getter method for consumer type
public function getConsumer() {
return $this->type;
}
/**
* Creates a new socket consumer for dispatching async requests immediately
* @param string $secret
* @param array $options
* number "timeout" - the timeout for connecting
* function "error_handler" - function called back on errors.
* boolean "debug" - whether to use debug output, wait for response.
*/
public function __construct($secret, $options = array()) {
if (!isset($options["timeout"]))
$options["timeout"] = 5;
if (!isset($options["host"]))
$options["host"] = "api.segment.io";
parent::__construct($secret, $options);
}
public function flushBatch($batch) {
$socket = $this->createSocket();
if (!$socket)
return;
$payload = $this->payload($batch);
$payload = json_encode($payload);
$body = $this->createBody($this->options["host"], $payload);
return $this->makeRequest($socket, $body);
}
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;
return false;
}
}
/**
* 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
* @return boolean $success
*/
private function makeRequest($socket, $req, $retry = true) {
$bytes_written = 0;
$bytes_total = strlen($req);
$closed = false;
# Write the request
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;
}
}
# If the socket has been closed, attempt to retry a single time.
if ($closed) {
fclose($socket);
if ($retry) {
$socket = $this->createSocket();
if ($socket) return $this->makeRequest($socket, $req, false);
}
return false;
}
$success = true;
if ($this->debug()) {
$res = $this->parseResponse(fread($socket, 2048));
if ($res["status"] != "200") {
$this->handleError($res["status"], $res["message"]);
$success = false;
}
} else {
// we have to read from the socket to avoid getting into
// states where the socket doesn't properly re-open.
// as long as we keep the recv buffer empty, php will
// properly reconnect
stream_set_timeout($socket, 0, 50000);
fread($socket, 2048);
stream_set_timeout($socket, 5);
}
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/import 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";
$req.= "Content-length: " . strlen($content) . "\r\n";
$req.= "\r\n";
$req.= $content;
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) {
$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
);
}
}

View File

@@ -0,0 +1,11 @@
<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@@ -0,0 +1,154 @@
<?php
abstract class Segment_QueueConsumer extends Segment_Consumer {
protected $type = "QueueConsumer";
protected $queue;
protected $max_queue_size = 1000;
protected $batch_size = 100;
/**
* 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"]))
$this->batch_size = $options["batch_size"];
$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);
}
/**
* 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);
}
/**
* Adds an item to our queue.
* @param mixed $item
* @return boolean whether call has succeeded
*/
protected function enqueue($item) {
$count = count($this->queue);
if ($count > $this->max_queue_size) {
return false;
}
$count = array_push($this->queue, $item);
if ($count >= $this->batch_size) {
return $this->flush(); // return ->flush() result: true on success
}
return true;
}
/**
* Flushes our queue of messages by batching them to the server
*/
public function flush() {
$count = count($this->queue);
$success = true;
while($count > 0 && $success) {
$batch = array_splice($this->queue, 0, min($this->batch_size, $count));
$success = $this->flushBatch($batch);
$count = count($this->queue);
}
return $success;
}
/**
* Given a batch of messages the method returns
* a valid payload.
*
* @param {Array} batch
* @return {Array}
**/
protected function payload($batch){
return array(
"batch" => $batch,
"sentAt" => date("c"),
);
}
/**
* Flushes a batch of messages.
* @param [type] $batch [description]
* @return [type] [description]
*/
abstract function flushBatch($batch);
}

View File

@@ -0,0 +1,4 @@
<?php
global $SEGMENT_VERSION;
$SEGMENT_VERSION = "1.5.2";
?>

View File

@@ -0,0 +1,11 @@
<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@@ -0,0 +1,11 @@
<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;

View File

@@ -0,0 +1,106 @@
<?php
/**
* require client
*/
require_once(__DIR__ . "/lib/Segment.php");
/**
* Args
*/
$args = parse($argv);
/**
* Make sure both are set
*/
if (!isset($args["secret"])) die("--secret must be given");
if (!isset($args["file"])) die("--file must be given");
$file = $args["file"];
if ($file[0] != '/') $file = __DIR__ . "/" . $file;
/**
* Rename the file so we don't write the same calls
* multiple times
*/
$dir = dirname($file);
$old = $file;
$file = $dir . '/analytics-' . rand() . '.log';
if(!file_exists($old)) {
print("file: $old does not exist");
exit(0);
}
if (!rename($old, $file)) {
print("error renaming from $old to $file\n");
exit(1);
}
/**
* File contents.
*/
$contents = file_get_contents($file);
$lines = explode("\n", $contents);
/**
* Initialize the client.
*/
Segment::init($args["secret"], array(
"debug" => true,
"error_handler" => function($code, $msg){
print("$code: $msg\n");
exit(1);
}
));
/**
* Payloads
*/
$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();
}
Segment::flush();
unlink($file);
/**
* Sent
*/
print("sent $successful from $total requests successfully");
exit(0);
/**
* Parse arguments
*/
function parse($argv){
$ret = array();
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]);
}
return $ret;
}

View File

@@ -0,0 +1,208 @@
<?php
require_once(dirname(__FILE__) . "/../lib/Segment.php");
class AnalyticsTest extends PHPUnit_Framework_TestCase {
function setUp() {
date_default_timezone_set("UTC");
Segment::init("oq0vdlg7yi", array("debug" => true));
}
function testTrack() {
$this->assertTrue(Segment::track(array(
"userId" => "john",
"event" => "Module PHP Event"
)));
}
function testGroup(){
$this->assertTrue(Segment::group(array(
"groupId" => "group-id",
"userId" => "user-id",
"traits" => array(
"plan" => "startup"
)
)));
}
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/"
)
)));
}
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/"
)
)));
}
function testBasicPage(){
$this->assertTrue(Segment::page(array(
"anonymousId" => "anonymous-id"
)));
}
function testScreen(){
$this->assertTrue(Segment::screen(array(
"anonymousId" => "anonymous-id",
"name" => "2048",
"category" => "game built with php :)",
"properties" => array(
"points" => 300
)
)));
}
function testBasicScreen(){
$this->assertTrue(Segment::screen(array(
"anonymousId" => "anonymous-id"
)));
}
function testIdentify() {
$this->assertTrue(Segment::identify(array(
"userId" => "doe",
"traits" => array(
"loves_php" => false,
"birthday" => time()
)
)));
}
function testEmptyTraits() {
$this->assertTrue(Segment::identify(array(
"userId" => "empty-traits"
)));
$this->assertTrue(Segment::group(array(
"userId" => "empty-traits",
"groupId" => "empty-traits"
)));
}
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()
)));
}
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"
)));
}
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()
)));
}
function testAlias() {
$this->assertTrue(Segment::alias(array(
"previousId" => "previous-id",
"userId" => "user-id"
)));
}
function testContextEmpty() {
$this->assertTrue(Segment::track(array(
"userId" => "user-id",
"event" => "Context Test",
"context" => array()
)));
}
function testContextCustom() {
$this->assertTrue(Segment::track(array(
"userId" => "user-id",
"event" => "Context Test",
"context" => array(
"active" => false
)
)));
}
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'))) . '.'
)));
}
}
?>

View File

@@ -0,0 +1,122 @@
<?php
require_once(dirname(__FILE__) . "/../lib/Segment/Client.php");
class ConsumerFileTest extends PHPUnit_Framework_TestCase {
private $client;
private $filename = "/tmp/analytics.log";
function setUp() {
date_default_timezone_set("UTC");
if (file_exists($this->filename()))
unlink($this->filename());
$this->client = new Segment_Client("oq0vdlg7yi",
array("consumer" => "file",
"filename" => $this->filename));
}
function tearDown(){
if (file_exists($this->filename))
unlink($this->filename);
}
function testTrack() {
$this->assertTrue($this->client->track(array(
"userId" => "some-user",
"event" => "File PHP Event - Microtime",
"timestamp" => microtime(true),
)));
$this->checkWritten("track");
}
function testIdentify() {
$this->assertTrue($this->client->identify(array(
"userId" => "Calvin",
"traits" => array(
"loves_php" => false,
"type" => "analytics.log",
"birthday" => time()
)
)));
$this->checkWritten("identify");
}
function testGroup(){
$this->assertTrue($this->client->group(array(
"userId" => "user-id",
"groupId" => "group-id",
"traits" => array(
"type" => "consumer analytics.log test",
)
)));
}
function testPage(){
$this->assertTrue($this->client->page(array(
"userId" => "user-id",
"name" => "analytics-php",
"category" => "analytics.log",
"properties" => array(
"url" => "https://a.url/"
)
)));
}
function testScreen(){
$this->assertTrue($this->client->screen(array(
"userId" => "userId",
"name" => "grand theft auto",
"category" => "analytics.log",
"properties" => array()
)));
}
function testAlias () {
$this->assertTrue($this->client->alias(array(
"previousId" => "previous-id",
"userId" => "user-id"
)));
$this->checkWritten("alias");
}
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->assertEquals("sent 200 from 200 requests successfully", trim($output[0]));
$this->assertFalse(file_exists($this->filename()));
}
function testProductionProblems() {
# Open to a place where we should not have write access.
$client = new Segment_Client("oq0vdlg7yi",
array("consumer" => "file",
"filename" => "/dev/xxxxxxx" ));
$tracked = $client->track(array("userId" => "some-user", "event" => "my event"));
$this->assertFalse($tracked);
}
function checkWritten($type) {
exec("wc -l " . $this->filename, $output);
$out = trim($output[0]);
$this->assertEquals($out, "1 " . $this->filename);
$str = file_get_contents($this->filename);
$json = json_decode(trim($str));
$this->assertEquals($type, $json->type);
unlink($this->filename);
}
function filename(){
return '/tmp/analytics.log';
}
}
?>

View File

@@ -0,0 +1,72 @@
<?php
require_once(dirname(__FILE__) . "/../lib/Segment/Client.php");
class ConsumerForkCurlTest extends PHPUnit_Framework_TestCase {
private $client;
function setUp() {
date_default_timezone_set("UTC");
$this->client = new Segment_Client("oq0vdlg7yi",
array("consumer" => "fork_curl",
"debug" => true));
}
function testTrack() {
$this->assertTrue($this->client->track(array(
"userId" => "some-user",
"event" => "PHP Fork Curl'd\" Event"
)));
}
function testIdentify() {
$this->assertTrue($this->client->identify(array(
"userId" => "user-id",
"traits" => array(
"loves_php" => false,
"type" => "consumer fork-curl test",
"birthday" => time()
)
)));
}
function testGroup(){
$this->assertTrue($this->client->group(array(
"userId" => "user-id",
"groupId" => "group-id",
"traits" => array(
"type" => "consumer fork-curl test"
)
)));
}
function testPage(){
$this->assertTrue($this->client->page(array(
"userId" => "userId",
"name" => "analytics-php",
"category" => "fork-curl",
"properties" => array(
"url" => "https://a.url/"
)
)));
}
function testScreen(){
$this->assertTrue($this->client->page(array(
"anonymousId" => "anonymous-id",
"name" => "grand theft auto",
"category" => "fork-curl",
"properties" => array()
)));
}
function testAlias() {
$this->assertTrue($this->client->alias(array(
"previousId" => "previous-id",
"userId" => "user-id"
)));
}
}
?>

View File

@@ -0,0 +1,73 @@
<?php
require_once(dirname(__FILE__) . "/../lib/Segment/Client.php");
class ConsumerLibCurlTest extends PHPUnit_Framework_TestCase {
private $client;
function setUp() {
date_default_timezone_set("UTC");
$this->client = new Segment_Client("oq0vdlg7yi",
array("consumer" => "lib_curl",
"debug" => true));
}
function testTrack() {
$this->assertTrue($this->client->track(array(
"userId" => "lib-curl-track",
"event" => "PHP Lib Curl'd\" Event"
)));
}
function testIdentify() {
$this->assertTrue($this->client->identify(array(
"userId" => "lib-curl-identify",
"traits" => array(
"loves_php" => false,
"type" => "consumer lib-curl test",
"birthday" => time()
)
)));
}
function testGroup(){
$this->assertTrue($this->client->group(array(
"userId" => "lib-curl-group",
"groupId" => "group-id",
"traits" => array(
"type" => "consumer lib-curl test"
)
)));
}
function testPage(){
$this->assertTrue($this->client->page(array(
"userId" => "lib-curl-page",
"name" => "analytics-php",
"category" => "fork-curl",
"properties" => array(
"url" => "https://a.url/"
)
)));
}
function testScreen(){
$this->assertTrue($this->client->page(array(
"anonymousId" => "lib-curl-screen",
"name" => "grand theft auto",
"category" => "fork-curl",
"properties" => array()
)));
}
function testAlias() {
$this->assertTrue($this->client->alias(array(
"previousId" => "lib-curl-alias",
"userId" => "user-id"
)));
}
}
?>

View File

@@ -0,0 +1,154 @@
<?php
require_once(dirname(__FILE__) . "/../lib/Segment/Client.php");
class ConsumerSocketTest extends PHPUnit_Framework_TestCase {
private $client;
function setUp() {
date_default_timezone_set("UTC");
$this->client = new Segment_Client("oq0vdlg7yi",
array("consumer" => "socket"));
}
function testTrack() {
$this->assertTrue($this->client->track(array(
"userId" => "some-user",
"event" => "Socket PHP Event"
)));
}
function testIdentify() {
$this->assertTrue($this->client->identify(array(
"userId" => "Calvin",
"traits" => array(
"loves_php" => false,
"birthday" => time()
)
)));
}
function testGroup(){
$this->assertTrue($this->client->group(array(
"userId" => "user-id",
"groupId" => "group-id",
"traits" => array(
"type" => "consumer socket test"
)
)));
}
function testPage(){
$this->assertTrue($this->client->page(array(
"userId" => "user-id",
"name" => "analytics-php",
"category" => "socket",
"properties" => array(
"url" => "https://a.url/"
)
)));
}
function testScreen(){
$this->assertTrue($this->client->screen(array(
"anonymousId" => "anonymousId",
"name" => "grand theft auto",
"category" => "socket",
"properties" => array()
)));
}
function testAlias() {
$this->assertTrue($this->client->alias(array(
"previousId" => "some-socket",
"userId" => "new-socket"
)));
}
function testShortTimeout() {
$client = new Segment_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();
}
function testProductionProblems() {
$client = new Segment_Client("x", array(
"consumer" => "socket",
"error_handler" => function () { throw new Exception("Was called"); }));
# Shouldn't error out without debug on.
$client->track(array("user_id" => "some-user", "event" => "Production Problems"));
$client->__destruct();
}
function testDebugProblems() {
$options = array(
"debug" => true,
"consumer" => "socket",
"error_handler" => function ($errno, $errmsg) {
if ($errno != 400)
throw new Exception("Response is not 400"); }
);
$client = new Segment_Client("x", $options);
# Should error out with debug on.
$client->track(array("user_id" => "some-user", "event" => "Socket PHP Event"));
$client->__destruct();
}
function testLargeMessage () {
$options = array(
"debug" => true,
"consumer" => "socket"
);
$client = new Segment_Client("testsecret", $options);
$big_property = "";
for ($i = 0; $i < 10000; $i++) {
$big_property .= "a";
}
$this->assertTrue($client->track(array(
"userId" => "some-user",
"event" => "Super Large PHP Event",
"properties" => array("big_property" => $big_property)
)));
$client->__destruct();
}
/**
* @expectedException \RuntimeException
*/
function testConnectionError() {
$client = new Segment_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();
}
}
?>

View File

@@ -0,0 +1,6 @@
{"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"}

View File

@@ -0,0 +1,11 @@
<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header("Location: ../");
exit;