Skip to content

Commit

Permalink
Merge pull request #15 from telebugs/composer-root-filter
Browse files Browse the repository at this point in the history
Add ComposerRootFilter
  • Loading branch information
kyrylo authored Jul 26, 2024
2 parents 20c03e1 + 593f093 commit c7b2a9a
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 6 deletions.
2 changes: 2 additions & 0 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Telebugs;

use Telebugs\MiddlewareStack;
use Telebugs\Middleware\ComposerRootFilter;
use Telebugs\Middleware\RootDirectoryFilter;

class Config
Expand Down Expand Up @@ -51,6 +52,7 @@ public function reset(): void
}

$this->middlewareStack = new MiddlewareStack();
$this->middlewareStack->use(new ComposerRootFilter());
$this->middlewareStack->use(new RootDirectoryFilter($this->rootDirectory));
}

Expand Down
124 changes: 124 additions & 0 deletions src/Middleware/ComposerRootFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

declare(strict_types=1);

namespace Telebugs\Middleware;

use Telebugs\BaseMiddleware;

class ComposerRootFilter extends BaseMiddleware
{
private ?string $composerDir = null;
private array $packageVersions = [];
private string $vendorPathPattern;

public function __construct()
{
if (!class_exists('\Composer\Autoload\ClassLoader', false)) {
return;
}

$reflection = new \ReflectionClass(\Composer\Autoload\ClassLoader::class);
$fileName = $reflection->getFileName();
if ($fileName === false) {
return;
}

// Go up two levels from autoload.php
$this->setComposerDir(dirname($fileName, 2));
}

public function setComposerDir(string $composerDir): void
{
$this->composerDir = $composerDir;
$this->packageVersions = $this->readPackageVersions();
$this->vendorPathPattern = '/^' . preg_quote($this->composerDir, '/') . '\/([^\/]+\/[^\/]+)\/(.+)$/';
}

public function __invoke($report): void
{
if (empty($this->packageVersions) || $this->composerDir === null) {
return;
}

foreach ($report->data['errors'] as &$error) {
$this->processBacktrace($error['backtrace']);
}
}

public function getWeight(): int
{
return -999;
}

public function readPackageVersions(): array
{
if ($this->composerDir === null) {
return [];
}

$installedJsonPath = $this->composerDir . '/composer/installed.json';

if (!file_exists($installedJsonPath)) {
return [];
}

$jsonContent = file_get_contents($installedJsonPath);
if ($jsonContent === false) {
return [];
}
$installedData = json_decode($jsonContent, true);
if (!is_array($installedData)) {
return [];
}

$packageVersions = [];

// Check if the structure is Composer 2.x (with 'packages' key) or 1.x
$packages = isset($installedData['packages']) && is_array($installedData['packages'])
? $installedData['packages']
: $installedData;

foreach ($packages as $package) {
if (isset($package['name']) && isset($package['version'])) {
$packageVersions[$package['name']] = $package['version'];
}
}

return $packageVersions;
}

private function processBacktrace(array &$backtrace): void
{
foreach ($backtrace as &$frame) {
if (!isset($frame['file'])) {
continue;
}

$this->processFrame($frame);
}
}

private function processFrame(array &$frame): void
{
if (!preg_match($this->vendorPathPattern, $frame['file'], $matches)) {
return;
}

$packageName = $matches[1];
$filePath = $matches[2];

if (!isset($this->packageVersions[$packageName])) {
return;
}

$version = $this->packageVersions[$packageName];

$frame['file'] = $this->formatFile($packageName, $version, $filePath);
}

private function formatFile(string $packageName, string $version, string $filePath): string
{
return sprintf('%s (%s) %s', $packageName, $version, $filePath);
}
}
3 changes: 3 additions & 0 deletions src/Report.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ private function attachCode(\Throwable $e, array $backtrace): array
if (!$this->frameBelongsToRootDirectory($frame['file'])) {
continue;
}
if (preg_match('/vendor\/bundle/', $frame['file'])) {
continue;
}
if (!is_readable($frame['file'])) {
continue;
}
Expand Down
5 changes: 1 addition & 4 deletions src/Sender.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,9 @@ class Sender

private Config $config;

private string $authorization;

public function __construct()
{
$this->config = Config::getInstance();
$this->authorization = 'Bearer ' . $this->config->getApiKey();
}

public function send(Report $report): Promise
Expand All @@ -31,7 +28,7 @@ public function send(Report $report): Promise
'headers' => [
'Content-Type' => self::CONTENT_TYPE,
'User-Agent' => self::USER_AGENT,
'Authorization' => $this->authorization
'Authorization' => 'Bearer ' . $this->config->getApiKey()
]
]);

Expand Down
4 changes: 2 additions & 2 deletions tests/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public function testSetHttpClient(): void
public function testMiddleware(): void
{
$this->config->middleware()->use(new TestIgnoreMiddleware());
$this->assertEquals(2, count($this->config->middleware()->getMiddlewares()));
$this->assertEquals(3, count($this->config->middleware()->getMiddlewares()));
}

public function testReset(): void
Expand All @@ -84,6 +84,6 @@ public function testReset(): void
$this->assertEquals("", $this->config->getApiKey());
$this->assertEquals("https://api.telebugs.com/2024-03-28/errors", $this->config->getApiURL());
$this->assertEquals(rtrim(rtrim(__DIR__, '/tests'), '\tests') . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'composer', $this->config->getRootDirectory());
$this->assertEquals(1, count($this->config->middleware()->getMiddlewares()));
$this->assertEquals(2, count($this->config->middleware()->getMiddlewares()));
}
}
41 changes: 41 additions & 0 deletions tests/Middleware/ComposerRootFilterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Telebugs\Tests;

use PHPUnit\Framework\TestCase;
use Telebugs\Middleware\ComposerRootFilter;
use Telebugs\Report;

class ComposerRootFilterTest extends TestCase
{
public function testComposerRootFilter()
{
$backtrace = [
[
'file' => getcwd() . '/tests/fixtures/vendor/telebugs/telebugs/src/foo.php',
'line' => 10,
],
[
'file' => getcwd() . '/tests/fixtures/vendor/telebugs/telebugs/src/bar.php',
'line' => 20,
],
];

$report = $this->createMock(Report::class);
$report->data = ['errors' => [['backtrace' => $backtrace]]];

$middleware = new ComposerRootFilter();
$middleware->setComposerDir(getcwd() . '/tests/fixtures/vendor');
$middleware->readPackageVersions();
$middleware($report);

$expectedBacktrace = [
['file' => 'telebugs/telebugs (v1.2.3) src/foo.php', 'line' => 10],
['file' => 'telebugs/telebugs (v1.2.3) src/bar.php', 'line' => 20],
];

$this->assertEquals($expectedBacktrace, $report->data['errors'][0]['backtrace']);
}
}
8 changes: 8 additions & 0 deletions tests/fixtures/vendor/composer/installed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"packages": [
{
"name": "telebugs/telebugs",
"version": "v1.2.3"
}
]
}

0 comments on commit c7b2a9a

Please sign in to comment.