Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/SDK/Metrics/Util/TimerTrackerById.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Metrics\Util;

class TimerTrackerById
{
protected array $timers = [];

public function start(string|int $id): void
{
$this->timers[$id] = microtime(true);
}

public function durationMs(string|int $id): float
{
if (!isset($this->timers[$id])) {
return 0;
}

return (microtime(true) - $this->timers[$id]) * 1000;
}
}
29 changes: 29 additions & 0 deletions src/SDK/Metrics/Util/TimerTrackerByObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Metrics\Util;

class TimerTrackerByObject
{
protected \WeakMap $timers;

public function __construct()
{
$this->timers = new \WeakMap();
}

public function start(object $id): void
{
$this->timers[$id] = microtime(true);
}

public function durationMs(object $id): float
{
if ($this->timers->offsetExists($id) === false) {
return 0;
}

return (microtime(true) - $this->timers[$id]) * 1000;
}
}
55 changes: 55 additions & 0 deletions src/SDK/Util/AttributeTrackerById.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Util;

class AttributeTrackerById
{
protected array $attributes = [];

public function has(string|int $id): bool
{
return isset($this->attributes[$id]);
}

public function set(string|int $id, array $attributes): void
{
$this->attributes[$id] = $attributes;
}

public function add(string|int $id, array $attributes): array
{
if (!isset($this->attributes[$id])) {
return $this->attributes[$id] = $attributes;

}

return $this->attributes[$id] = [...$this->attributes[$id], ...$attributes];

Check failure on line 28 in src/SDK/Util/AttributeTrackerById.php

View workflow job for this annotation

GitHub Actions / php (8.2, false)

InvalidOperand

src/SDK/Util/AttributeTrackerById.php:28:45: InvalidOperand: Cannot use spread operator on non-iterable type mixed (see https://psalm.dev/058)

Check failure on line 28 in src/SDK/Util/AttributeTrackerById.php

View workflow job for this annotation

GitHub Actions / php (8.3, false)

InvalidOperand

src/SDK/Util/AttributeTrackerById.php:28:45: InvalidOperand: Cannot use spread operator on non-iterable type mixed (see https://psalm.dev/058)

Check failure on line 28 in src/SDK/Util/AttributeTrackerById.php

View workflow job for this annotation

GitHub Actions / php (8.5, true, --ignore-platform-reqs)

InvalidOperand

src/SDK/Util/AttributeTrackerById.php:28:45: InvalidOperand: Cannot use spread operator on non-iterable type mixed (see https://psalm.dev/058)

Check failure on line 28 in src/SDK/Util/AttributeTrackerById.php

View workflow job for this annotation

GitHub Actions / php (8.1, false)

InvalidOperand

src/SDK/Util/AttributeTrackerById.php:28:45: InvalidOperand: Cannot use spread operator on non-iterable type mixed (see https://psalm.dev/058)

Check failure on line 28 in src/SDK/Util/AttributeTrackerById.php

View workflow job for this annotation

GitHub Actions / php (8.4, false, --ignore-platform-reqs)

InvalidOperand

src/SDK/Util/AttributeTrackerById.php:28:45: InvalidOperand: Cannot use spread operator on non-iterable type mixed (see https://psalm.dev/058)
}

public function get(string|int $id): array
{
if (!isset($this->attributes[$id])) {
return [];
}

return $this->attributes[$id];
}

public function append(string|int $id, string|int $key, mixed $value): void
{
$this->attributes[$id] ??= [];
$this->attributes[$id][$key] = $value;
}

public function clear(string|int $id): void
{
unset($this->attributes[$id]);
}

public function reset(): void
{
$this->attributes = [];
}
}
61 changes: 61 additions & 0 deletions src/SDK/Util/AttributeTrackerByObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\SDK\Util;

class AttributeTrackerByObject
{
protected \WeakMap $attributes;

public function __construct()
{
$this->attributes = new \WeakMap();
}

public function has(object $id): bool
{
return $this->attributes->offsetExists($id);
}

public function set(object $id, array $attributes): void
{
$this->attributes[$id] = $attributes;
}

public function add(object $id, array $attributes): array
{
if ($this->attributes->offsetExists($id) === false) {
return $this->attributes[$id] = $attributes;

}

return $this->attributes[$id] = [...$this->attributes[$id], ...$attributes];

Check failure on line 33 in src/SDK/Util/AttributeTrackerByObject.php

View workflow job for this annotation

GitHub Actions / php (8.2, false)

InvalidOperand

src/SDK/Util/AttributeTrackerByObject.php:33:45: InvalidOperand: Cannot use spread operator on non-iterable type mixed (see https://psalm.dev/058)

Check failure on line 33 in src/SDK/Util/AttributeTrackerByObject.php

View workflow job for this annotation

GitHub Actions / php (8.3, false)

InvalidOperand

src/SDK/Util/AttributeTrackerByObject.php:33:45: InvalidOperand: Cannot use spread operator on non-iterable type mixed (see https://psalm.dev/058)

Check failure on line 33 in src/SDK/Util/AttributeTrackerByObject.php

View workflow job for this annotation

GitHub Actions / php (8.5, true, --ignore-platform-reqs)

InvalidOperand

src/SDK/Util/AttributeTrackerByObject.php:33:45: InvalidOperand: Cannot use spread operator on non-iterable type mixed (see https://psalm.dev/058)

Check failure on line 33 in src/SDK/Util/AttributeTrackerByObject.php

View workflow job for this annotation

GitHub Actions / php (8.1, false)

InvalidOperand

src/SDK/Util/AttributeTrackerByObject.php:33:45: InvalidOperand: Cannot use spread operator on non-iterable type mixed (see https://psalm.dev/058)

Check failure on line 33 in src/SDK/Util/AttributeTrackerByObject.php

View workflow job for this annotation

GitHub Actions / php (8.4, false, --ignore-platform-reqs)

InvalidOperand

src/SDK/Util/AttributeTrackerByObject.php:33:45: InvalidOperand: Cannot use spread operator on non-iterable type mixed (see https://psalm.dev/058)
}

public function get(object $id): array
{
if ($this->attributes->offsetExists($id) === false) {
return [];
}

return $this->attributes[$id];
}

public function append(object $id, string|int $key, mixed $value): void
{
$attributes = $this->attributes[$id] ?? [];
$attributes[$key] = $value;
$this->attributes[$id] = $attributes;
}

public function clear(object $id): void
{
unset($this->attributes[$id]);
}

public function reset(): void
{
$this->attributes = new \WeakMap();
}
}
71 changes: 71 additions & 0 deletions tests/Unit/SDK/Metrics/Util/TimerTrackerByIdTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Tests\Unit\SDK\Metrics\Util;

use OpenTelemetry\SDK\Metrics\Util\TimerTrackerById;
use PHPUnit\Framework\TestCase;

class TimerTrackerByIdTest extends TestCase
{
private TimerTrackerById $timerTracker;

protected function setUp(): void
{
$this->timerTracker = new TimerTrackerById();
}

public function test_start(): void
{
$id = 'test_id';
$this->timerTracker->start($id);

// Assert that the timer has started for the given ID.
// This might involve checking an internal state if available,
// or simply ensuring no error is thrown.
// For now, we'll assume a subsequent call to durationMs will indicate success.
$this->assertGreaterThan(0, $this->timerTracker->durationMs($id));
}

public function test_duration_ms(): void
{
$id = 'test_id_duration';
$this->timerTracker->start($id);

// Simulate some time passing
usleep(2000); // 2 millisecond

$duration = $this->timerTracker->durationMs($id);

// Assert that the duration is a positive number
$this->assertIsFloat($duration);
$this->assertGreaterThan(0, $duration);
}

public function test_duration_ms_for_non_existent_id(): void
{
$id = 'non_existent_id';
$duration = $this->timerTracker->durationMs($id);

// Assert that duration for a non-existent ID is 0 or null, depending on implementation
$this->assertEquals(0, $duration);
}

public function test_start_multiple_times_for_same_id(): void
{
$id = 'test_id_multiple';
$this->timerTracker->start($id);
usleep(2500);
$firstDuration = $this->timerTracker->durationMs($id);

$this->timerTracker->start($id); // Restart the timer
usleep(2000);
$secondDuration = $this->timerTracker->durationMs($id);

// Assert that the second duration is significantly smaller than the first,
// indicating the timer was reset.
$this->assertGreaterThan(0, $secondDuration);
$this->assertLessThan($firstDuration, $secondDuration); // Second duration should be smaller
}
}
99 changes: 99 additions & 0 deletions tests/Unit/SDK/Metrics/Util/TimerTrackerByObjectTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Tests\Unit\SDK\Metrics\Util;

use OpenTelemetry\SDK\Metrics\Util\TimerTrackerByObject;
use PHPUnit\Framework\TestCase;
use stdClass;

class TimerTrackerByObjectTest extends TestCase
{
private TimerTrackerByObject $timerTracker;

protected function setUp(): void
{
$this->timerTracker = new TimerTrackerByObject();
}

public function test_start(): void
{
$id = new stdClass();
$this->timerTracker->start($id);

// Assert that the timer has started for the given object.
// We can't directly inspect the WeakMap, but we can check if durationMs returns a non-zero value.
$this->assertGreaterThan(0, $this->timerTracker->durationMs($id));
}

public function test_duration_ms(): void
{
$id = new stdClass();
$this->timerTracker->start($id);

// Simulate some time passing
usleep(2000); // 2 millisecond

$duration = $this->timerTracker->durationMs($id);

// Assert that the duration is a positive number
$this->assertIsFloat($duration);
$this->assertGreaterThan(0, $duration);
}

public function test_duration_ms_for_non_existent_object(): void
{
$id = new stdClass();
$duration = $this->timerTracker->durationMs($id);

// Assert that duration for a non-existent ID is 0
$this->assertEquals(0, $duration);
}

public function test_start_multiple_times_for_same_object(): void
{
$id = new stdClass();
$this->timerTracker->start($id);
usleep(2500);
$firstDuration = $this->timerTracker->durationMs($id);

$this->timerTracker->start($id); // Restart the timer
usleep(1000);
$secondDuration = $this->timerTracker->durationMs($id);

// Assert that the second duration is significantly smaller than the first,
// indicating the timer was reset.
$this->assertGreaterThan(0, $secondDuration);
$this->assertLessThan($firstDuration, $secondDuration); // Second duration should be smaller
}

public function test_timer_removes_entry_when_object_is_garbage_collected(): void
{
$id = new stdClass();
$this->timerTracker->start($id);

// Ensure the timer is active for the object
$this->assertGreaterThan(0, $this->timerTracker->durationMs($id));

// Unset the reference to the object, allowing it to be garbage collected
unset($id);

// Although garbage collection is non-deterministic in PHP,
// for testing purposes, we can assume that if the WeakMap is working,
// eventually the entry will be removed.
// In a real-world scenario, we might need to force GC or use a more
// elaborate testing mechanism if this assertion fails intermittently.
// For now, we'll just check immediately.
$dummyObject = new stdClass();
$this->assertEquals(0, $this->timerTracker->durationMs($dummyObject));
// Note: It's hard to directly test if the original object's entry is gone
// without keeping a reference to it. The key here is that if a timer was
// started for an object that is no longer referenced, subsequent calls
// with *that specific object* (if it were somehow recreated with the same
// internal ID, which is unlikely) or any other object will correctly
// return 0 if that object was not started.
// A more direct test would involve an internal WeakMap check if possible,
// but that breaks encapsulation.
}
}
Loading
Loading