diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..4b413bd
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,4 @@
+.git
+vendor
+.phpunit.result.cache
+.php-cs-fixer.cache
diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml
index abf53ba..8dee84e 100644
--- a/.github/workflows/code-quality.yml
+++ b/.github/workflows/code-quality.yml
@@ -6,9 +6,13 @@ on:
pull_request: ~
jobs:
- test-php-80:
- name: Code quality PHP 8.0
+ test:
+ name: Code quality PHP ${{ matrix.php-version }}
runs-on: [ubuntu-latest]
+ strategy:
+ fail-fast: false
+ matrix:
+ php-version: ['8.2.35', '8.3.36', '8.4.27', '8.5.12']
steps:
- name: Checkout
uses: actions/checkout@v2
@@ -16,10 +20,10 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
- php-version: 8.0
+ php-version: ${{ matrix.php-version }}
coverage: xdebug
- - name: Validate composer.json and composer.lock
+ - name: Validate Composer manifest
run: composer validate --strict
- name: Install dependencies
diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index fba9b86..cbaec1d 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -8,8 +8,8 @@
$config
->setRules([
'@Symfony' => true,
- '@PHP71Migration:risky' => true,
- '@PHPUnit60Migration:risky' => true,
+ '@PHP7x1Migration:risky' => true,
+ '@PHPUnit6x0Migration:risky' => true,
'array_indentation' => true,
'array_syntax' => ['syntax' => 'short'],
'blank_line_after_opening_tag' => true,
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..2e1375d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,10 @@
+FROM php:8.5.12-cli
+
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends libzip-dev unzip \
+ && docker-php-ext-install zip \
+ && rm -rf /var/lib/apt/lists/*
+
+COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
+
+WORKDIR /app
diff --git a/README.md b/README.md
index dcd0ce7..5dbcd96 100644
--- a/README.md
+++ b/README.md
@@ -77,3 +77,22 @@ Log::emergency('This is an emergency in the emergency channel', 'emergency');
## Contributing
Feel free to send your contributions as a PR. Make sure you update/write new tests to support your contribution.
Please follow PSR-2.
+
+### Development with Docker
+
+The development container uses PHP 8.5.12, the latest patched version listed in the PHP security advisory. Docker and Docker Compose are required. The provided Netskope CA bundle is mounted read-only for Composer's TLS connections; set `NETSKOPE_CA_BUNDLE` if it is stored elsewhere.
+
+```sh
+docker compose build
+docker compose run --rm php php --version
+docker compose run --rm php composer install
+docker compose run --rm php composer test
+```
+
+Run other Composer commands in the same container, for example:
+
+```sh
+docker compose run --rm php composer require vendor/package
+```
+
+The package requires PHP 8.2.35, 8.3.36, 8.4.27, or 8.5.12 (or a later patch release in those branches), so Composer will not install it on an official PHP release that predates the security fixes.
diff --git a/compose.yaml b/compose.yaml
new file mode 100644
index 0000000..3ab0a9a
--- /dev/null
+++ b/compose.yaml
@@ -0,0 +1,13 @@
+services:
+ php:
+ build: .
+ image: microlog-php:8.5.12
+ working_dir: /app
+ volumes:
+ - .:/app
+ - ${NETSKOPE_CA_BUNDLE:-/Users/lhbarrientos/netskope/netskope-cert-bundle.pem}:/run/certs/netskope-ca-bundle.pem:ro
+ environment:
+ COMPOSER_CACHE_DIR: /tmp/composer-cache
+ COMPOSER_CAFILE: /run/certs/netskope-ca-bundle.pem
+ CURL_CA_BUNDLE: /run/certs/netskope-ca-bundle.pem
+ SSL_CERT_FILE: /run/certs/netskope-ca-bundle.pem
diff --git a/composer.json b/composer.json
index d8ea3a6..af8811c 100644
--- a/composer.json
+++ b/composer.json
@@ -6,13 +6,13 @@
"homepage": "https://github.com/linioit/microlog",
"license": "BSD-3-Clause",
"require": {
- "php": "^7.4 || ^8.0",
+ "php": "~8.2.35 || ~8.3.36 || ~8.4.27 || ~8.5.12",
"psr/log": "^1.0.1 || ^2.0 || ^3.0"
},
"require-dev": {
- "phpunit/phpunit": "^8.5",
+ "phpunit/phpunit": "^11.5",
"friendsofphp/php-cs-fixer": "^3.6",
- "phpstan/phpstan": "^0.12"
+ "phpstan/phpstan": "^2.1"
},
"autoload": {
"psr-4": {
diff --git a/phpstan.neon b/phpstan.neon
index da5b10b..87c05eb 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -1,5 +1,6 @@
parameters:
level: max
- checkMissingIterableValueType: false
paths:
- src
+ ignoreErrors:
+ - identifier: missingType.iterableValue
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index bf26a6d..c1ff4be 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,22 +1,19 @@
-
+ requireCoverageMetadata="true"
+ beStrictAboutCoverageMetadata="true"
+ beStrictAboutOutputDuringTests="true">
./tests
-
-
+
+
./src
-
-
+
+
diff --git a/src/ThrowableProcessor.php b/src/ThrowableProcessor.php
index 7e6236b..de8b110 100644
--- a/src/ThrowableProcessor.php
+++ b/src/ThrowableProcessor.php
@@ -4,17 +4,20 @@
namespace Linio\Component\Microlog;
-use Throwable;
-
class ThrowableProcessor
{
+ /**
+ * @param array{message: mixed, context: array} $record
+ *
+ * @return array{message: mixed, context: array}
+ */
public function __invoke(array $record): array
{
- if (!$record['message'] instanceof Throwable) {
+ if (!$record['message'] instanceof \Throwable) {
return $record;
}
- /** @var Throwable $throwable */
+ /** @var \Throwable $throwable */
$throwable = $record['message'];
$record['message'] = $throwable->getMessage();
diff --git a/tests/LogTest.php b/tests/LogTest.php
index 951bb14..f497f5b 100644
--- a/tests/LogTest.php
+++ b/tests/LogTest.php
@@ -4,49 +4,46 @@
namespace Linio\Component\Microlog;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
-use ReflectionProperty;
+#[CoversClass(Log::class)]
class LogTest extends TestCase
{
protected function tearDown(): void
{
- $reflection = new ReflectionProperty(Log::class, 'loggers');
- $reflection->setAccessible(true);
+ $reflection = new \ReflectionProperty(Log::class, 'loggers');
$reflection->setValue(null, [Log::DEFAULT_CHANNEL => new NullLogger()]);
}
- /**
- * @dataProvider logLevelProvider
- */
+ #[DataProvider('logLevelProvider')]
public function testItLogsMessages(string $level): void
{
$message = 'this is a test';
$context = ['test' => 'test context'];
- $logger = $this->prophesize(LoggerInterface::class);
- $logger->log($level, $message, $context)->shouldBeCalled();
+ $logger = $this->createMock(LoggerInterface::class);
+ $logger->expects($this->once())->method('log')->with($level, $message, $context);
- Log::setLoggerForChannel($logger->reveal(), Log::DEFAULT_CHANNEL);
+ Log::setLoggerForChannel($logger, Log::DEFAULT_CHANNEL);
Log::{$level}($message, $context);
}
- /**
- * @dataProvider logLevelProvider
- */
+ #[DataProvider('logLevelProvider')]
public function testItLogsMessagesInANonDefaultChannel(string $level): void
{
$message = 'this is a test';
$channel = 'testChannel';
$context = ['test' => 'test context'];
- $logger = $this->prophesize(LoggerInterface::class);
- $logger->log($level, $message, $context)->shouldBeCalled();
+ $logger = $this->createMock(LoggerInterface::class);
+ $logger->expects($this->once())->method('log')->with($level, $message, $context);
- Log::setLoggerForChannel($logger->reveal(), $channel);
+ Log::setLoggerForChannel($logger, $channel);
Log::{$level}($message, $context, $channel);
}
@@ -56,15 +53,15 @@ public function testItLogsWithAnArbitraryLevel(): void
$message = 'this is a test';
$level = 'emergency';
- $logger = $this->prophesize(LoggerInterface::class);
- $logger->log($level, $message, [])->shouldBeCalled();
+ $logger = $this->createMock(LoggerInterface::class);
+ $logger->expects($this->once())->method('log')->with($level, $message, []);
- Log::setLoggerForChannel($logger->reveal(), Log::DEFAULT_CHANNEL);
+ Log::setLoggerForChannel($logger, Log::DEFAULT_CHANNEL);
Log::log($level, $message);
}
- public function logLevelProvider(): array
+ public static function logLevelProvider(): array
{
return [
['debug'],
diff --git a/tests/ThrowableProcessorTest.php b/tests/ThrowableProcessorTest.php
index 095289d..b7167d2 100644
--- a/tests/ThrowableProcessorTest.php
+++ b/tests/ThrowableProcessorTest.php
@@ -4,10 +4,10 @@
namespace Linio\Component\Microlog;
-use Exception;
+use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
-use Throwable;
+#[CoversClass(ThrowableProcessor::class)]
class ThrowableProcessorTest extends TestCase
{
public function testItDoesNotProcessMessagesThatAreNotThrowables(): void
@@ -26,7 +26,7 @@ public function testItDoesNotProcessMessagesThatAreNotThrowables(): void
public function testItProcessesMessagesThatAreThrowables(): void
{
$record = [
- 'message' => new Exception('Test Message'),
+ 'message' => new \Exception('Test Message'),
'context' => [],
];
@@ -34,6 +34,6 @@ public function testItProcessesMessagesThatAreThrowables(): void
$actual = $processor($record);
$this->assertSame('Test Message', $actual['message']);
- $this->assertInstanceOf(Throwable::class, $actual['context']['exception']);
+ $this->assertInstanceOf(\Throwable::class, $actual['context']['exception']);
}
}