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

Extend compatibility with newer version of psr/log:"^1.1.2|^2.0|^3.0" #50

Closed
wants to merge 3 commits into from
Closed
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
/vendor/
.phpunit.result.cache
.phpcs-cache
.idea
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"php": "~8.0.0 || ~8.1.0 || ~8.2.0",
"laminas/laminas-servicemanager": "^3.3.0",
"laminas/laminas-stdlib": "^3.0",
"psr/log": "^1.1.2"
"psr/log": "^1.1.2|^2.0|^3.0"
},
"require-dev": {
"ext-dom": "*",
Expand Down
20 changes: 10 additions & 10 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions docs/book/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# This Is Only a Placeholder

The content of this page can be found under:

https://github.com/laminas/documentation-theme/blob/master/theme/pages/installation.html
4 changes: 4 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ site_dir: docs/html
nav:
- Home: index.md
- Introduction: intro.md
- Installation: installation.md
- Reference:
- Writers: writers.md
- Filters: filters.md
Expand All @@ -15,3 +16,6 @@ site_description: 'Robust, composite logger with filtering, formatting, and PSR-
repo_url: 'https://github.com/laminas/laminas-log'
extra:
project: Components
installation:
config_provider_class: 'Laminas\Log\ConfigProvider'
module_class: 'Laminas\Log\Module'
23 changes: 10 additions & 13 deletions src/PsrLoggerAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,15 @@ class PsrLoggerAdapter extends PsrAbstractLogger
{
/**
* Laminas\Log logger
*
* @var LoggerInterface
*/
protected $logger;
protected LoggerInterface $logger;

/**
* Map PSR-3 LogLevels to priority
*
* @var array
*/
protected $psrPriorityMap = [
protected array $psrPriorityMap = [
LogLevel::EMERGENCY => Logger::EMERG,
LogLevel::ALERT => Logger::ALERT,
LogLevel::CRITICAL => Logger::CRIT,
Expand All @@ -53,10 +51,8 @@ public function __construct(LoggerInterface $logger)

/**
* Returns composed LoggerInterface instance.
*
* @return LoggerInterface
*/
public function getLogger()
public function getLogger(): LoggerInterface
{
return $this->logger;
}
Expand All @@ -67,16 +63,17 @@ public function getLogger()
* @param mixed $level
* @param string $message
* @param array $context
* @return void
* @throws InvalidArgumentException If log level is not recognized.
*/
public function log($level, $message, array $context = [])
public function log($level, $message, array $context = []): void
Copy link
Member

Choose a reason for hiding this comment

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

Let's please not add type information where it would break subclasses

Copy link
Author

Choose a reason for hiding this comment

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

I had to add this because this was enforced by in psr/log 3.0
https://github.com/php-fig/log/blob/3.0.0/src/LoggerTrait.php

Copy link
Author

Choose a reason for hiding this comment

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

Also, I was thinking that this was just a wrapper to offer compatibility from laminas/log to psr/log ... most likely it was used as is, but you are right, it can break sub classes..

Copy link
Author

Choose a reason for hiding this comment

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

Does it make sense to put additional effort into this (make different branch for targeting different psr/log versions)?

Copy link
Member

Choose a reason for hiding this comment

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

I think this component should really not be touched: moving to Monolog\Monolog is endorsed.

Copy link
Author

Choose a reason for hiding this comment

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

I think this component should really not be touched: moving to Monolog\Monolog is endorsed.

Understoo, thank you.

{
if (! array_key_exists($level, $this->psrPriorityMap)) {
throw new InvalidArgumentException(sprintf(
'$level must be one of PSR-3 log levels; received %s',
var_export($level, true)
));
throw new InvalidArgumentException(
sprintf(
'$level must be one of PSR-3 log levels; received %s',
var_export($level, true)
)
);
}

$priority = $this->psrPriorityMap[$level];
Expand Down
20 changes: 20 additions & 0 deletions test/Psr/DummyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace LaminasTest\Log\Psr;

/**
* This class is internal and does not follow the BC promise.
*
* Do NOT use this class in any way.
*
* @internal
*/
class DummyTest
{
public function __toString()
{
return 'DummyTest';
}
}
119 changes: 111 additions & 8 deletions test/PsrLoggerAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,29 @@

namespace LaminasTest\Log;

use DateTime;
use Laminas\Log\Logger;
use Laminas\Log\PsrLoggerAdapter;
use Laminas\Log\Writer\Mock as MockWriter;
use LogicException;
use PHPUnit\Framework\TestCase;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LoggerInterface as PsrLoggerInterface;
use Psr\Log\LogLevel;
use Psr\Log\Test\LoggerInterfaceTest;

use function array_flip;
use function array_map;
use function fclose;
use function fopen;

/**
* @coversDefaultClass \Laminas\Log\PsrLoggerAdapter
* @covers ::<!public>
*/
class PsrLoggerAdapterTest extends LoggerInterfaceTest
class PsrLoggerAdapterTest extends TestCase
{
/** @var array */
protected $psrPriorityMap = [
protected array $psrPriorityMap = [
LogLevel::EMERGENCY => Logger::EMERG,
LogLevel::ALERT => Logger::ALERT,
LogLevel::CRITICAL => Logger::CRIT,
Expand All @@ -32,17 +37,20 @@ class PsrLoggerAdapterTest extends LoggerInterfaceTest
LogLevel::DEBUG => Logger::DEBUG,
];

private ?MockWriter $mockWriter = null;

/**
* Provides logger for LoggerInterface compat tests
*
* @return PsrLoggerAdapter
*/
public function getLogger()
public function getLogger(): PsrLoggerInterface
{
$this->mockWriter = new MockWriter();
$logger = new Logger();
$logger->addProcessor('psrplaceholder');
$logger->addWriter($this->mockWriter);

return new PsrLoggerAdapter($logger);
}

Expand All @@ -55,18 +63,20 @@ public function getLogger()
*
* @return string[]
*/
public function getLogs()
public function getLogs(): array
{
$prefixMap = array_flip($this->psrPriorityMap);

return array_map(function ($event) use ($prefixMap) {
$prefix = $prefixMap[$event['priority']];

return $prefix . ' ' . $event['message'];
}, $this->mockWriter->events);
}

protected function tearDown(): void
{
unset($this->mockWriter);
$this->mockWriter = null;
}

/**
Expand All @@ -91,7 +101,7 @@ public function testPsrLogLevelsMapsToPriorities($logLevel, $priority): void
$context = ['bar' => 'baz'];

$logger = $this->getMockBuilder(Logger::class)
->setMethods(['log'])
->onlyMethods(['log'])
->getMock();
$logger->expects($this->once())
->method('log')
Expand All @@ -110,7 +120,7 @@ public function testPsrLogLevelsMapsToPriorities($logLevel, $priority): void
*
* @return array
*/
public function logLevelsToPriorityProvider()
public function logLevelsToPriorityProvider(): array
{
$return = [];
foreach ($this->psrPriorityMap as $level => $priority) {
Expand All @@ -125,4 +135,97 @@ public function testThrowsOnInvalidLevel()
$this->expectException(InvalidArgumentException::class);
$logger->log('invalid level', 'Foo');
}

public function testImplements()
{
$this->assertInstanceOf(PsrLoggerInterface::class, $this->getLogger());
}

/**
* @dataProvider provideLevelsAndMessages
*/
public function testLogsAtAllLevels($level, $message)
{
$logger = $this->getLogger();
$logger->{$level}($message, ['user' => 'Bob']);
$logger->log($level, $message, ['user' => 'Bob']);

$expected = [
$level . ' message of level ' . $level . ' with context: Bob',
$level . ' message of level ' . $level . ' with context: Bob',
];
$this->assertEquals($expected, $this->getLogs());
}

public function provideLevelsAndMessages(): array
{
return [
LogLevel::EMERGENCY => [LogLevel::EMERGENCY, 'message of level emergency with context: {user}'],
LogLevel::ALERT => [LogLevel::ALERT, 'message of level alert with context: {user}'],
LogLevel::CRITICAL => [LogLevel::CRITICAL, 'message of level critical with context: {user}'],
LogLevel::ERROR => [LogLevel::ERROR, 'message of level error with context: {user}'],
LogLevel::WARNING => [LogLevel::WARNING, 'message of level warning with context: {user}'],
LogLevel::NOTICE => [LogLevel::NOTICE, 'message of level notice with context: {user}'],
LogLevel::INFO => [LogLevel::INFO, 'message of level info with context: {user}'],
LogLevel::DEBUG => [LogLevel::DEBUG, 'message of level debug with context: {user}'],
];
}

public function testContextReplacement()
{
$logger = $this->getLogger();
$logger->info('{Message {nothing} {user} {foo.bar} a}', ['user' => 'Bob', 'foo.bar' => 'Bar']);

$expected = ['info {Message {nothing} Bob Bar a}'];
$this->assertEquals($expected, $this->getLogs());
}

public function testObjectCastToString()
{
$dummy = $this->createPartialMock(Psr\DummyTest::class, ['__toString']);
$dummy->expects($this->once())
->method('__toString')
->will($this->returnValue('DUMMY'));

$this->getLogger()->warning($dummy);

$expected = ['warning DUMMY'];
$this->assertEquals($expected, $this->getLogs());
}

public function testContextCanContainAnything()
{
$closed = fopen('php://memory', 'r');
fclose($closed);

$context = [
'bool' => true,
'null' => null,
'string' => 'Foo',
'int' => 0,
'float' => 0.5,
'nested' => ['with object' => new Psr\DummyTest()],
'object' => new DateTime(),
'resource' => fopen('php://memory', 'r'),
'closed' => $closed,
];

$this->getLogger()->warning('Crazy context data', $context);

$expected = ['warning Crazy context data'];
$this->assertEquals($expected, $this->getLogs());
}

public function testContextExceptionKeyCanBeExceptionOrOtherValues()
{
$logger = $this->getLogger();
$logger->warning('Random message', ['exception' => 'oops']);
$logger->critical('Uncaught Exception!', ['exception' => new LogicException('Fail')]);

$expected = [
'warning Random message',
'critical Uncaught Exception!',
];
$this->assertEquals($expected, $this->getLogs());
}
}
3 changes: 2 additions & 1 deletion test/WriterPluginManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace LaminasTest\Log;

use Laminas\Log\Writer\FirePhp;
use Laminas\Log\WriterPluginManager;
use Laminas\ServiceManager\ServiceManager;
use PHPUnit\Framework\TestCase;
Expand All @@ -21,6 +22,6 @@ protected function setUp(): void
public function testInvokableClassFirephp(): void
{
$firephp = $this->plugins->get('firephp');
$this->assertInstanceOf('Laminas\Log\Writer\Firephp', $firephp);
$this->assertInstanceOf(FirePhp::class, $firephp);
}
}