diff --git a/.github/workflows/ci_build.yml b/.github/workflows/ci_build.yml index ac3b1e2f..a07daf14 100644 --- a/.github/workflows/ci_build.yml +++ b/.github/workflows/ci_build.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['8.1', '8.2'] + php-versions: ['8.1', '8.2', '8.3', '8.4'] steps: - name: Setup PHP Action uses: shivammathur/setup-php@v2 diff --git a/composer.json b/composer.json index c27ac87e..f56424b5 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,6 @@ "twig", "plates", "symfony", - "aura", "pimple" ], "homepage": "https://github.com/samsonasik/ErrorHeroModule", @@ -37,15 +36,15 @@ ], "require": { "php": "^8.1", - "laminas/laminas-db": "^2.16.2", - "laminas/laminas-diactoros": "^2.22", - "laminas/laminas-log": "^2.16", - "laminas/laminas-mail": "^2.21", - "laminas/laminas-psr7bridge": "^1.8", - "laminas/laminas-uri": "^2.10", + "laminas/laminas-db": "^2.20.0", + "laminas/laminas-diactoros": "^2.26", + "laminas/laminas-log": "^2.17.1", + "laminas/laminas-mail": "^2.25.1", + "laminas/laminas-psr7bridge": "^1.11", + "laminas/laminas-uri": "^2.13", "psr/container": "^1.1.2 || 2.0", - "samsonasik/array-lookup": "^1.0", - "seld/jsonlint": "^1.9", + "samsonasik/array-lookup": "^1.8.1 || ^2.0", + "seld/jsonlint": "^1.11", "webmozart/assert": "^1.11" }, "conflict": { @@ -57,23 +56,22 @@ "laminas/laminas-mvc-console": "*" }, "require-dev": { - "aura/di": "^3.4 || ^4.2.1", - "doctrine/dbal": "^3.5.1", - "doctrine/doctrine-orm-module": "^4.2.1 || ^5.0", - "kahlan/kahlan": "^5.2.2", - "laminas/laminas-cli": "^1.7", - "laminas/laminas-coding-standard": "^2.4", - "laminas/laminas-form": "^2.16 || ^3.8", - "laminas/laminas-mvc": "^3.6", - "laminas/laminas-servicemanager": "^3.20", - "mezzio/mezzio": "^3.14", - "mezzio/mezzio-laminasviewrenderer": "^2.10", - "php-coveralls/php-coveralls": "^2.5.3", - "phpstan/phpstan": "^1.9.3", - "phpstan/phpstan-webmozart-assert": "^1.2.2", + "doctrine/dbal": "^3.9.3", + "doctrine/doctrine-orm-module": "^4.2.1 || ^5.3", + "kahlan/kahlan": "^5.2.8", + "laminas/laminas-cli": "^1.11", + "laminas/laminas-coding-standard": "^2.5", + "laminas/laminas-form": "^2.16 || ^3.21", + "laminas/laminas-mvc": "^3.8", + "laminas/laminas-servicemanager": "^3.23", + "mezzio/mezzio": "^3.20.1", + "mezzio/mezzio-laminasviewrenderer": "^2.16", + "php-coveralls/php-coveralls": "^2.7.0", + "phpstan/phpstan": "^2.0.4", + "phpstan/phpstan-webmozart-assert": "^2.0", "pimple/pimple": "^3.5", "rector/rector": "dev-main", - "symfony/dependency-injection": "^5.4.16" + "symfony/dependency-injection": "^5.4.48" }, "suggest": { "mezzio/mezzio-laminasviewrenderer": "^2.0 to be able to use laminas-view template engine in mezzio ^3.0", diff --git a/config/mezzio-error-hero-module.local.php.dist b/config/mezzio-error-hero-module.local.php.dist index 5483f7d2..769c7efc 100644 --- a/config/mezzio-error-hero-module.local.php.dist +++ b/config/mezzio-error-hero-module.local.php.dist @@ -6,6 +6,7 @@ use App\Exception\MyException; use RuntimeException; use Error; use ErrorHeroModule\Command\BaseLoggingCommandInitializer; +use ErrorHeroModule\Compat\LoggerAbstractServiceFactory; use ErrorHeroModule\Middleware\Mezzio; use ErrorHeroModule\Middleware\MezzioFactory; use ErrorHeroModule\ErrorHeroModule\Middleware\Routed\Preview\ErrorPreviewAction; @@ -15,7 +16,6 @@ use ErrorHeroModule\Command\BaseLoggingCommand; use ErrorHeroModule\Command\Preview\ErrorPreviewConsoleCommand; use Laminas\Db\Adapter\AdapterInterface; use Laminas\Log; -use Laminas\Log\LoggerAbstractServiceFactory; use Laminas\ServiceManager\Factory\InvokableFactory; return [ diff --git a/config/module.config.php b/config/module.config.php index 6f7e687a..2e1ab484 100644 --- a/config/module.config.php +++ b/config/module.config.php @@ -4,8 +4,8 @@ use ErrorHeroModule\Command\BaseLoggingCommandInitializer; use ErrorHeroModule\Command\Preview\ErrorPreviewConsoleCommand; +use ErrorHeroModule\Compat\LoggerAbstractServiceFactory; use ErrorHeroModule\Controller\ErrorPreviewController; -use Laminas\Log\LoggerAbstractServiceFactory; use ErrorHeroModule\Listener\Mvc; use ErrorHeroModule\Listener\MvcFactory; use ErrorHeroModule\Handler\Logging; diff --git a/kahlan-config.php b/kahlan-config.php index b99c87c6..89b6a9c2 100644 --- a/kahlan-config.php +++ b/kahlan-config.php @@ -1,13 +1,14 @@ ! $this->commandLine()->get('no-colors') ]); $reporters->add('coverage', $coverage); -}); \ No newline at end of file +}); diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9f3c6cc2..2b727fc8 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,26 +1,1189 @@ parameters: ignoreErrors: - - message: "#^Instanceof between \\*NEVER\\* and ErrorHeroModule\\\\Listener\\\\Mvc will always evaluate to false\\.$#" + message: '#^Cannot access offset ''console'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 path: src/Command/BaseLoggingCommand.php - - message: "#^Parameter \\#1 \\$callback of function set_error_handler expects \\(callable\\(int, string, string, int\\)\\: bool\\)\\|null, array\\{\\$this\\(ErrorHeroModule\\\\Command\\\\BaseLoggingCommand\\), 'phpErrorHandler'\\} given\\.$#" + message: '#^Cannot access offset ''display_errors'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 + path: src/Command/BaseLoggingCommand.php + + - + message: '#^Cannot access offset ''exclude\-exceptions'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Command/BaseLoggingCommand.php + + - + message: '#^Cannot access offset ''exclude\-php\-errors'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Command/BaseLoggingCommand.php + + - + message: '#^Cannot access offset ''message'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Command/BaseLoggingCommand.php + + - + message: '#^Instanceof between \*NEVER\* and ErrorHeroModule\\Listener\\Mvc will always evaluate to false\.$#' + identifier: instanceof.alwaysFalse + count: 1 + path: src/Command/BaseLoggingCommand.php + + - + message: '#^Method ErrorHeroModule\\Command\\BaseLoggingCommand\:\:init\(\) has parameter \$errorHeroModuleConfig with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Command/BaseLoggingCommand.php + + - + message: '#^Parameter \#1 \$callback of function set_error_handler expects \(callable\(int, string, string, int\)\: bool\)\|null, array\{\$this\(ErrorHeroModule\\Command\\BaseLoggingCommand\), ''phpErrorHandler''\} given\.$#' + identifier: argument.type + count: 1 + path: src/Command/BaseLoggingCommand.php + + - + message: '#^Parameter \#1 \$data of static method ArrayLookup\\AtLeast\:\:once\(\) expects iterable\, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Command/BaseLoggingCommand.php + + - + message: '#^Parameter \#1 \$excludeExceptionsConfig of function ErrorHeroModule\\isExcludedException expects array\\|string\>, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Command/BaseLoggingCommand.php + + - + message: '#^Property ErrorHeroModule\\Command\\BaseLoggingCommand\:\:\$errorHeroModuleConfig type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Command/BaseLoggingCommand.php + + - + message: '#^PHPDoc tag @var for variable \$config has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Command/BaseLoggingCommandInitializer.php + + - + message: '#^Parameter \#1 \$errorHeroModuleConfig of method ErrorHeroModule\\Command\\BaseLoggingCommand\:\:init\(\) expects array, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Command/BaseLoggingCommandInitializer.php + + - + message: '#^Cannot access offset ''name'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 + path: src/Compat/Logger.php + + - + message: '#^Cannot access offset ''options'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 + path: src/Compat/Logger.php + + - + message: '#^Cannot access offset ''priority'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 + path: src/Compat/Logger.php + + - + message: '#^Cannot call method shutdown\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:__construct\(\) has parameter \$options with no value type specified in iterable type Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:__construct\(\) has parameter \$options with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:addProcessor\(\) has parameter \$options with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:addWriter\(\) has parameter \$options with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:alert\(\) has parameter \$extra with no value type specified in iterable type Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:alert\(\) has parameter \$extra with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:alert\(\) has parameter \$extra with no value type specified in iterable type array\|Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:crit\(\) has parameter \$extra with no value type specified in iterable type Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:crit\(\) has parameter \$extra with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:crit\(\) has parameter \$extra with no value type specified in iterable type array\|Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:debug\(\) has parameter \$extra with no value type specified in iterable type Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:debug\(\) has parameter \$extra with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:debug\(\) has parameter \$extra with no value type specified in iterable type array\|Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:emerg\(\) has parameter \$extra with no value type specified in iterable type Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:emerg\(\) has parameter \$extra with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:emerg\(\) has parameter \$extra with no value type specified in iterable type array\|Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:err\(\) has parameter \$extra with no value type specified in iterable type Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:err\(\) has parameter \$extra with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:err\(\) has parameter \$extra with no value type specified in iterable type array\|Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:getProcessors\(\) return type with generic class Laminas\\Stdlib\\SplPriorityQueue does not specify its types\: TValue, TPriority$#' + identifier: missingType.generics + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:getWriters\(\) return type with generic class Laminas\\Stdlib\\SplPriorityQueue does not specify its types\: TValue, TPriority$#' + identifier: missingType.generics + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:info\(\) has parameter \$extra with no value type specified in iterable type Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:info\(\) has parameter \$extra with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:info\(\) has parameter \$extra with no value type specified in iterable type array\|Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:log\(\) has parameter \$extra with no value type specified in iterable type Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:log\(\) has parameter \$extra with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:log\(\) has parameter \$extra with no value type specified in iterable type array\|Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:notice\(\) has parameter \$extra with no value type specified in iterable type Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:notice\(\) has parameter \$extra with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:notice\(\) has parameter \$extra with no value type specified in iterable type array\|Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:processorPlugin\(\) has parameter \$options with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:processorPlugin\(\) should return Laminas\\Log\\Processor\\ProcessorInterface but returns mixed\.$#' + identifier: return.type + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:setWriters\(\) has parameter \$splPriorityQueue with generic class Laminas\\Stdlib\\SplPriorityQueue but does not specify its types\: TValue, TPriority$#' + identifier: missingType.generics + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:warn\(\) has parameter \$extra with no value type specified in iterable type Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:warn\(\) has parameter \$extra with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:warn\(\) has parameter \$extra with no value type specified in iterable type array\|Traversable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:writerPlugin\(\) has parameter \$options with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\Logger\:\:writerPlugin\(\) should return Laminas\\Log\\Writer\\WriterInterface but returns mixed\.$#' + identifier: return.type + count: 1 + path: src/Compat/Logger.php + + - + message: '#^PHPDoc tag @return with type mixed is not subtype of native type \(callable\)\|false\|null\.$#' + identifier: return.phpDocType + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Parameter \#1 \$plugins of method ErrorHeroModule\\Compat\\Logger\:\:setProcessorPluginManager\(\) expects Laminas\\Log\\ProcessorPluginManager\|string, Laminas\\ServiceManager\\AbstractPluginManager given\.$#' + identifier: argument.type + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Parameter \#1 \$priority of method ErrorHeroModule\\Compat\\Logger\:\:log\(\) expects int, mixed given\.$#' + identifier: argument.type + count: 3 + path: src/Compat/Logger.php + + - + message: '#^Parameter \#1 \$processor of method ErrorHeroModule\\Compat\\Logger\:\:addProcessor\(\) expects Laminas\\Log\\Processor\\ProcessorInterface\|string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Parameter \#1 \$writer of method ErrorHeroModule\\Compat\\Logger\:\:addWriter\(\) expects Laminas\\Log\\Writer\\WriterInterface\|string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Parameter \#1 \$writerPluginManager of method ErrorHeroModule\\Compat\\Logger\:\:setWriterPluginManager\(\) expects Laminas\\Log\\WriterPluginManager, Laminas\\ServiceManager\\AbstractPluginManager given\.$#' + identifier: argument.type + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Parameter \#2 \$priority of method ErrorHeroModule\\Compat\\Logger\:\:addProcessor\(\) expects int, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Parameter \#2 \$priority of method ErrorHeroModule\\Compat\\Logger\:\:addWriter\(\) expects int, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Parameter \#3 \$options of method ErrorHeroModule\\Compat\\Logger\:\:addProcessor\(\) expects array\|null, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Parameter \#3 \$options of method ErrorHeroModule\\Compat\\Logger\:\:addWriter\(\) expects array\|null, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Property ErrorHeroModule\\Compat\\Logger\:\:\$errorPriorityMap type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Property ErrorHeroModule\\Compat\\Logger\:\:\$priorities type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Property ErrorHeroModule\\Compat\\Logger\:\:\$processors with generic class Laminas\\Stdlib\\SplPriorityQueue does not specify its types\: TValue, TPriority$#' + identifier: missingType.generics + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Property ErrorHeroModule\\Compat\\Logger\:\:\$writers with generic class Laminas\\Stdlib\\SplPriorityQueue does not specify its types\: TValue, TPriority$#' + identifier: missingType.generics + count: 1 + path: src/Compat/Logger.php + + - + message: '#^Method ErrorHeroModule\\Compat\\LoggerAbstractServiceFactory\:\:__invoke\(\) has parameter \$options with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Compat/LoggerAbstractServiceFactory.php + + - + message: '#^Method ErrorHeroModule\\Compat\\LoggerAbstractServiceFactory\:\:__invoke\(\) should return Laminas\\Log\\Logger but returns ErrorHeroModule\\Compat\\Logger\.$#' + identifier: return.type + count: 1 + path: src/Compat/LoggerAbstractServiceFactory.php + + - + message: '#^Parameter \#1 \$config of method Laminas\\Log\\LoggerServiceFactory\:\:processConfig\(\) expects array, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Compat/LoggerAbstractServiceFactory.php + + - + message: '#^Call to static method Webmozart\\Assert\\Assert\:\:isInstanceOf\(\) with Laminas\\Http\\PhpEnvironment\\Request and ''Laminas\\\\Http\\\\PhpEnvironment\\\\Request'' will always evaluate to true\.$#' + identifier: staticMethod.alreadyNarrowedType + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Cannot access offset ''email\-from'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Cannot access offset ''email\-to\-send'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Cannot access offset ''files_data'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Invalid type object to throw\.$#' + identifier: throw.notThrowable + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Method ErrorHeroModule\\Handler\\Logging\:\:__construct\(\) has parameter \$errorHeroModuleLocalConfig with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Method ErrorHeroModule\\Handler\\Logging\:\:__construct\(\) has parameter \$logWritersConfig with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Method ErrorHeroModule\\Handler\\Logging\:\:collectErrorExceptionData\(\) should return array\{priority\: int, errorType\: string, errorFile\: string, errorLine\: int, trace\: string, errorMessage\: string\} but returns array\{priority\: mixed, errorType\: ''E_COMPILE_ERROR''\|''E_COMPILE_WARNING''\|''E_CORE_ERROR''\|''E_CORE_WARNING''\|''E_DEPRECATED''\|''E_ERROR''\|''E_NOTICE''\|''E_PARSE''\|''E_RECOVERABLE_ERROR''\|''E_STRICT''\|''E_USER_DEPRECATED''\|''E_USER_ERROR''\|''E_USER_NOTICE''\|''E_USER_WARNING''\|''E_WARNING''\|\(class\-string\&literal\-string\), errorFile\: string, errorLine\: int, trace\: string, errorMessage\: string\}\.$#' + identifier: return.type + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Method ErrorHeroModule\\Handler\\Logging\:\:collectErrorExceptionExtraData\(\) has parameter \$collectedExceptionData with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Method ErrorHeroModule\\Handler\\Logging\:\:collectErrorExceptionExtraData\(\) should return array\{server_url\: string, url\: string, file\: string, line\: int, error_type\: string, trace\: string, request_data\: array\\} but returns array\{server_url\: string, url\: string, file\: mixed, line\: mixed, error_type\: mixed, trace\: mixed, request_data\: array\\}\.$#' + identifier: return.type + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Method ErrorHeroModule\\Handler\\Logging\:\:sendMail\(\) has parameter \$extra with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Logging.php + + - + message: '#^PHPDoc tag @var for variable \$files contains generic interface Laminas\\Stdlib\\ParametersInterface but does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: src/Handler/Logging.php + + - + message: '#^PHPDoc tag @var for variable \$files has no value type specified in iterable type Laminas\\Stdlib\\ParametersInterface\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Logging.php + + - + message: '#^PHPDoc tag @var for variable \$post contains generic interface Laminas\\Stdlib\\ParametersInterface but does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: src/Handler/Logging.php + + - + message: '#^PHPDoc tag @var for variable \$post has no value type specified in iterable type Laminas\\Stdlib\\ParametersInterface\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Logging.php + + - + message: '#^PHPDoc tag @var for variable \$query contains generic interface Laminas\\Stdlib\\ParametersInterface but does not specify its types\: TKey, TValue$#' + identifier: missingType.generics + count: 1 + path: src/Handler/Logging.php + + - + message: '#^PHPDoc tag @var for variable \$query has no value type specified in iterable type Laminas\\Stdlib\\ParametersInterface\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Parameter \#1 \$emailOrAddressList of method Laminas\\Mail\\Message\:\:setTo\(\) expects array\|Laminas\\Mail\\Address\\AddressInterface\|string\|Traversable, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Parameter \#2 \$array of function implode expects array\|null, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Parameter \#3 \$filesData of class ErrorHeroModule\\Handler\\Writer\\Mail constructor expects array, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Property ErrorHeroModule\\Handler\\Logging\:\:\$configLoggingSettings \(array\) does not accept mixed\.$#' + identifier: assign.propertyType + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Property ErrorHeroModule\\Handler\\Logging\:\:\$configLoggingSettings type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Property ErrorHeroModule\\Handler\\Logging\:\:\$emailReceivers \(array\) does not accept mixed\.$#' + identifier: assign.propertyType + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Property ErrorHeroModule\\Handler\\Logging\:\:\$emailReceivers type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Property ErrorHeroModule\\Handler\\Logging\:\:\$emailSender \(string\) does not accept mixed\.$#' + identifier: assign.propertyType + count: 1 + path: src/Handler/Logging.php + + - + message: '#^Cannot access offset ''ErrorHeroModuleLogger'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/LoggingFactory.php + + - + message: '#^Cannot access offset ''email\-notification\-settings'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/LoggingFactory.php + + - + message: '#^Cannot access offset ''enable'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/LoggingFactory.php + + - + message: '#^Cannot access offset ''include\-files\-to\-attachments'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/LoggingFactory.php + + - + message: '#^Cannot access offset ''mail\-message'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/LoggingFactory.php + + - + message: '#^Cannot access offset ''mail\-transport'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/LoggingFactory.php + + - + message: '#^Cannot access offset ''writers'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/LoggingFactory.php + + - + message: '#^PHPDoc tag @var for variable \$config has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/LoggingFactory.php + + - + message: '#^Parameter \#1 \$id of method Psr\\Container\\ContainerInterface\:\:get\(\) expects string, mixed given\.$#' + identifier: argument.type + count: 2 + path: src/Handler/LoggingFactory.php + + - + message: '#^Parameter \#2 \$errorHeroModuleLocalConfig of class ErrorHeroModule\\Handler\\Logging constructor expects array, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/LoggingFactory.php + + - + message: '#^Parameter \#3 \$logWritersConfig of class ErrorHeroModule\\Handler\\Logging constructor expects array, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/LoggingFactory.php + + - + message: '#^Parameter \#6 \$includeFilesToAttachments of class ErrorHeroModule\\Handler\\Logging constructor expects bool, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/LoggingFactory.php + + - + message: '#^Binary operation "\." between mixed and '' DESC'' results in an error\.$#' + identifier: binaryOp.invalid + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access offset ''column'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 6 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access offset ''error_type'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access offset ''extra'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 4 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access offset ''file'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access offset ''line'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access offset ''message'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 - path: src/Command/BaseLoggingCommand.php + path: src/Handler/Writer/Checker/Db.php - - message: "#^Invalid type object to throw\\.$#" + message: '#^Cannot access offset ''name'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 - path: src/Handler/Logging.php + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access offset ''options'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 7 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access offset ''table'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access offset ''timestamp'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access offset ''url'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access property \$db on mixed\.$#' + identifier: property.nonObject + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot cast mixed to string\.$#' + identifier: cast.string + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Method ErrorHeroModule\\Handler\\Writer\\Checker\\Db\:\:__construct\(\) has parameter \$configLoggingSettings with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Method ErrorHeroModule\\Handler\\Writer\\Checker\\Db\:\:__construct\(\) has parameter \$logWritersConfig with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Parameter \#1 \$table of class Laminas\\Db\\TableGateway\\TableGateway constructor expects array\|Laminas\\Db\\Sql\\TableIdentifier\|string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Parameter \#2 \$adapter of class Laminas\\Db\\TableGateway\\TableGateway constructor expects Laminas\\Db\\Adapter\\AdapterInterface, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/Writer/Checker/Db.php + + - + message: '#^Cannot access offset ''name'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Handler/Writer/Mail.php + + - + message: '#^Method ErrorHeroModule\\Handler\\Writer\\Mail\:\:__construct\(\) has parameter \$filesData with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Writer/Mail.php + + - + message: '#^Method ErrorHeroModule\\Handler\\Writer\\Mail\:\:bodyAddPart\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Writer/Mail.php + + - + message: '#^Method ErrorHeroModule\\Handler\\Writer\\Mail\:\:singleBodyAddPart\(\) has parameter \$data with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Handler/Writer/Mail.php + + - + message: '#^Parameter \#1 \$array of function key expects array\|object, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/Writer/Mail.php + + - + message: '#^Parameter \#1 \$filename of function fopen expects string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/Writer/Mail.php + + - + message: '#^Parameter \#2 \$data of method ErrorHeroModule\\Handler\\Writer\\Mail\:\:bodyAddPart\(\) expects array, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/Writer/Mail.php + + - + message: '#^Parameter \#2 \$data of method ErrorHeroModule\\Handler\\Writer\\Mail\:\:singleBodyAddPart\(\) expects array, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Handler/Writer/Mail.php + + - + message: '#^Property Laminas\\Mime\\Part\:\:\$filename \(string\|null\) does not accept mixed\.$#' + identifier: assign.propertyType + count: 1 + path: src/Handler/Writer/Mail.php + + - + message: '#^Property Laminas\\Mime\\Part\:\:\$type \(string\) does not accept mixed\.$#' + identifier: assign.propertyType + count: 1 + path: src/Handler/Writer/Mail.php + + - + message: '#^Cannot access offset ''ajax'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Listener/Mvc.php + + - + message: '#^Cannot access offset ''display_errors'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 + path: src/Listener/Mvc.php + + - + message: '#^Cannot access offset ''exclude\-exceptions'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Listener/Mvc.php + + - + message: '#^Cannot access offset ''exclude\-php\-errors'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Listener/Mvc.php + + - + message: '#^Cannot access offset ''layout'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Listener/Mvc.php + + - + message: '#^Cannot access offset ''message'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Listener/Mvc.php + + - + message: '#^Cannot access offset ''template'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 + path: src/Listener/Mvc.php + + - + message: '#^Cannot access offset ''view'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Listener/Mvc.php + + - + message: '#^Method ErrorHeroModule\\Listener\\Mvc\:\:__construct\(\) has parameter \$errorHeroModuleConfig with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Listener/Mvc.php + + - + message: '#^Parameter \#1 \$callback of function set_error_handler expects \(callable\(int, string, string, int\)\: bool\)\|null, array\{\$this\(ErrorHeroModule\\Listener\\Mvc\), ''phpErrorHandler''\} given\.$#' + identifier: argument.type + count: 1 + path: src/Listener/Mvc.php + + - + message: '#^Parameter \#1 \$data of static method ArrayLookup\\AtLeast\:\:once\(\) expects iterable\, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Listener/Mvc.php + + - + message: '#^Parameter \#1 \$excludeExceptionsConfig of function ErrorHeroModule\\isExcludedException expects array\\|string\>, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Listener/Mvc.php + + - + message: '#^Parameter \#1 \$message of function ErrorHeroModule\\detectMessageContentType expects string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Listener/Mvc.php + + - + message: '#^Parameter \#1 \$nameOrModel of method Laminas\\View\\Renderer\\PhpRenderer\:\:render\(\) expects Laminas\\View\\Model\\ModelInterface\|string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Listener/Mvc.php - - message: "#^Parameter \\#1 \\$callback of function set_error_handler expects \\(callable\\(int, string, string, int\\)\\: bool\\)\\|null, array\\{\\$this\\(ErrorHeroModule\\\\Listener\\\\Mvc\\), 'phpErrorHandler'\\} given\\.$#" + message: '#^Parameter \#1 \$template of method Laminas\\View\\Model\\ModelInterface\:\:setTemplate\(\) expects string, mixed given\.$#' + identifier: argument.type count: 1 path: src/Listener/Mvc.php - - message: "#^Parameter \\#1 \\$callback of function set_error_handler expects \\(callable\\(int, string, string, int\\)\\: bool\\)\\|null, array\\{\\$this\\(ErrorHeroModule\\\\Middleware\\\\Mezzio\\), 'phpErrorHandler'\\} given\\.$#" + message: '#^PHPDoc tag @var for variable \$config has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Listener/MvcFactory.php + + - + message: '#^Parameter \#1 \$errorHeroModuleConfig of class ErrorHeroModule\\Listener\\Mvc constructor expects array, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Listener/MvcFactory.php + + - + message: '#^Cannot access offset ''layout'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Middleware/Mezzio.php + + - + message: '#^Cannot access offset ''message'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 + path: src/Middleware/Mezzio.php + + - + message: '#^Cannot access offset ''view'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Middleware/Mezzio.php + + - + message: '#^Cannot access property \$layout on mixed\.$#' + identifier: property.nonObject + count: 1 + path: src/Middleware/Mezzio.php + + - + message: '#^Method ErrorHeroModule\\Middleware\\Mezzio\:\:__construct\(\) has parameter \$errorHeroModuleConfig with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 2 + path: src/Middleware/Mezzio.php + + - + message: '#^Parameter \#1 \$callback of function set_error_handler expects \(callable\(int, string, string, int\)\: bool\)\|null, array\{\$this\(ErrorHeroModule\\Middleware\\Mezzio\), ''phpErrorHandler''\} given\.$#' + identifier: argument.type + count: 1 + path: src/Middleware/Mezzio.php + + - + message: '#^Parameter \#1 \$data of static method ArrayLookup\\AtLeast\:\:once\(\) expects iterable\, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Middleware/Mezzio.php + + - + message: '#^Parameter \#1 \$excludeExceptionsConfig of function ErrorHeroModule\\isExcludedException expects array\\|string\>, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Middleware/Mezzio.php + + - + message: '#^Parameter \#1 \$message of function ErrorHeroModule\\detectMessageContentType expects string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Middleware/Mezzio.php + + - + message: '#^Parameter \#1 \$name of method Mezzio\\Template\\TemplateRendererInterface\:\:render\(\) expects string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Middleware/Mezzio.php + + - + message: '#^Parameter \#1 \$string of method Psr\\Http\\Message\\StreamInterface\:\:write\(\) expects string, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Middleware/Mezzio.php + + - + message: '#^Parameter \#1 \$template of method Laminas\\View\\Model\\ViewModel\:\:setTemplate\(\) expects string, mixed given\.$#' + identifier: argument.type count: 1 path: src/Middleware/Mezzio.php + + - + message: '#^Method ErrorHeroModule\\Middleware\\MezzioFactory\:\:createMiddlewareInstance\(\) has parameter \$configuration with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Middleware/MezzioFactory.php + + - + message: '#^Method ErrorHeroModule\\Middleware\\MezzioFactory\:\:verifyConfig\(\) has parameter \$configuration with no value type specified in iterable type iterable\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Middleware/MezzioFactory.php + + - + message: '#^Parameter \#1 \$errorHeroModuleConfig of class ErrorHeroModule\\Middleware\\Mezzio constructor expects array\{exclude\-exceptions\: array, enable\: bool, display\-settings\: array\}, mixed given\.$#' + identifier: argument.type + count: 1 + path: src/Middleware/MezzioFactory.php + + - + message: '#^Cannot access offset ''ErrorHeroModule\\\\Command\\\\Preview\\\\ErrorPreviewConsoleCommand'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Module.php + + - + message: '#^Cannot access offset ''ErrorHeroModule\\\\Controller\\\\ErrorPreviewController'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Module.php + + - + message: '#^Cannot access offset ''commands'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Module.php + + - + message: '#^Cannot access offset ''enable\-error\-preview\-page'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Module.php + + - + message: '#^Cannot access offset ''error\-preview'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Module.php + + - + message: '#^Cannot access offset ''errorheromodule\:preview'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Module.php + + - + message: '#^Cannot access offset ''factories'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 + path: src/Module.php + + - + message: '#^Cannot access offset ''routes'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Module.php + + - + message: '#^Method ErrorHeroModule\\Module\:\:getConfig\(\) should return array\ but returns mixed\.$#' + identifier: return.type + count: 1 + path: src/Module.php + + - + message: '#^PHPDoc tag @var for variable \$configuration has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Module.php + + - + message: '#^Method ErrorHeroModule\\Transformer\\Doctrine\:\:transform\(\) has parameter \$configuration with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Transformer/Doctrine.php + + - + message: '#^PHPDoc tag @var for variable \$params has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Transformer/Doctrine.php + + - + message: '#^Cannot access offset ''ErrorHeroModuleLogger'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Transformer/PimpleService.php + + - + message: '#^Cannot access property \$pimple on mixed\.$#' + identifier: property.nonObject + count: 1 + path: src/Transformer/PimpleService.php + + - + message: '#^Method ErrorHeroModule\\Transformer\\PimpleService\:\:transform\(\) has parameter \$configuration with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Transformer/PimpleService.php + + - + message: '#^Method ErrorHeroModule\\Transformer\\SymfonyService\:\:transform\(\) has parameter \$configuration with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Transformer/SymfonyService.php + + - + message: '#^Cannot access offset ''ErrorHeroModuleLogger'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Transformer/TransformerAbstract.php + + - + message: '#^Cannot access offset ''adapters'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Transformer/TransformerAbstract.php + + - + message: '#^Cannot access offset ''writers'' on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Transformer/TransformerAbstract.php + + - + message: '#^Cannot access offset mixed on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 1 + path: src/Transformer/TransformerAbstract.php + + - + message: '#^Method ErrorHeroModule\\Transformer\\TransformerAbstract\:\:getDbAdapterConfig\(\) has parameter \$configuration with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Transformer/TransformerAbstract.php + + - + message: '#^Method ErrorHeroModule\\Transformer\\TransformerAbstract\:\:getDbAdapterConfig\(\) should return array\ but returns mixed\.$#' + identifier: return.type + count: 2 + path: src/Transformer/TransformerAbstract.php + + - + message: '#^Method ErrorHeroModule\\Transformer\\TransformerAbstract\:\:getLoggerInstance\(\) has parameter \$configuration with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Transformer/TransformerAbstract.php + + - + message: '#^Method ErrorHeroModule\\Transformer\\TransformerAbstract\:\:getLoggerInstance\(\) has parameter \$dbConfig with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Transformer/TransformerAbstract.php + + - + message: '#^Method ErrorHeroModule\\Transformer\\TransformerAbstract\:\:getWriterConfig\(\) has parameter \$configuration with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Transformer/TransformerAbstract.php + + - + message: '#^Method ErrorHeroModule\\Transformer\\TransformerAbstract\:\:getWriterConfig\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Transformer/TransformerAbstract.php + + - + message: '#^Method ErrorHeroModule\\Transformer\\TransformerAbstract\:\:getWriterConfig\(\) should return array\{array\{name\: string, options\: array\}\} but returns mixed\.$#' + identifier: return.type + count: 1 + path: src/Transformer/TransformerAbstract.php + + - + message: '#^Method ErrorHeroModule\\Transformer\\TransformerInterface\:\:transform\(\) has parameter \$configuration with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: src/Transformer/TransformerInterface.php diff --git a/phpstan.neon b/phpstan.neon index 9554453d..7b6f6291 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -7,7 +7,5 @@ parameters: - src/Command/Preview/ErrorPreviewConsoleCommand.php - src/Controller/ErrorPreviewController.php - src/Middleware/Routed/Preview/ErrorPreviewAction.php - checkMissingIterableValueType: false reportUnmatchedIgnoredErrors: false treatPhpDocTypesAsCertain: false - checkGenericClassInNonGenericObjectType: false diff --git a/rector.php b/rector.php index 50a29492..3c6b9ed6 100644 --- a/rector.php +++ b/rector.php @@ -2,45 +2,30 @@ declare(strict_types=1); -use Rector\CodeQuality\Rector\Array_\CallableThisArrayToAnonymousFunctionRector; -use Rector\CodingStyle\Rector\ArrowFunction\StaticArrowFunctionRector; -use Rector\CodingStyle\Rector\Closure\StaticClosureRector; use Rector\Config\RectorConfig; use Rector\Php81\Rector\Array_\FirstClassCallableRector; -use Rector\Set\ValueObject\LevelSetList; -use Rector\Set\ValueObject\SetList; use Rector\Strict\Rector\BooleanNot\BooleanInBooleanNotRuleFixerRector; -return static function (RectorConfig $rectorConfig): void { - $rectorConfig->sets([ - LevelSetList::UP_TO_PHP_81, - SetList::CODE_QUALITY, - SetList::CODING_STYLE, - SetList::DEAD_CODE, - SetList::NAMING, - SetList::PRIVATIZATION, - SetList::TYPE_DECLARATION, - SetList::STRICT_BOOLEANS, - ]); - - $rectorConfig->parallel(); - $rectorConfig->paths([__DIR__ . '/config', __DIR__ . '/src', __DIR__ . '/spec', __FILE__]); - $rectorConfig->importNames(); - $rectorConfig->removeUnusedImports(); - $rectorConfig->skip([ +return RectorConfig::configure() + ->withPhpSets() + ->withPreparedSets( + codeQuality: true, + codingStyle: true, + deadCode: true, + naming: true, + privatization: true, + typeDeclarations: true, + strictBooleans: true + ) + ->withPaths([__DIR__ . '/config', __DIR__ . '/src', __DIR__ . '/spec']) + ->withRootFiles() + ->withImportNames(removeUnusedImports: true) + ->withSkip([ __DIR__ . '/src/Controller', __DIR__ . '/src/Command/Preview', __DIR__ . '/src/Middleware/Routed/Preview', - CallableThisArrayToAnonymousFunctionRector::class, - StaticArrowFunctionRector::class => [ - __DIR__ . '/spec', - ], - StaticClosureRector::class => [ - __DIR__ . '/spec', - ], FirstClassCallableRector::class, BooleanInBooleanNotRuleFixerRector::class => [ __DIR__ . '/src/Handler/Logging.php', ], ]); -}; diff --git a/spec/Fixture/config/module.local.php b/spec/Fixture/config/module.local.php index 1dd63af9..034e6297 100644 --- a/spec/Fixture/config/module.local.php +++ b/spec/Fixture/config/module.local.php @@ -7,7 +7,7 @@ use ErrorHeroModule\Handler\LoggingFactory; use ErrorHeroModule\Listener\Mvc; use ErrorHeroModule\Listener\MvcFactory; -use Laminas\Log\LoggerAbstractServiceFactory; +use ErrorHeroModule\Compat\LoggerAbstractServiceFactory; use Laminas\ServiceManager\Factory\InvokableFactory; return [ diff --git a/spec/Handler/LoggingFactorySpec.php b/spec/Handler/LoggingFactorySpec.php index 6ad5fa3c..f0198d95 100644 --- a/spec/Handler/LoggingFactorySpec.php +++ b/spec/Handler/LoggingFactorySpec.php @@ -2,11 +2,11 @@ namespace ErrorHeroModule\Spec\Handler; +use ErrorHeroModule\Compat\Logger; use ErrorHeroModule\Handler\Logging; use ErrorHeroModule\Handler\LoggingFactory; use Kahlan\Plugin\Double; use Laminas\Db\Adapter\AdapterInterface; -use Laminas\Log\Logger; use Laminas\Mail\Message; use Laminas\Mail\Transport\TransportInterface; use Psr\Container\ContainerInterface; diff --git a/spec/Handler/LoggingSpec.php b/spec/Handler/LoggingSpec.php index 757573cc..2320905a 100644 --- a/spec/Handler/LoggingSpec.php +++ b/spec/Handler/LoggingSpec.php @@ -3,6 +3,7 @@ namespace ErrorHeroModule\Spec\Handler; use ErrorException; +use ErrorHeroModule\Compat\Logger; use ErrorHeroModule\Handler\Logging; use Exception; use Kahlan\Plugin\Double; @@ -12,7 +13,6 @@ use Laminas\Db\Sql\Sql; use Laminas\Db\TableGateway\TableGateway; use Laminas\Http\PhpEnvironment\Request; -use Laminas\Log\Logger; use Laminas\Log\Writer\Db; use ReflectionProperty; diff --git a/spec/Listener/MvcSpec.php b/spec/Listener/MvcSpec.php index 80295b13..f4b1c977 100644 --- a/spec/Listener/MvcSpec.php +++ b/spec/Listener/MvcSpec.php @@ -4,6 +4,7 @@ use Closure; use ErrorException; +use ErrorHeroModule\Compat\Logger; use ErrorHeroModule\Handler\Logging; use ErrorHeroModule\Listener\Mvc; use Exception; @@ -12,7 +13,6 @@ use Laminas\Db\Adapter\AdapterInterface; use Laminas\EventManager\EventManagerInterface; use Laminas\Http\PhpEnvironment\Request; -use Laminas\Log\Logger; use Laminas\Log\Writer\Db; use Laminas\Mvc\MvcEvent; use Laminas\Uri\Uri; diff --git a/spec/Middleware/MezzioFactorySpec.php b/spec/Middleware/MezzioFactorySpec.php index 36fe4efe..f4be5d18 100644 --- a/spec/Middleware/MezzioFactorySpec.php +++ b/spec/Middleware/MezzioFactorySpec.php @@ -2,9 +2,6 @@ namespace ErrorHeroModule\Spec\Middleware; -use ArrayObject; -use Aura\Di\Container as AuraContainer; -use Aura\Di\ContainerBuilder as AuraContainerBuilder; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver\PDO\MySql\Driver; use Doctrine\ORM\EntityManager; @@ -26,7 +23,6 @@ given('factory', fn() : MezzioFactory => new MezzioFactory()); given('mapCreateContainers', fn() : array => [ - AuraContainer::class => (new AuraContainerBuilder())->newInstance(), SymfonyContainerBuilder::class => new SymfonyContainerBuilder(), Psr11PimpleContainer::class => new Psr11PimpleContainer(new PimpleContainer()), ]); @@ -202,15 +198,9 @@ $config = []; foreach ($this->mapCreateContainers as $containerClass => $container) { - if ($container instanceof AuraContainer) { - $config = new ArrayObject($config); - } allow($container)->toReceive('get')->with('config') ->andReturn($config); - if ($container instanceof AuraContainer) { - $config = $config->getArrayCopy(); - } allow($container)->toReceive('has')->with(EntityManager::class)->andReturn(false); @@ -242,16 +232,9 @@ foreach ($this->mapCreateContainers as $mapCreateContainer) { $config['log']['ErrorHeroModuleLogger']['writers'][0]['options']['db'] = AdapterInterface::class; - if ($mapCreateContainer instanceof AuraContainer) { - $config = new ArrayObject($config); - } allow($mapCreateContainer)->toReceive('get')->with('config') ->andReturn($config); - if ($mapCreateContainer instanceof AuraContainer) { - $config = $config->getArrayCopy(); - } - allow($mapCreateContainer)->toReceive('has')->with(EntityManager::class)->andReturn(false); $logging = Double::instance(['extends' => Logging::class, 'methods' => '__construct']); @@ -275,15 +258,9 @@ foreach ($this->mapCreateContainers as $mapCreateContainer) { $config = $this->config; $config['log']['ErrorHeroModuleLogger']['writers'][0]['options']['db'] = 'my-adapter'; - if ($mapCreateContainer instanceof AuraContainer) { - $config = new ArrayObject($config); - } allow($mapCreateContainer)->toReceive('get')->with('config') ->andReturn($config); - if ($mapCreateContainer instanceof AuraContainer) { - $config = $config->getArrayCopy(); - } allow($mapCreateContainer)->toReceive('has')->with(EntityManager::class)->andReturn(false); @@ -307,15 +284,9 @@ $config = $this->config; foreach ($this->mapCreateContainers as $mapCreateContainer) { - if ($mapCreateContainer instanceof AuraContainer) { - $config = new ArrayObject($config); - } allow($mapCreateContainer)->toReceive('get')->with('config') ->andReturn($config); - if ($mapCreateContainer instanceof AuraContainer) { - $config = $config->getArrayCopy(); - } allow($mapCreateContainer)->toReceive('has')->with(EntityManager::class)->andReturn(false); diff --git a/spec/Middleware/MezzioSpec.php b/spec/Middleware/MezzioSpec.php index ad70a737..2dd8803e 100644 --- a/spec/Middleware/MezzioSpec.php +++ b/spec/Middleware/MezzioSpec.php @@ -4,6 +4,7 @@ use Closure; use ErrorException; +use ErrorHeroModule\Compat\Logger; use ErrorHeroModule\Handler\Logging; use ErrorHeroModule\Middleware\Mezzio; use Exception; @@ -13,7 +14,6 @@ use Laminas\Diactoros\Response; use Laminas\Diactoros\ServerRequest; use Laminas\Diactoros\Uri; -use Laminas\Log\Logger; use Laminas\Log\Writer\Db; use Laminas\View\Renderer\PhpRenderer; use Laminas\View\Resolver\AggregateResolver; @@ -237,8 +237,8 @@ allow($handler)->toReceive('handle')->with($this->request)->andReturn(new Response()); $middleware = new Mezzio($config, $this->logging, $this->renderer); - $actual = $middleware->process($this->request, $handler); - expect($actual)->toBeAnInstanceOf(ResponseInterface::class); + $response = $middleware->process($this->request, $handler); + expect($response)->toBeAnInstanceOf(ResponseInterface::class); }); @@ -276,10 +276,10 @@ }); $middleware = new Mezzio($config, $logging, $this->renderer); - $actual = $middleware->process($this->request, $handler); - expect($actual)->toBeAnInstanceOf(Response::class); + $response = $middleware->process($this->request, $handler); + expect($response)->toBeAnInstanceOf(Response::class); - $content = $actual->getBody()->__toString(); + $content = $response->getBody()->__toString(); expect($content)->toContain('Error'); expect($content)->toContain('<p>We have encountered a problem and we can not fulfill your request'); @@ -335,9 +335,9 @@ }); $middleware = new Mezzio($config, $logging, null); - $actual = $middleware->process($request, $handler); - expect($actual)->toBeAnInstanceOf(Response::class); - expect($actual->getBody()->__toString())->toBe(<<<json + $response = $middleware->process($request, $handler); + expect($response)->toBeAnInstanceOf(Response::class); + expect($response->getBody()->__toString())->toBe(<<<json { "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html", "title": "Internal Server Error", @@ -372,9 +372,9 @@ }); $middleware = new Mezzio($config, $logging, $this->renderer); - $actual = $middleware->process($request, $handler); - expect($actual)->toBeAnInstanceOf(Response::class); - expect($actual->getBody()->__toString())->toBe(<<<json + $response = $middleware->process($request, $handler); + expect($response)->toBeAnInstanceOf(Response::class); + expect($response->getBody()->__toString())->toBe(<<<json { "type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html", "title": "Internal Server Error", @@ -668,7 +668,7 @@ // null means use default $handler->handle($request) expect($actual)->toBeNull(); - expect(\error_reporting())->toBe(\E_ALL | \E_STRICT); + expect(\error_reporting())->toBe(\E_ALL | 2048); expect(\ini_get('display_errors'))->toBe("0"); }); diff --git a/src/Compat/Logger.php b/src/Compat/Logger.php new file mode 100644 index 00000000..df54c177 --- /dev/null +++ b/src/Compat/Logger.php @@ -0,0 +1,740 @@ +<?php + +declare(strict_types=1); + +namespace ErrorHeroModule\Compat; + +use DateTime; +use ErrorException; +use Exception; +use Laminas\Log\Exception\InvalidArgumentException; +use Laminas\Log\Exception\RuntimeException; +use Laminas\Log\LoggerInterface; +use Laminas\Log\Processor\ProcessorInterface; +use Laminas\Log\ProcessorPluginManager; +use Laminas\Log\Writer\WriterInterface; +use Laminas\Log\WriterPluginManager; +use Laminas\ServiceManager\AbstractPluginManager; +use Laminas\ServiceManager\ServiceManager; +use Laminas\Stdlib\ArrayUtils; +use Laminas\Stdlib\SplPriorityQueue; +use Traversable; + +use function array_reverse; +use function count; +use function error_get_last; +use function error_reporting; +use function get_debug_type; +use function in_array; +use function is_array; +use function is_int; +use function is_object; +use function is_string; +use function method_exists; +use function property_exists; +use function register_shutdown_function; +use function restore_error_handler; +use function restore_exception_handler; +use function set_error_handler; +use function set_exception_handler; +use function sprintf; +use function var_export; + +use const E_COMPILE_ERROR; +use const E_COMPILE_WARNING; +use const E_CORE_ERROR; +use const E_CORE_WARNING; +use const E_DEPRECATED; +use const E_ERROR; +use const E_NOTICE; +use const E_PARSE; +use const E_RECOVERABLE_ERROR; +use const E_USER_DEPRECATED; +use const E_USER_ERROR; +use const E_USER_NOTICE; +use const E_USER_WARNING; +use const E_WARNING; + +/** + * Logging messages with a stack of backends + * + * @override \Laminas\Logger to support php 8.4 + */ +class Logger implements LoggerInterface +{ + /** + * @link http://tools.ietf.org/html/rfc3164 + * + * @const int defined from the BSD Syslog message severities + */ + public const EMERG = 0; + + public const ALERT = 1; + + public const CRIT = 2; + + public const ERR = 3; + + public const WARN = 4; + + public const NOTICE = 5; + + public const INFO = 6; + + public const DEBUG = 7; + + /** + * Map native PHP errors to priority + * + * @var array + */ + public static $errorPriorityMap = [ + E_NOTICE => self::NOTICE, + E_USER_NOTICE => self::NOTICE, + E_WARNING => self::WARN, + E_CORE_WARNING => self::WARN, + E_USER_WARNING => self::WARN, + E_ERROR => self::ERR, + E_USER_ERROR => self::ERR, + E_CORE_ERROR => self::ERR, + E_RECOVERABLE_ERROR => self::ERR, + E_PARSE => self::ERR, + E_COMPILE_ERROR => self::ERR, + E_COMPILE_WARNING => self::ERR, + // E_STRICT is deprecated in php 8.4 + 2048 => self::DEBUG, + E_DEPRECATED => self::DEBUG, + E_USER_DEPRECATED => self::DEBUG, + ]; + + /** + * Registered error handler + * + * @var bool + */ + protected static $registeredErrorHandler = false; + + /** + * Registered shutdown error handler + * + * @var bool + */ + protected static $registeredFatalErrorShutdownFunction = false; + + /** + * Registered exception handler + * + * @var bool + */ + protected static $registeredExceptionHandler = false; + + /** + * List of priority code => priority (short) name + * + * @var array + */ + protected $priorities = [ + self::EMERG => 'EMERG', + self::ALERT => 'ALERT', + self::CRIT => 'CRIT', + self::ERR => 'ERR', + self::WARN => 'WARN', + self::NOTICE => 'NOTICE', + self::INFO => 'INFO', + self::DEBUG => 'DEBUG', + ]; + + /** + * Writers + * + * @var SplPriorityQueue + */ + protected $writers; + + /** + * Processors + */ + protected SplPriorityQueue $processors; + + /** + * Writer writerPlugins + * + * @var WriterPluginManager + */ + protected $writerPlugins; + + /** + * Processor writerPlugins + * + * @var ProcessorPluginManager + */ + protected $processorPlugins; + + /** + * Constructor + * + * Set options for a logger. Accepted options are: + * - writers: array of writers to add to this logger + * - exceptionhandler: if true register this logger as exceptionhandler + * - errorhandler: if true register this logger as errorhandler + * + * @param array|Traversable $options + * @throws InvalidArgumentException + */ + public function __construct($options = null) + { + $this->writers = new SplPriorityQueue(); + $this->processors = new SplPriorityQueue(); + + if ($options instanceof Traversable) { + $options = ArrayUtils::iteratorToArray($options); + } + + if (! $options) { + return; + } + + if (! is_array($options)) { + throw new InvalidArgumentException( + 'Options must be an array or an object implementing \Traversable ' + ); + } + + // Inject writer plugin manager, if available + if ( + isset($options['writer_plugin_manager']) + && $options['writer_plugin_manager'] instanceof AbstractPluginManager + ) { + $this->setWriterPluginManager($options['writer_plugin_manager']); + } + + // Inject processor plugin manager, if available + if ( + isset($options['processor_plugin_manager']) + && $options['processor_plugin_manager'] instanceof AbstractPluginManager + ) { + $this->setProcessorPluginManager($options['processor_plugin_manager']); + } + + if (isset($options['writers']) && is_array($options['writers'])) { + foreach ($options['writers'] as $writer) { + if (! isset($writer['name'])) { + throw new InvalidArgumentException('Options must contain a name for the writer'); + } + + $priority = $writer['priority'] ?? null; + $writerOptions = $writer['options'] ?? null; + + $this->addWriter($writer['name'], $priority, $writerOptions); + } + } + + if (isset($options['processors']) && is_array($options['processors'])) { + foreach ($options['processors'] as $processor) { + if (! isset($processor['name'])) { + throw new InvalidArgumentException('Options must contain a name for the processor'); + } + + $priority = $processor['priority'] ?? null; + $processorOptions = $processor['options'] ?? null; + + $this->addProcessor($processor['name'], $priority, $processorOptions); + } + } + + if (isset($options['exceptionhandler']) && $options['exceptionhandler'] === true) { + static::registerExceptionHandler($this); + } + + if (isset($options['errorhandler']) && $options['errorhandler'] === true) { + static::registerErrorHandler($this); + } + + if (isset($options['fatal_error_shutdownfunction']) && $options['fatal_error_shutdownfunction'] === true) { + static::registerFatalErrorShutdownFunction($this); + } + } + + /** + * Shutdown all writers + * + * @return void + */ + public function __destruct() + { + foreach ($this->writers as $writer) { + try { + $writer->shutdown(); + } catch (Exception) { + } + } + } + + /** + * Get writer plugin manager + * + * @return WriterPluginManager + */ + public function getWriterPluginManager() + { + if (null === $this->writerPlugins) { + $this->setWriterPluginManager(new WriterPluginManager(new ServiceManager())); + } + + return $this->writerPlugins; + } + + /** + * Set writer plugin manager + */ + public function setWriterPluginManager(WriterPluginManager $writerPluginManager): static + { + $this->writerPlugins = $writerPluginManager; + return $this; + } + + /** + * Get writer instance + * + * @param string $name + * @return WriterInterface + */ + public function writerPlugin($name, ?array $options = null) + { + return $this->getWriterPluginManager()->get($name, $options); + } + + /** + * Add a writer to a logger + * + * @param string|WriterInterface $writer + * @param int $priority + * @throws InvalidArgumentException + */ + public function addWriter($writer, $priority = 1, ?array $options = null): static + { + if (is_string($writer)) { + $writer = $this->writerPlugin($writer, $options); + } elseif (! $writer instanceof WriterInterface) { + throw new InvalidArgumentException(sprintf( + 'Writer must implement %s\Writer\WriterInterface; received "%s"', + __NAMESPACE__, + get_debug_type($writer) + )); + } + + $this->writers->insert($writer, $priority); + + return $this; + } + + /** + * Get writers + * + * @return SplPriorityQueue + */ + public function getWriters() + { + return $this->writers; + } + + /** + * Set the writers + * + * @throws InvalidArgumentException + */ + public function setWriters(SplPriorityQueue $splPriorityQueue): static + { + foreach ($splPriorityQueue->toArray() as $writer) { + if (! $writer instanceof WriterInterface) { + throw new InvalidArgumentException( + 'Writers must be a SplPriorityQueue of Laminas\Log\Writer' + ); + } + } + + $this->writers = $splPriorityQueue; + return $this; + } + + /** + * Get processor plugin manager + * + * @return ProcessorPluginManager + */ + public function getProcessorPluginManager() + { + if (null === $this->processorPlugins) { + $this->setProcessorPluginManager(new ProcessorPluginManager(new ServiceManager())); + } + + return $this->processorPlugins; + } + + /** + * Set processor plugin manager + * + * @param string|ProcessorPluginManager $plugins + * @throws InvalidArgumentException + */ + public function setProcessorPluginManager($plugins): static + { + if (is_string($plugins)) { + $plugins = new $plugins(); + } + + if (! $plugins instanceof ProcessorPluginManager) { + throw new InvalidArgumentException(sprintf( + 'processor plugin manager must extend %s\ProcessorPluginManager; received %s', + __NAMESPACE__, + get_debug_type($plugins) + )); + } + + $this->processorPlugins = $plugins; + return $this; + } + + /** + * Get processor instance + * + * @param string $name + * @return ProcessorInterface + */ + public function processorPlugin($name, ?array $options = null) + { + return $this->getProcessorPluginManager()->get($name, $options); + } + + /** + * Add a processor to a logger + * + * @param string|ProcessorInterface $processor + * @param int $priority + * @throws InvalidArgumentException + */ + public function addProcessor($processor, $priority = 1, ?array $options = null): static + { + if (is_string($processor)) { + $processor = $this->processorPlugin($processor, $options); + } elseif (! $processor instanceof ProcessorInterface) { + throw new InvalidArgumentException(sprintf( + 'Processor must implement Laminas\Log\ProcessorInterface; received "%s"', + get_debug_type($processor) + )); + } + + $this->processors->insert($processor, $priority); + + return $this; + } + + /** + * Get processors + */ + public function getProcessors(): SplPriorityQueue + { + return $this->processors; + } + + /** + * Add a message as a log entry + * + * @param int $priority + * @param mixed $message + * @param array|Traversable $extra + * @throws InvalidArgumentException If message can't be cast to string. + * @throws InvalidArgumentException If extra can't be iterated over. + * @throws RuntimeException If no log writer specified. + */ + public function log($priority, $message, $extra = []): static + { + if (! is_int($priority) || ($priority < 0) || ($priority >= count($this->priorities))) { + throw new InvalidArgumentException(sprintf( + '$priority must be an integer >= 0 and < %d; received %s', + count($this->priorities), + var_export($priority, true) + )); + } + + if (is_object($message) && ! method_exists($message, '__toString')) { + throw new InvalidArgumentException( + '$message must implement magic __toString() method' + ); + } + + if (! is_array($extra) && ! $extra instanceof Traversable) { + throw new InvalidArgumentException( + '$extra must be an array or implement Traversable' + ); + } elseif ($extra instanceof Traversable) { + $extra = ArrayUtils::iteratorToArray($extra); + } + + if ($this->writers->count() === 0) { + throw new RuntimeException('No log writer specified'); + } + + $timestamp = new DateTime(); + + if (is_array($message)) { + $message = var_export($message, true); + } + + $event = [ + 'timestamp' => $timestamp, + 'priority' => $priority, + 'priorityName' => $this->priorities[$priority], + 'message' => (string) $message, + 'extra' => $extra, + ]; + + /** @var ProcessorInterface $processor */ + foreach ($this->processors->toArray() as $processor) { + $event = $processor->process($event); + } + + /** @var WriterInterface $writer */ + foreach ($this->writers->toArray() as $writer) { + $writer->write($event); + } + + return $this; + } + + /** + * @param string $message + * @param array|Traversable $extra + * @return Logger + */ + public function emerg($message, $extra = []) + { + return $this->log(self::EMERG, $message, $extra); + } + + /** + * @param string $message + * @param array|Traversable $extra + * @return Logger + */ + public function alert($message, $extra = []) + { + return $this->log(self::ALERT, $message, $extra); + } + + /** + * @param string $message + * @param array|Traversable $extra + * @return Logger + */ + public function crit($message, $extra = []) + { + return $this->log(self::CRIT, $message, $extra); + } + + /** + * @param string $message + * @param array|Traversable $extra + * @return Logger + */ + public function err($message, $extra = []) + { + return $this->log(self::ERR, $message, $extra); + } + + /** + * @param string $message + * @param array|Traversable $extra + * @return Logger + */ + public function warn($message, $extra = []) + { + return $this->log(self::WARN, $message, $extra); + } + + /** + * @param string $message + * @param array|Traversable $extra + * @return Logger + */ + public function notice($message, $extra = []) + { + return $this->log(self::NOTICE, $message, $extra); + } + + /** + * @param string $message + * @param array|Traversable $extra + * @return Logger + */ + public function info($message, $extra = []) + { + return $this->log(self::INFO, $message, $extra); + } + + /** + * @param string $message + * @param array|Traversable $extra + * @return Logger + */ + public function debug($message, $extra = []) + { + return $this->log(self::DEBUG, $message, $extra); + } + + /** + * Register logging system as an error handler to log PHP errors + * + * @link http://www.php.net/manual/function.set-error-handler.php + * + * @param bool $continueNativeHandler + * @return mixed Returns result of set_error_handler + */ + public static function registerErrorHandler(Logger $logger, $continueNativeHandler = false): false|callable|null + { + // Only register once per instance + if (static::$registeredErrorHandler) { + return false; + } + + $errorPriorityMap = static::$errorPriorityMap; + + $previous = set_error_handler( + function ($level, $message, $file, $line) use ($logger, $errorPriorityMap, $continueNativeHandler): bool { + $iniLevel = error_reporting(); + + if (($iniLevel & $level) !== 0) { + $priority = $errorPriorityMap[$level] ?? Logger::INFO; + + $logger->log($priority, $message, [ + 'errno' => $level, + 'file' => $file, + 'line' => $line, + ]); + } + + return ! $continueNativeHandler; + } + ); + + static::$registeredErrorHandler = true; + return $previous; + } + + /** + * Unregister error handler + */ + public static function unregisterErrorHandler(): void + { + restore_error_handler(); + static::$registeredErrorHandler = false; + } + + /** + * Register a shutdown handler to log fatal errors + * + * @link http://www.php.net/manual/function.register-shutdown-function.php + */ + public static function registerFatalErrorShutdownFunction(Logger $logger): bool + { + // Only register once per instance + if (static::$registeredFatalErrorShutdownFunction) { + return false; + } + + $errorPriorityMap = static::$errorPriorityMap; + + register_shutdown_function(function () use ($logger, $errorPriorityMap): void { + $error = error_get_last(); + + if ( + null === $error + || ! in_array( + $error['type'], + [ + E_ERROR, + E_PARSE, + E_CORE_ERROR, + E_CORE_WARNING, + E_COMPILE_ERROR, + E_COMPILE_WARNING, + ], + true + ) + ) { + return; + } + + $logger->log( + $errorPriorityMap[$error['type']], + $error['message'], + [ + 'file' => $error['file'], + 'line' => $error['line'], + ] + ); + }); + + static::$registeredFatalErrorShutdownFunction = true; + + return true; + } + + /** + * Register logging system as an exception handler to log PHP exceptions + * + * @link http://www.php.net/manual/en/function.set-exception-handler.php + */ + public static function registerExceptionHandler(Logger $logger): bool + { + // Only register once per instance + if (static::$registeredExceptionHandler) { + return false; + } + + $errorPriorityMap = static::$errorPriorityMap; + + set_exception_handler(function ($exception) use ($logger, $errorPriorityMap): void { + $logMessages = []; + + do { + $priority = Logger::ERR; + if ($exception instanceof ErrorException && isset($errorPriorityMap[$exception->getSeverity()])) { + $priority = $errorPriorityMap[$exception->getSeverity()]; + } + + $extra = [ + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'trace' => $exception->getTrace(), + ]; + if (property_exists($exception, 'xdebug_message') && $exception->xdebug_message !== null) { + $extra['xdebug'] = $exception->xdebug_message; + } + + $logMessages[] = [ + 'priority' => $priority, + 'message' => $exception->getMessage(), + 'extra' => $extra, + ]; + $exception = $exception->getPrevious(); + } while ($exception); + + foreach (array_reverse($logMessages) as $logMessage) { + $logger->log($logMessage['priority'], $logMessage['message'], $logMessage['extra']); + } + }); + + static::$registeredExceptionHandler = true; + return true; + } + + /** + * Unregister exception handler + */ + public static function unregisterExceptionHandler(): void + { + restore_exception_handler(); + static::$registeredExceptionHandler = false; + } +} diff --git a/src/Compat/LoggerAbstractServiceFactory.php b/src/Compat/LoggerAbstractServiceFactory.php new file mode 100644 index 00000000..50e655f4 --- /dev/null +++ b/src/Compat/LoggerAbstractServiceFactory.php @@ -0,0 +1,23 @@ +<?php + +declare(strict_types=1); + +namespace ErrorHeroModule\Compat; + +use Psr\Container\ContainerInterface; + +class LoggerAbstractServiceFactory extends \Laminas\Log\LoggerAbstractServiceFactory +{ + /** + * {@inheritdoc} + */ + public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null) + { + $config = $this->getConfig($container); + $config = $config[$requestedName]; + + $this->processConfig($config, $container); + + return new Logger($config); + } +} diff --git a/src/Handler/Logging.php b/src/Handler/Logging.php index b22d57f5..3502a01f 100644 --- a/src/Handler/Logging.php +++ b/src/Handler/Logging.php @@ -5,6 +5,7 @@ namespace ErrorHeroModule\Handler; use ErrorException; +use ErrorHeroModule\Compat\Logger; use ErrorHeroModule\Handler\Formatter\Json; use ErrorHeroModule\Handler\Writer\Mail; use ErrorHeroModule\HeroConstant; @@ -12,7 +13,6 @@ use Laminas\Http\Header\Cookie; use Laminas\Http\PhpEnvironment\RemoteAddress; use Laminas\Http\PhpEnvironment\Request as HttpRequest; -use Laminas\Log\Logger; use Laminas\Log\Writer\Db; use Laminas\Mail\Message; use Laminas\Mail\Transport\TransportInterface; diff --git a/src/Handler/LoggingFactory.php b/src/Handler/LoggingFactory.php index 0756b178..dda88edd 100644 --- a/src/Handler/LoggingFactory.php +++ b/src/Handler/LoggingFactory.php @@ -4,7 +4,7 @@ namespace ErrorHeroModule\Handler; -use Laminas\Log\Logger; +use ErrorHeroModule\Compat\Logger; use Laminas\Mail\Message; use Laminas\Mail\Transport\TransportInterface; use Psr\Container\ContainerInterface; diff --git a/src/HeroConstant.php b/src/HeroConstant.php index 09776371..1cda192f 100644 --- a/src/HeroConstant.php +++ b/src/HeroConstant.php @@ -13,7 +13,6 @@ use const E_NOTICE; use const E_PARSE; use const E_RECOVERABLE_ERROR; -use const E_STRICT; use const E_USER_DEPRECATED; use const E_USER_ERROR; use const E_USER_NOTICE; @@ -24,18 +23,19 @@ final class HeroConstant { /** @var array<int, string> */ public const ERROR_TYPE = [ - E_ERROR => 'E_ERROR', - E_WARNING => 'E_WARNING', - E_PARSE => 'E_PARSE', - E_NOTICE => 'E_NOTICE', - E_CORE_ERROR => 'E_CORE_ERROR', - E_CORE_WARNING => 'E_CORE_WARNING', - E_COMPILE_ERROR => 'E_COMPILE_ERROR', - E_COMPILE_WARNING => 'E_COMPILE_WARNING', - E_USER_ERROR => 'E_USER_ERROR', - E_USER_WARNING => 'E_USER_WARNING', - E_USER_NOTICE => 'E_USER_NOTICE', - E_STRICT => 'E_STRICT', + E_ERROR => 'E_ERROR', + E_WARNING => 'E_WARNING', + E_PARSE => 'E_PARSE', + E_NOTICE => 'E_NOTICE', + E_CORE_ERROR => 'E_CORE_ERROR', + E_CORE_WARNING => 'E_CORE_WARNING', + E_COMPILE_ERROR => 'E_COMPILE_ERROR', + E_COMPILE_WARNING => 'E_COMPILE_WARNING', + E_USER_ERROR => 'E_USER_ERROR', + E_USER_WARNING => 'E_USER_WARNING', + E_USER_NOTICE => 'E_USER_NOTICE', + // E_STRICT is deprecated in php 8.4 + 2048 => 'E_STRICT', E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', E_DEPRECATED => 'E_DEPRECATED', E_USER_DEPRECATED => 'E_USER_DEPRECATED', diff --git a/src/HeroTrait.php b/src/HeroTrait.php index b522241b..136c33b2 100644 --- a/src/HeroTrait.php +++ b/src/HeroTrait.php @@ -27,7 +27,6 @@ use function str_starts_with; use const E_ALL; -use const E_STRICT; trait HeroTrait { @@ -43,7 +42,7 @@ public function phpError(mixed ...$args): void } if (! $this->errorHeroModuleConfig['display-settings']['display_errors']) { - error_reporting(E_ALL | E_STRICT); + error_reporting(E_ALL | 2048); ini_set('display_errors', '0'); } diff --git a/src/Middleware/MezzioFactory.php b/src/Middleware/MezzioFactory.php index 61a61540..5007ccc5 100644 --- a/src/Middleware/MezzioFactory.php +++ b/src/Middleware/MezzioFactory.php @@ -5,10 +5,8 @@ namespace ErrorHeroModule\Middleware; use ArrayObject; -use Aura\Di\Container as AuraContainer; use Doctrine\ORM\EntityManager; use ErrorHeroModule\Handler\Logging; -use ErrorHeroModule\Transformer\AuraService; use ErrorHeroModule\Transformer\Doctrine; use ErrorHeroModule\Transformer\PimpleService; use ErrorHeroModule\Transformer\SymfonyService; @@ -29,7 +27,6 @@ final class MezzioFactory /** @var array<string, string> */ private const CONTAINERS_TRANSFORM = [ ContainerBuilder::class => SymfonyService::class, - AuraContainer::class => AuraService::class, Psr11PimpleContainer::class => PimpleService::class, ]; diff --git a/src/Transformer/AuraService.php b/src/Transformer/AuraService.php deleted file mode 100644 index 933f7443..00000000 --- a/src/Transformer/AuraService.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -declare(strict_types=1); - -namespace ErrorHeroModule\Transformer; - -use Aura\Di\Container; -use Closure; -use Psr\Container\ContainerInterface; -use Webmozart\Assert\Assert; - -final class AuraService extends TransformerAbstract implements TransformerInterface -{ - public static function transform(ContainerInterface $container, array $configuration): ContainerInterface - { - Assert::isInstanceOf($container, Container::class); - - $dbAdapterConfig = parent::getDbAdapterConfig($configuration); - $logger = parent::getLoggerInstance($configuration, $dbAdapterConfig); - - $containerLocked = &Closure::bind(static fn &($container) => $container->locked, null, $container)($container); - $containerLocked = false; - - return $container->set('ErrorHeroModuleLogger', $logger); - } -} diff --git a/src/Transformer/TransformerAbstract.php b/src/Transformer/TransformerAbstract.php index 23b9ce53..2899c541 100644 --- a/src/Transformer/TransformerAbstract.php +++ b/src/Transformer/TransformerAbstract.php @@ -4,8 +4,8 @@ namespace ErrorHeroModule\Transformer; +use ErrorHeroModule\Compat\Logger; use Laminas\Db\Adapter\Adapter; -use Laminas\Log\Logger; abstract class TransformerAbstract {