Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow integer log levels #3

Merged
merged 18 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from 17 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
17 changes: 3 additions & 14 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

- uses: shivammathur/setup-php@v2
with:
php-version: 7.4
php-version: 8.0
coverage: none
tools: composer:v2, cs2pr

Expand All @@ -34,17 +34,6 @@ jobs:
stable: [true]
coverage: [true]
composer-flags: ['', '--prefer-lowest']
include:
- php: '7.4'
psrlog: '^1.0'
stable: true
coverage: true
composer-flags: ''
- php: '7.4'
psrlog: '^1.0'
stable: true
coverage: true
composer-flags: '--prefer-lowest'

steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -80,7 +69,7 @@ jobs:

- uses: shivammathur/setup-php@v2
with:
php-version: 7.4
php-version: 8.0
coverage: none
tools: composer:v2

Expand All @@ -97,7 +86,7 @@ jobs:

- uses: shivammathur/setup-php@v2
with:
php-version: 7.4
php-version: 8.0
coverage: none
tools: composer:v2

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ PSR-3 compliant test logger based on psr/log v1's, but compatible with v2 and v3

## 📦 Installation

This project requires PHP 7.4 or higher. To install it via Composer simply run:
This project requires PHP 8.0 or higher. To install it via Composer simply run:

``` bash
$ composer require --dev colinodell/psr-testlogger
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"source": "https://github.com/colinodell/psr-testlogger"
},
"require": {
"php": "^7.4 || ^8.0",
"php": "^8.0",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to semver this means it should be version 2.0.0? 🤔

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

semver doesn't tie you to a particular PHP version support cycle, it's up to the project to determine what counts as a breaking API change and document it, and if PHP version increases don't count, then they don't.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, a bump in dependencies is not a BC break - see https://github.com/semver/semver/blob/d460299638d3d2af6fcf809285dc6a469e15742a/semver.md#what-should-i-do-if-i-update-my-own-dependencies-without-changing-the-public-api

The only reason we might need to release this as a new major version would be if we BC in the public API. Although the API signature of TestLogger did change, all of those changes are backward-compatible:

  • The class is final, so we don't need to worry about child classes suddenly becoming incompatible
  • The only type changes were parameter type widening. The new string|int types will continue to accept string inputs with no changes needed by end users.

I'm fairly sure this would make the Drupal maintainers feel uneasy because if this happens again at some point in the future, then it technically breaks backwards compatibility for Drupal

This shouldn't be an issue as Composer will prevent the installation of newer versions with incompatible dependencies.

For example, this change will be released as v1.2.0, so I imagine Drupal will use a constraint like ^1.2. If I were to release v1.3.0 in the future with a new minimum PHP version of 8.2.0, Composer would still allow v1.2.0 on PHP 8.0 and 8.1 - only users on 8.2 could install v1.3.0. And because it would be a minor version bump and we follow semver, there would be no breaking changes to the API. This means that both versions would function identically as far as Drupal is concerned.

Bumping dependencies (including minimum PHP versions) in a minor release is done by many other large PHP projects, including Doctrine: https://www.doctrine-project.org/2017/07/25/php-7.1-requirement-and-composer.html

TL;DR: Everything will "just work" :)

"psr/log": "^1.0 || ^2.0 || ^3.0"
},
"provide": {
Expand Down
33 changes: 13 additions & 20 deletions src/TestLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace ColinODell\PsrTestLogger;

use Psr\Log\AbstractLogger;
use Psr\Log\LogLevel;
use Psr\Log\InvalidArgumentException;

/**
* Used for testing purposes.
Expand Down Expand Up @@ -62,7 +62,7 @@ final class TestLogger extends AbstractLogger
/** @var array<int, array<string, mixed>> */
public array $records = [];

/** @var array<int|string, array<int, array<string, mixed>>> */
/** @var array<string|int, array<int, array<string, mixed>>> */
public array $recordsByLevel = [];

/**
Expand All @@ -72,6 +72,10 @@ final class TestLogger extends AbstractLogger
*/
public function log($level, $message, array $context = []): void
{
if (! (\is_string($level) || \is_int($level))) {
throw new InvalidArgumentException('Unsupported log level. The psr-testlogger library only supports string and integer log levels; passed ' . \print_r($level, true));
}

$record = [
'level' => $level,
'message' => $message,
Expand All @@ -82,19 +86,15 @@ public function log($level, $message, array $context = []): void
$this->records[] = $record;
}

/**
* @param LogLevel::* $level
*/
public function hasRecords(string $level): bool
public function hasRecords(string|int $level): bool
{
return isset($this->recordsByLevel[$level]);
}

/**
* @param string|array<string, mixed> $record
* @param LogLevel::* $level
*/
public function hasRecord($record, string $level): bool
public function hasRecord(string|array $record, string|int $level): bool
{
if (\is_string($record)) {
$record = ['message' => $record];
Expand All @@ -109,20 +109,14 @@ public function hasRecord($record, string $level): bool
}, $level);
}

/**
* @param LogLevel::* $level
*/
public function hasRecordThatContains(string $message, string $level): bool
public function hasRecordThatContains(string $message, string|int $level): bool
{
return $this->hasRecordThatPasses(static function (array $rec) use ($message) {
return \strpos($rec['message'], $message) !== false;
return \str_contains($rec['message'], $message);
}, $level);
}

/**
* @param LogLevel::* $level
*/
public function hasRecordThatMatches(string $regex, string $level): bool
public function hasRecordThatMatches(string $regex, string|int $level): bool
{
return $this->hasRecordThatPasses(static function ($rec) use ($regex) {
return \preg_match($regex, $rec['message']) > 0;
Expand All @@ -131,9 +125,8 @@ public function hasRecordThatMatches(string $regex, string $level): bool

/**
* @param callable(array<string, mixed>, int): bool $predicate
* @param LogLevel::* $level
*/
public function hasRecordThatPasses(callable $predicate, string $level): bool
public function hasRecordThatPasses(callable $predicate, string|int $level): bool
{
if (! isset($this->recordsByLevel[$level])) {
return false;
Expand Down Expand Up @@ -164,7 +157,7 @@ public function __call(string $method, array $args): bool
}
}

throw new \BadMethodCallException('Call to undefined method ' . static::class . '::' . $method . '()');
throw new \BadMethodCallException('Call to undefined method ' . self::class . '::' . $method . '()');
}

public function reset(): void
Expand Down
62 changes: 62 additions & 0 deletions tests/unit/TestLoggerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use ColinODell\PsrTestLogger\TestLogger;
use PHPUnit\Framework\TestCase;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;

final class TestLoggerTest extends TestCase
Expand Down Expand Up @@ -136,6 +137,67 @@ public function testCallMagicMethodThatDoesNotExist(): void
$logger->someMethodThatDoesNotExist(); // @phpstan-ignore-line
}

/**
* @dataProvider provideInvalidLogLevels
*/
public function testLogWithUnsupportedLevel(mixed $invalidLogLevel): void
{
$logger = new TestLogger();

$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupported log level');

$logger->log($invalidLogLevel, 'Test');
}

/**
* @return iterable<string, array{0: mixed}>
*/
public function provideInvalidLogLevels(): iterable
{
yield 'null' => [null];
yield 'bool' => [true];
yield 'float' => [1.0];
yield 'array' => [[]];
yield 'object' => [new \stdClass()];
yield 'resource' => [\fopen('php://memory', 'r')];
yield 'callable' => [static fn () => null];
}

public function testCustomLogLevels(): void
{
$recordsToLog = [
0 => 'Emergency',
1 => 'Alert',
2 => 'Critical',
3 => 'Error',
4 => 'Warning',
5 => 'Notice',
6 => 'Informational',
7 => 'Debug',
'super low priority' => 'Super Low Priority',
];

$logger = new TestLogger();

foreach ($recordsToLog as $level => $message) {
$logger->log($level, $message);
$this->assertTrue($logger->hasRecord($message, $level));
}

$this->assertCount(\count($recordsToLog), $logger->records);

// Custom log levels don't work with the magic methods
$this->assertFalse($logger->hasEmergencyRecords());
$this->assertFalse($logger->hasAlertRecords());
$this->assertFalse($logger->hasCriticalRecords());
$this->assertFalse($logger->hasErrorRecords());
$this->assertFalse($logger->hasWarningRecords());
$this->assertFalse($logger->hasNoticeRecords());
$this->assertFalse($logger->hasInfoRecords());
$this->assertFalse($logger->hasDebugRecords());
}

/**
* @return iterable<array<mixed>>
*/
Expand Down