Skip to content
This repository was archived by the owner on Dec 22, 2025. It is now read-only.

Commit 5ec78ce

Browse files
committed
Initial Version with Log and NoOp clients implemented
1 parent 8c4b32c commit 5ec78ce

27 files changed

+1219
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
vendor/*
2+
composer.lock

.php_cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
$finder = PhpCsFixer\Finder::create()
4+
->exclude('vendor')
5+
->in(__DIR__);
6+
7+
return PhpCsFixer\Config::create()
8+
->setRiskyAllowed(true)
9+
->setRules([
10+
'@PSR2' => true,
11+
'array_syntax' => ['syntax' => 'short'],
12+
13+
// additional symfony fixers
14+
'blank_line_after_namespace' => true,
15+
'blank_line_before_return' => true,
16+
'concat_space' => ['spacing' => 'one'],
17+
'function_typehint_space' => true,
18+
'hash_to_slash_comment' => true,
19+
'include' => true,
20+
'lowercase_cast' => true,
21+
'new_with_braces' => true,
22+
'no_blank_lines_before_namespace' => true,
23+
'no_empty_statement' => true,
24+
'no_extra_consecutive_blank_lines' => ['use'],
25+
'no_leading_import_slash' => true,
26+
'no_unused_imports' => true,
27+
'no_whitespace_in_blank_line' => true,
28+
'object_operator_without_whitespace' => true,
29+
'phpdoc_align' => true,
30+
'phpdoc_scalar' => true,
31+
'phpdoc_types' => true,
32+
'short_scalar_cast' => true,
33+
'single_blank_line_at_eof' => true,
34+
'single_quote' => true,
35+
'trailing_comma_in_multiline_array' => true,
36+
// additional contrib fixers
37+
'ordered_imports' => true,
38+
'phpdoc_order' => true,
39+
])
40+
->setFinder($finder);

composer.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "hellofresh/stats-php",
3+
"authors": [
4+
{
5+
"name": "Vladimir Garvardt",
6+
"email": "[email protected]"
7+
}
8+
],
9+
"autoload": {
10+
"psr-4": {
11+
"HelloFresh\\Stats\\": "src/"
12+
}
13+
},
14+
"require": {
15+
"php": ">= 5.6",
16+
"league/statsd": "^1.4",
17+
"psr/http-message": "^1.0",
18+
"behat/transliterator": "^1.2"
19+
},
20+
"require-dev": {
21+
"phpunit/phpunit": "^4.8.0",
22+
"friendsofphp/php-cs-fixer": "^2.3"
23+
}
24+
}

docker-compose.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: '3'
2+
services:
3+
app:
4+
image: quay.io/hellofresh/php56
5+
volumes:
6+
- ".:/app"

phpunit.xml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit backupGlobals="false"
3+
backupStaticAttributes="false"
4+
bootstrap="tests/bootstrap.php"
5+
colors="true"
6+
convertErrorsToExceptions="true"
7+
convertNoticesToExceptions="true"
8+
convertWarningsToExceptions="true"
9+
processIsolation="true"
10+
stopOnFailure="false"
11+
syntaxCheck="false"
12+
verbose="true"
13+
>
14+
<logging>
15+
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
16+
</logging>
17+
18+
<testsuites>
19+
<testsuite name="main">
20+
<directory>./tests/</directory>
21+
</testsuite>
22+
</testsuites>
23+
24+
<filter>
25+
<whitelist>
26+
<directory>./src</directory>
27+
</whitelist>
28+
</filter>
29+
</phpunit>

src/Bucket.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace HelloFresh\Stats;
4+
5+
6+
interface Bucket
7+
{
8+
const TOTAL_BUCKET = 'total';
9+
10+
const SUFFIX_STATUS_OK = 'ok';
11+
const SUFFIX_STATUS_FAIL = 'fail';
12+
13+
const PREFIX_UNICODE = '-u-';
14+
15+
const METRIC_EMPTY_PLACEHOLDER = '-';
16+
const METRIC_ID_PLACEHOLDER = '-id-';
17+
18+
const DEFAULT_HTTP_REQUEST_SECTION = 'request';
19+
20+
/**
21+
* @return string
22+
*/
23+
public function metric();
24+
25+
/**
26+
* @return string
27+
*/
28+
public function metricWithSuffix();
29+
30+
/**
31+
* @return string
32+
*/
33+
public function metricTotal();
34+
35+
/**
36+
* @return string
37+
*/
38+
public function metricTotalWithSuffix();
39+
}

src/Bucket/HTTPRequest.php

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
3+
namespace HelloFresh\Stats\Bucket;
4+
5+
6+
use HelloFresh\Stats\HTTPMetricAlterCallback;
7+
use Psr\Http\Message\RequestInterface;
8+
9+
class HTTPRequest extends Plain
10+
{
11+
/** @var RequestInterface */
12+
protected $request;
13+
/** @var HTTPMetricAlterCallback */
14+
protected $httpMetricAlterCallback;
15+
16+
/**
17+
* HTTPRequest constructor.
18+
*
19+
* @param string $section
20+
* @param RequestInterface $request
21+
* @param bool $success
22+
* @param HTTPMetricAlterCallback|null $callback
23+
*/
24+
public function __construct($section, RequestInterface $request, $success, HTTPMetricAlterCallback $callback = null)
25+
{
26+
$this->request = $request;
27+
$this->httpMetricAlterCallback = $callback;
28+
29+
parent::__construct($section, $this->buildMetricOperation(), $success);
30+
}
31+
32+
/**
33+
* @return MetricOperation
34+
*/
35+
public function buildMetricOperation()
36+
{
37+
$operation = new MetricOperation(strtolower($this->request->getMethod()));
38+
if ($this->request->getUri()->getPath() != '/') {
39+
$partsFilled = 1;
40+
foreach (explode('/', $this->request->getUri()->getPath()) as $fragment) {
41+
if ($fragment == '') {
42+
continue;
43+
}
44+
45+
$operation[$partsFilled] = $fragment;
46+
$partsFilled++;
47+
if ($partsFilled >= $operation::LENGTH) {
48+
break;
49+
}
50+
}
51+
}
52+
53+
if (null != $this->httpMetricAlterCallback) {
54+
$operation = call_user_func_array($this->httpMetricAlterCallback, [$operation, $this->request]);
55+
}
56+
57+
return $operation;
58+
}
59+
}

src/Bucket/MetricOperation.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace HelloFresh\Stats\Bucket;
4+
5+
6+
use HelloFresh\Stats\Bucket;
7+
8+
class MetricOperation extends \SplFixedArray
9+
{
10+
const LENGTH = 3;
11+
12+
/**
13+
* MetricOperation constructor.
14+
*
15+
* @param array $operations
16+
*/
17+
public function __construct(array $operations = [])
18+
{
19+
parent::__construct(static::LENGTH);
20+
21+
// ensure that operations is not bigger than allowed
22+
array_splice($operations, static::LENGTH);
23+
24+
foreach ($operations as $key => $value) {
25+
$this->offsetSet($key, $value);
26+
}
27+
28+
$opLength = count($operations);
29+
for ($i = $opLength; $i < static::LENGTH; $i++) {
30+
$this->offsetSet($i, Bucket::METRIC_EMPTY_PLACEHOLDER);
31+
}
32+
}
33+
}

src/Bucket/Plain.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
namespace HelloFresh\Stats\Bucket;
4+
5+
6+
use Behat\Transliterator\Transliterator;
7+
use HelloFresh\Stats\Bucket;
8+
9+
class Plain implements Bucket
10+
{
11+
/** @var string */
12+
protected $section;
13+
/** @var string */
14+
protected $operation;
15+
/** @var bool */
16+
protected $success;
17+
18+
protected static $status = [true => Bucket::SUFFIX_STATUS_OK, false => Bucket::SUFFIX_STATUS_FAIL];
19+
20+
/**
21+
* Plain constructor.
22+
*
23+
* @param string $section
24+
* @param MetricOperation $operation
25+
* @param bool $success
26+
*/
27+
public function __construct($section, MetricOperation $operation, $success)
28+
{
29+
$this->section = static::sanitizeMetricName($section);
30+
$this->success = $success;
31+
32+
$operationSanitized = [];
33+
foreach ($operation as $op) {
34+
$operationSanitized[] = static::sanitizeMetricName($op);
35+
}
36+
$this->operation = implode('.', $operationSanitized);
37+
}
38+
39+
/**
40+
* @inheritdoc
41+
*/
42+
public function metric()
43+
{
44+
return sprintf('%s.%s', $this->section, $this->operation);
45+
}
46+
47+
/**
48+
* @inheritdoc
49+
*/
50+
public function metricWithSuffix()
51+
{
52+
return sprintf('%s-%s.%s', $this->section, static::$status[$this->success], $this->operation);
53+
}
54+
55+
/**
56+
* @inheritdoc
57+
*/
58+
public function metricTotal()
59+
{
60+
return sprintf('%s.%s', Bucket::TOTAL_BUCKET, $this->section);
61+
}
62+
63+
/**
64+
* @inheritdoc
65+
*/
66+
public function metricTotalWithSuffix()
67+
{
68+
return sprintf('%s.%s-%s', Bucket::TOTAL_BUCKET, $this->section, static::$status[$this->success]);
69+
}
70+
71+
/**
72+
* @param string $metric
73+
*
74+
* @return string
75+
*/
76+
public static function sanitizeMetricName($metric)
77+
{
78+
if ($metric == "") {
79+
return Bucket::METRIC_EMPTY_PLACEHOLDER;
80+
}
81+
82+
// convert unicode symbols to ASCII
83+
$asciiMetric = Transliterator::utf8ToAscii($metric);
84+
if ($asciiMetric != $metric) {
85+
$metric = Bucket::PREFIX_UNICODE . $asciiMetric;
86+
}
87+
88+
// replace underscores with double underscores
89+
// and dots with single underscore
90+
return str_replace(
91+
'.',
92+
'_',
93+
str_replace('_', '__', $metric)
94+
);
95+
}
96+
}

0 commit comments

Comments
 (0)