From 20cb2f4f36b06c6b686337bd97568a716eaf94ca Mon Sep 17 00:00:00 2001 From: Dustin Wilson Date: Thu, 6 Apr 2023 22:50:25 -0500 Subject: [PATCH] Fixes, working on documentation --- README.md | 93 ++++++++++++++++- composer.json | 4 +- composer.lock | 161 +++++++++++------------------- lib/Logger.php | 9 -- lib/Logger/StreamHandler.php | 2 +- tests/cases/TestStreamHandler.php | 34 ++++++- 6 files changed, 180 insertions(+), 123 deletions(-) diff --git a/README.md b/README.md index 18f383a..93ae40b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ [d]: https://www.php-fig.org/psr/psr-3/#13-context [e]: https://www.php-fig.org/psr/psr-3/#11-basics [f]: http://tools.ietf.org/html/rfc5424 +[g]: https://code.mensbeam.com/MensBeam/Filesystem +[h]: https://code.mensbeam.com/MensBeam/Catcher # Logger # @@ -21,6 +23,95 @@ This library attempts what we're calling an "opinionated" implementation of PSR- 4. Also in the first item of [section 1.3][d] it states categorically that implementors must not trigger a warning when errant data is in the `$context` array and treat it with _"as much lenience as possible"_. It then states in the following ittem that if an exception is present in the context data it *must* be in the `exception` key and that implementors *must* verify the `exception` key. This is contradictory. You can't verify the `exception` key without triggering an error or exception when it's wrong. The user should be notified they've made a mistake; it's bad design otherwise. Our solution to this problem is to remove errant throwables from `$context` and also trigger warnings when they're encountered. However, `Logger->$warnOnInvalidContextThrowables` is provided to make it easy to suppress the warnings if necessary. +## Requirements ## + +* PHP 8.1 or newer with the following _optional_ extensions: + * ext-ctype + * ext-mbstring + +### Note ### + +This library uses [mensbeam/filesystem][g] which provides polyfills for `ext-ctype` and `ext-mbstring`. If you have these extensions installed the polyfills won't be used. However, they are still installed. If you don't want the polyfills needlessly installed you can add this to your project's `composer.json`: + +```json +{ + "require": { + "ext-ctype": "*", + "ext-mbstring": "*" + }, + "provide": { + "symfony/polyfill-ctype": "*", + "symfony/polyfill-mbstring": "*" + } +} +``` + +## Installation ## + +```bash +composer require mensbeam/logger +``` + ## Usage ## -To be continued... \ No newline at end of file +This library works without any configuration, but it might not be quite how you think it would work by default: + +```php +use MensBeam\Logger; + +$logger = new Logger(); +``` + +This will create a logger that outputs all debug, info, notice, and warning entries to `STDOUT` while any error, critical, alert, and emergency entries are output to `STDERR`. This seems like it would be a bizarre default since it causes duplicate output to the shell on errors. However, if you accompany it with an error handler like [`Catcher`][h] it suddenly makes sense: + +```php +use MensBeam\{ + Catcher, + Logger +}; +use MensBeam\Catcher\PlainTextHandler; + +$catcher = new Catcher(new PlainTextHandler([ + 'logger' => new Logger('log'), + 'silent' => true +])); +``` + +Now, _Logger_ will take care of the printing. But, _Logger_ can do far more. + +## Documentation ## + +### MensBeam\Logger ### + +```php +namespace MensBeam; + +class Logger implements Psr\Log\LoggerInterface { + public bool $warnOnInvalidContextThrowables = true; + + public function __construct(?string $channel = null, Handler ...$handlers); + + public function getChannel(): ?string; + public function getHandlers(): array; + public function popHandler(): Handler; + public function pushHandler(Handler ...$handlers): void; + public function setHandlers(Handler ...$handlers): void; + public function setChannel(?string $value): void; + public function shiftHandler(): Handler; + public function unshiftHandler(Handler ...$handlers): void; + public function emergency(string|\Stringable $message, array $context = []): void; + public function alert(string|\Stringable $message, array $context = []): void; + public function critical(string|\Stringable $message, array $context = []): void; + public function error(string|\Stringable $message, array $context = []): void; + public function warning(string|\Stringable $message, array $context = []): void; + public function notice(string|\Stringable $message, array $context = []): void; + public function info(string|\Stringable $message, array $context = []): void; + public function debug(string|\Stringable $message, array $context = []): void; + public function log($level, string|\Stringable $message, array $context = []): void; +} +``` + +#### Properties #### + +_warnOnInvalidContextThrowables_: When set to true Logger will trigger warnings when invalid `Throwable`s are in the `$context` array in the logging methods. + diff --git a/composer.json b/composer.json index 773b717..e1ed52b 100644 --- a/composer.json +++ b/composer.json @@ -5,8 +5,7 @@ "require": { "php": ">=8.1", "psr/log": "^3.0", - "mensbeam/filesystem": "^1.0", - "nikic/php-parser": "^4.15" + "mensbeam/filesystem": "^1.0" }, "license": "MIT", "autoload": { @@ -27,7 +26,6 @@ ], "require-dev": { "ext-pcov": "*", - "docopt/docopt": "^1.0", "phpunit/phpunit": "^10.0", "mikey179/vfsstream": "^1.6" } diff --git a/composer.lock b/composer.lock index 90982e9..8b0cdf7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3d02f5002c6b62190718aa8c70a8acd1", + "content-hash": "e0336fd838352efa362298a3b61d3379", "packages": [ { "name": "mensbeam/filesystem", @@ -54,62 +54,6 @@ }, "time": "2023-02-19T18:05:52+00:00" }, - { - "name": "nikic/php-parser", - "version": "v4.15.4", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.9-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" - }, - "time": "2023-03-05T19:49:14+00:00" - }, { "name": "psr/log", "version": "3.0.0", @@ -327,53 +271,6 @@ } ], "packages-dev": [ - { - "name": "docopt/docopt", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/docopt/docopt.php.git", - "reference": "0e3db660cf2f2eb07a83253790b7d97cdb398826" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/docopt/docopt.php/zipball/0e3db660cf2f2eb07a83253790b7d97cdb398826", - "reference": "0e3db660cf2f2eb07a83253790b7d97cdb398826", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/docopt.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Blake Williams", - "email": "code@shabbyrobe.org", - "homepage": "http://docopt.org/", - "role": "Developer" - } - ], - "description": "Port of Python's docopt for PHP >=5.3", - "homepage": "http://github.com/docopt/docopt.php", - "keywords": [ - "cli", - "docs" - ], - "support": { - "issues": "https://github.com/docopt/docopt.php/issues", - "source": "https://github.com/docopt/docopt.php/tree/1.0.5" - }, - "time": "2023-03-22T12:31:48+00:00" - }, { "name": "mikey179/vfsstream", "version": "v1.6.11", @@ -484,6 +381,62 @@ ], "time": "2023-03-08T13:26:56+00:00" }, + { + "name": "nikic/php-parser", + "version": "v4.15.4", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + }, + "time": "2023-03-05T19:49:14+00:00" + }, { "name": "phar-io/manifest", "version": "2.0.3", diff --git a/lib/Logger.php b/lib/Logger.php index 819349e..93fe2a9 100644 --- a/lib/Logger.php +++ b/lib/Logger.php @@ -19,15 +19,6 @@ use Psr\Log\LoggerInterface; class Logger implements LoggerInterface { - public const EMERGENCY = 0; - public const ALERT = 1; - public const CRITICAL = 2; - public const ERROR = 3; - public const WARNING = 4; - public const NOTICE = 5; - public const INFO = 6; - public const DEBUG = 7; - /** * Flag that causes warnings to be triggered when invalid throwables are in the * context array diff --git a/lib/Logger/StreamHandler.php b/lib/Logger/StreamHandler.php index 3ad3581..183961c 100644 --- a/lib/Logger/StreamHandler.php +++ b/lib/Logger/StreamHandler.php @@ -75,7 +75,7 @@ class StreamHandler extends Handler { $this->urlScheme = $matches['scheme'] ?: 'file'; } else { $type = gettype($value); - $type = ($type === 'object') ? $value::class : $value; + $type = ($type === 'object') ? $value::class : $type; throw new InvalidArgumentException(sprintf('Argument #1 ($value) must be of type resource|string, %s given', $type)); } } diff --git a/tests/cases/TestStreamHandler.php b/tests/cases/TestStreamHandler.php index 70b9ee5..7c3753d 100644 --- a/tests/cases/TestStreamHandler.php +++ b/tests/cases/TestStreamHandler.php @@ -11,6 +11,7 @@ use MensBeam\Logger, org\bovigo\vfs\vfsStream; use MensBeam\Logger\{ InvalidArgumentException, + IOException, Level, StreamHandler }; @@ -24,11 +25,6 @@ class TestStreamHandler extends \PHPUnit\Framework\TestCase { $this->assertEquals(1, preg_match($regex, $closure())); } - public function testFatalErrors(): void { - $this->expectException(InvalidArgumentException::class); - new StreamHandler(42); - } - public function testGetStream(): void { $h = new StreamHandler('ook'); $this->assertSame(CWD . '/ook', $h->getStream()); @@ -47,6 +43,17 @@ class TestStreamHandler extends \PHPUnit\Framework\TestCase { fclose($s); } + /** @dataProvider provideFatalErrorTests */ + public function testFatalErrors(string $throwableClassName, int $code, string $message, \Closure $closure): void { + $this->expectException($throwableClassName); + $this->expectExceptionMessage($message); + if ($throwableClassName === Error::class) { + $this->expectExceptionCode($code); + } + + $closure(new StreamHandler()); + } + public static function provideResourceTypesTests(): iterable { $iterable = [ @@ -92,4 +99,21 @@ class TestStreamHandler extends \PHPUnit\Framework\TestCase { yield $i; } } + + public static function provideFatalErrorTests(): iterable { + $iterable = [ + [ + InvalidArgumentException::class, + 0, + 'Argument #1 ($value) must be of type resource|string, integer given', + function (StreamHandler $h): void { + new StreamHandler(42); + } + ] + ]; + + foreach ($iterable as $i) { + yield $i; + } + } } \ No newline at end of file