diff --git a/CHANGELOG.md b/CHANGELOG.md index c1dc023f..96128f47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] - Add forward compatibility with Symfony 5 (#235, thanks to @garak) + - Fix Hub initialization for `ErrorListener` (#243, thanks to @teohhanhui) - Fix compatibility with sentry/sentry 2.2+ (#244) + - Add support for `class_serializers` option (#245) ## 3.1.0 - 2019-07-02 - Add support for Symfony 2.8 (#233, thanks to @nocive) diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 6f25af07..288fa59c 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -57,6 +57,11 @@ public function getConfigTreeBuilder(): TreeBuilder if (PrettyVersions::getVersion('sentry/sentry')->getPrettyVersion() !== '2.0.0') { $optionsChildNodes->booleanNode('capture_silenced_errors'); } + if ($this->classSerializersAreSupported()) { + $optionsChildNodes->arrayNode('class_serializers') + ->defaultValue([]) + ->prototype('scalar'); + } $optionsChildNodes->integerNode('context_lines') ->min(0) ->max(99); @@ -80,13 +85,13 @@ public function getConfigTreeBuilder(): TreeBuilder ->prototype('scalar') ->validate() ->ifTrue(function ($value): bool { - if (! is_string($value)) { + if (! is_string($value) && '' != $value) { return true; } - return '@' !== substr($value, 0, 1); + return '@' !== $value[0]; }) - ->thenInvalid('Expecting service reference, got %s'); + ->thenInvalid('Expecting service reference, got "%s"'); $optionsChildNodes->scalarNode('logger'); $optionsChildNodes->integerNode('max_breadcrumbs') ->min(1); @@ -163,4 +168,15 @@ private function isNotAValidCallback(): \Closure return true; }; } + + private function classSerializersAreSupported(): bool + { + try { + new Options(['class_serializers' => []]); + + return true; + } catch (\Throwable $throwable) { + return false; + } + } } diff --git a/src/DependencyInjection/SentryExtension.php b/src/DependencyInjection/SentryExtension.php index 751387e5..fbb52b77 100644 --- a/src/DependencyInjection/SentryExtension.php +++ b/src/DependencyInjection/SentryExtension.php @@ -14,7 +14,6 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\Console\ConsoleEvents; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Loader; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\DependencyInjection\Extension; @@ -97,11 +96,22 @@ private function passConfigurationToOptions(ContainerBuilder $container, array $ } if (\array_key_exists('before_send', $processedOptions)) { - $this->mapCallableValue($options, 'setBeforeSendCallback', $processedOptions['before_send']); + $beforeSendCallable = $this->valueToCallable($processedOptions['before_send']); + $options->addMethodCall('setBeforeSendCallback', [$beforeSendCallable]); } if (\array_key_exists('before_breadcrumb', $processedOptions)) { - $this->mapCallableValue($options, 'setBeforeBreadcrumbCallback', $processedOptions['before_breadcrumb']); + $beforeBreadcrumbCallable = $this->valueToCallable($processedOptions['before_breadcrumb']); + $options->addMethodCall('setBeforeBreadcrumbCallback', [$beforeBreadcrumbCallable]); + } + + if (\array_key_exists('class_serializers', $processedOptions)) { + $classSerializers = []; + foreach ($processedOptions['class_serializers'] as $class => $serializer) { + $classSerializers[$class] = $this->valueToCallable($serializer); + } + + $options->addMethodCall('setClassSerializers', [$classSerializers]); } if (\array_key_exists('integrations', $processedOptions)) { @@ -114,20 +124,13 @@ private function passConfigurationToOptions(ContainerBuilder $container, array $ } } - /** - * @param Definition $options - * @param string $method - * @param callable|string $optionValue - */ - private function mapCallableValue(Definition $options, string $method, $optionValue): void + private function valueToCallable($value) { - if (is_string($optionValue) && 0 === strpos($optionValue, '@')) { - $beforeSend = new Reference(substr($optionValue, 1)); - } else { - $beforeSend = $optionValue; + if (is_string($value) && 0 === strpos($value, '@')) { + return new Reference(substr($value, 1)); } - $options->addMethodCall($method, [$beforeSend]); + return $value; } /** diff --git a/src/EventListener/ConsoleListener.php b/src/EventListener/ConsoleListener.php index 71972214..4d47dc8c 100644 --- a/src/EventListener/ConsoleListener.php +++ b/src/EventListener/ConsoleListener.php @@ -3,7 +3,6 @@ namespace Sentry\SentryBundle\EventListener; use Sentry\SentryBundle\SentryBundle; -use Sentry\State\Hub; use Sentry\State\HubInterface; use Sentry\State\Scope; use Symfony\Component\Console\Event\ConsoleCommandEvent; diff --git a/src/EventListener/RequestListener.php b/src/EventListener/RequestListener.php index 06a2a2be..8c36b669 100644 --- a/src/EventListener/RequestListener.php +++ b/src/EventListener/RequestListener.php @@ -3,7 +3,6 @@ namespace Sentry\SentryBundle\EventListener; use Sentry\SentryBundle\SentryBundle; -use Sentry\State\Hub; use Sentry\State\HubInterface; use Sentry\State\Scope; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; diff --git a/test/BaseTestCase.php b/test/BaseTestCase.php new file mode 100644 index 00000000..4b4cbbb6 --- /dev/null +++ b/test/BaseTestCase.php @@ -0,0 +1,32 @@ + []]); + + return true; + } catch (\Throwable $throwable) { + return false; + } + } + + protected function getSupportedOptionsCount(): int + { + if ($this->classSerializersAreSupported()) { + return ConfigurationTest::SUPPORTED_SENTRY_OPTIONS_COUNT + 1; + } + + return ConfigurationTest::SUPPORTED_SENTRY_OPTIONS_COUNT; + } +} diff --git a/test/DependencyInjection/ConfigurationTest.php b/test/DependencyInjection/ConfigurationTest.php index 783a2740..8305c4b8 100644 --- a/test/DependencyInjection/ConfigurationTest.php +++ b/test/DependencyInjection/ConfigurationTest.php @@ -3,23 +3,21 @@ namespace Sentry\SentryBundle\Test\DependencyInjection; use Jean85\PrettyVersions; -use PHPUnit\Framework\TestCase; use Sentry\Options; use Sentry\SentryBundle\DependencyInjection\Configuration; +use Sentry\SentryBundle\Test\BaseTestCase; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\Definition\Processor; use Symfony\Component\HttpKernel\Kernel; -class ConfigurationTest extends TestCase +class ConfigurationTest extends BaseTestCase { - public const SUPPORTED_SENTRY_OPTIONS_COUNT = 23; - public function testDataProviderIsMappingTheRightNumberOfOptions(): void { $providerData = $this->optionValuesProvider(); $supportedOptions = \array_unique(\array_column($providerData, 0)); - $expectedCount = self::SUPPORTED_SENTRY_OPTIONS_COUNT; + $expectedCount = $this->getSupportedOptionsCount(); if (PrettyVersions::getVersion('sentry/sentry')->getPrettyVersion() !== '2.0.0') { ++$expectedCount; @@ -38,7 +36,7 @@ public function testInvalidDataProviderIsMappingTheRightNumberOfOptions(): void $supportedOptions = \array_unique(\array_column($providerData, 0)); $this->assertCount( - self::SUPPORTED_SENTRY_OPTIONS_COUNT, + $this->getSupportedOptionsCount(), $supportedOptions, 'Provider for invalid configuration options mismatch: ' . PHP_EOL . print_r($supportedOptions, true) ); @@ -76,6 +74,10 @@ public function testConfigurationDefaults(): void $expectedDefaults['options']['in_app_exclude'][1] = '%kernel.project_dir%/vendor'; } + if ($this->classSerializersAreSupported()) { + $expectedDefaults['options']['class_serializers'] = []; + } + $this->assertEquals($expectedDefaults, $processed); $this->assertArrayNotHasKey('server_name', $processed['options'], 'server_name has to be fetched at runtime, not before (see #181)'); } @@ -127,6 +129,10 @@ public function optionValuesProvider(): array $options[] = ['capture_silenced_errors', true]; } + if ($this->classSerializersAreSupported()) { + $options[] = ['class_serializers', ['count']]; + } + return $options; } @@ -144,7 +150,7 @@ public function testInvalidValues(string $option, $value): void public function invalidValuesProvider(): array { - return [ + $values = [ ['attach_stacktrace', 'string'], ['before_breadcrumb', 'this is not a callable'], ['before_breadcrumb', [$this, 'is not a callable']], @@ -184,6 +190,15 @@ public function invalidValuesProvider(): array ['server_name', []], ['tags', 'invalid-unmapped-tag'], ]; + + if ($this->classSerializersAreSupported()) { + $values[] = ['class_serializers', 'this is not a callable']; + $values[] = ['class_serializers', [$this, 'is not a callable']]; + $values[] = ['class_serializers', false]; + $values[] = ['class_serializers', -1]; + } + + return $values; } private function processConfiguration(array $values): array diff --git a/test/DependencyInjection/SentryExtensionTest.php b/test/DependencyInjection/SentryExtensionTest.php index 26d0bd42..c4625512 100644 --- a/test/DependencyInjection/SentryExtensionTest.php +++ b/test/DependencyInjection/SentryExtensionTest.php @@ -3,12 +3,12 @@ namespace Sentry\SentryBundle\Test\DependencyInjection; use Jean85\PrettyVersions; -use PHPUnit\Framework\TestCase; use Sentry\Breadcrumb; use Sentry\Event; use Sentry\Integration\IntegrationInterface; use Sentry\Options; use Sentry\SentryBundle\DependencyInjection\SentryExtension; +use Sentry\SentryBundle\Test\BaseTestCase; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -17,7 +17,7 @@ use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Kernel; -class SentryExtensionTest extends TestCase +class SentryExtensionTest extends BaseTestCase { private const OPTIONS_TEST_PUBLIC_ALIAS = 'sentry.options.public_alias'; @@ -27,10 +27,10 @@ public function testDataProviderIsMappingTheRightNumberOfOptions(): void $supportedOptions = \array_unique(\array_column($providerData, 0)); // subtracted one is `integration`, which cannot be tested with the provider - $expectedCount = ConfigurationTest::SUPPORTED_SENTRY_OPTIONS_COUNT - 1; + $expectedCount = $this->getSupportedOptionsCount(); - if (PrettyVersions::getVersion('sentry/sentry')->getPrettyVersion() !== '2.0.0') { - ++$expectedCount; + if (PrettyVersions::getVersion('sentry/sentry')->getPrettyVersion() === '2.0.0') { + --$expectedCount; } $this->assertCount( @@ -138,6 +138,16 @@ public function optionsValueProvider(): array $options[] = ['capture_silenced_errors', true, 'shouldCaptureSilencedErrors']; } + if ($this->classSerializersAreSupported()) { + $options['class_serializer'] = [ + 'class_serializers', + [ + self::class => __NAMESPACE__ . '\mockClassSerializer', + ], + 'getClassSerializers', + ]; + } + return $options; } @@ -375,6 +385,11 @@ function mockBeforeBreadcrumb(Breadcrumb $breadcrumb): ?Breadcrumb return null; } +function mockClassSerializer($object) +{ + return ['value' => 'serialized_class']; +} + class CallbackMock { public static function callback()