diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3953460..521f402 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,62 +5,57 @@ on: [push] jobs: cs: name: Code Style - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.2 coverage: xdebug - name: Get Composer Cache Directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} restore-keys: ${{ runner.os }}-composer- - name: Install Dependencies run: composer install --no-progress - - name: Run php-cs-fixture + - name: Run php-cs-fixer env: PHP_CS_FIXER_FUTURE_MODE: 1 run: bin/php-cs-fixer fix --config=.php-cs-fixer.php --dry-run --no-interaction --diff phpunit: name: Unit Tests - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 env: COMPOSER_MEMORY_LIMIT: "-1" strategy: matrix: include: - # oldest supported versions - - php: 7.2 - composer_flags: "--prefer-lowest" - cs_fixer_config: "1.3.*" - symfony_phpunit_remove_return_typehint: 1 - - php: 7.3 - symfony_version: "4.4.*" - - php: 7.4 - symfony_version: "5.1.*" - - php: 7.4 - symfony_version: 5.2.* - # most recent versions - - php: 8.0 - symfony_version: 5.3.* - - php: 8.0 - symfony_version: 5.4.* - - php: 8.0 - symfony_version: 6.0.* - php: 8.1 + symfony_version: "6.4.*" + - php: 8.2 + symfony_version: "6.4.*" + - php: 8.2 + symfony_version: "7.0.*" + - php: 8.2 + symfony_version: "7.1.*" + - php: 8.3 + symfony_version: "7.1.*" + - php: 8.3 + symfony_version: "7.2.*" + - php: 8.4 + symfony_version: "7.3.*" fail-fast: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: @@ -69,9 +64,9 @@ jobs: coverage: xdebug - name: Get Composer Cache Directory id: composer-cache - run: echo "::set-output name=dir::$(composer config cache-files-dir)" + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache dependencies - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} @@ -79,15 +74,9 @@ jobs: - name: Install Symfony ${{ matrix.symfony_version }} run: composer require "symfony/framework-bundle:${{ matrix.symfony_version }}" --no-update if: matrix.symfony_version != '' - - name: Downgrade php-cs-fixer - run: composer require "m6web/php-cs-fixer-config:${{ matrix.cs_fixer_config }}" --no-update - if: matrix.cs_fixer_config != '' - name: Install Dependencies - run: composer update --prefer-dist --no-interaction --optimize-autoloader --prefer-stable --no-progress $COMPOSER_FLAGS - env: - COMPOSER_FLAGS: ${{ matrix.composer_flags }} + run: composer update --prefer-dist --no-interaction --optimize-autoloader --prefer-stable --no-progress - name: Run PHPUnit run: bin/simple-phpunit env: SYMFONY_DEPRECATIONS_HELPER: weak - SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: ${{ matrix.symfony_phpunit_remove_return_typehint }} diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 7dfbf10..c3c1a24 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -1,10 +1,32 @@ getFinder() +$finder = (PhpCsFixer\Finder::create()) ->in([ - __DIR__ + __DIR__, ]); +$config = new class() extends PhpCsFixer\Config { + public function __construct() + { + parent::__construct('customized Bedrock Streaming'); + $this->setRiskyAllowed(true); + } + + public function getRules(): array + { + // Merge base rules and disable declare_strict_types + return array_merge( + (new M6Web\CS\Config\BedrockStreaming())->getRules(), + [ + 'declare_strict_types' => false, + '@PHP81Migration' => true, + '@PSR12' => true, + 'array_syntax' => ['syntax' => 'short'], + ] + ); + } +}; + +$config->setFinder($finder); + return $config; diff --git a/Client/ClientInterface.php b/Client/ClientInterface.php index eae7f0e..d792a5f 100644 --- a/Client/ClientInterface.php +++ b/Client/ClientInterface.php @@ -12,7 +12,7 @@ public function __construct(ServerInterface $server); /** * Send metrics data to the configured server * - * @param array $lines array of string lines to send + * @param array $lines array of string lines to send */ public function sendLines(array $lines): void; } diff --git a/Client/Server.php b/Client/Server.php index 0ab6d0a..3a16e65 100644 --- a/Client/Server.php +++ b/Client/Server.php @@ -6,8 +6,8 @@ class Server implements ServerInterface { - /** @var string */ - private $name; + /** @phpstan-ignore property.onlyWritten */ + private string $name; /** @var mixed string format udp://.+ */ private $address; @@ -18,6 +18,8 @@ class Server implements ServerInterface /** * Server constructor. * + * @param array $serverConfig + * * @throws ServerException */ public function __construct(string $serverName, array $serverConfig) @@ -25,13 +27,15 @@ public function __construct(string $serverName, array $serverConfig) if ($this->checkServersConfigurations($serverName, $serverConfig)) { $this->name = $serverName; $this->address = $serverConfig['address']; - $this->port = intval($serverConfig['port']); + $this->port = (int) $serverConfig['port']; } } /** * Init the servers defined in the app configuration * + * @param array $serverConfig + * * @throws ServerException */ protected function checkServersConfigurations(string $serverName, array $serverConfig): bool @@ -40,10 +44,10 @@ protected function checkServersConfigurations(string $serverName, array $serverC throw new ServerException('No servers have been configured'); } - if (!isset($serverConfig['address']) || !isset($serverConfig['port'])) { + if (!isset($serverConfig['address'], $serverConfig['port'])) { throw new ServerException($serverName.' : no address or port in the configuration'); } - if (strpos($serverConfig['address'], 'udp://') !== 0) { + if (!str_starts_with($serverConfig['address'], 'udp://')) { throw new ServerException($serverName.' : address should begin with udp://'); } diff --git a/Client/ServerInterface.php b/Client/ServerInterface.php index a14cdab..72d34a9 100644 --- a/Client/ServerInterface.php +++ b/Client/ServerInterface.php @@ -7,6 +7,8 @@ interface ServerInterface { /** + * @param array $serverConfig + * * @throws ServerException */ public function __construct(string $serverName, array $serverConfig); diff --git a/Client/UdpClient.php b/Client/UdpClient.php index 26bc25b..2a8e200 100644 --- a/Client/UdpClient.php +++ b/Client/UdpClient.php @@ -7,11 +7,9 @@ class UdpClient implements ClientInterface /** @var int max safe size in bytes of one message to send (max official size is 65507) */ public const MAX_MESSAGE_SIZE = 64000; - /** @var ServerInterface */ - protected $server; + protected ServerInterface $server; - /** @var ?bool */ - protected $debugEnabled; + protected ?bool $debugEnabled; public function __construct(ServerInterface $server, ?bool $debugEnabled = false) { @@ -21,6 +19,8 @@ public function __construct(ServerInterface $server, ?bool $debugEnabled = false /** * Split metrics to send them group by group + * + * @param array $lines */ public function sendLines(array $lines): void { @@ -30,6 +30,9 @@ public function sendLines(array $lines): void } } + /** + * @param array $lines + */ protected function writeLines(array $lines): bool { if ($resource = @fsockopen($this->server->getAddress(), $this->server->getPort())) { diff --git a/DataCollector/StatsdDataCollector.php b/DataCollector/StatsdDataCollector.php index a3e7395..65fca81 100644 --- a/DataCollector/StatsdDataCollector.php +++ b/DataCollector/StatsdDataCollector.php @@ -4,7 +4,6 @@ use M6Web\Bundle\StatsdPrometheusBundle\Exception\MetricException; use M6Web\Bundle\StatsdPrometheusBundle\Listener\EventListener; -use M6Web\Bundle\StatsdPrometheusBundle\Metric\MetricInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DataCollector; @@ -15,7 +14,7 @@ class StatsdDataCollector extends DataCollector { /** @var EventListener[] */ - private $eventListeners; + private array $eventListeners; public function __construct() { @@ -36,7 +35,7 @@ public function reset(): void public function onKernelResponse(ResponseEvent $event): void { - if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) { + if (HttpKernelInterface::MAIN_REQUEST === $event->getRequestType()) { foreach ($this->eventListeners as $serviceId => $eventListener) { $clientInfo = [ 'name' => $serviceId, @@ -45,14 +44,12 @@ public function onKernelResponse(ResponseEvent $event): void $metricHandler = $eventListener->getMetricHandler(); foreach ($metricHandler->getMetrics() as $metric) { - if ($metric instanceof MetricInterface) { - try { - $clientInfo['operations'][] = [ - 'message' => $metricHandler->getFormattedMetric($metric), - ]; - $this->data['operations']++; - } catch (MetricException $e) { - } + try { + $clientInfo['operations'][] = [ + 'message' => $metricHandler->getFormattedMetric($metric), + ]; + $this->data['operations']++; + } catch (MetricException $e) { } } $this->data['clients'][] = $clientInfo; @@ -71,18 +68,18 @@ public function addEventListener(string $serviceId, EventListener $eventListener /** * Collect the data * - * @param Request $request The request object - * @param Response $response The response object - * @param \Throwable $exception A throwable + * @param Request $request The request object + * @param Response $response The response object + * @param \Throwable|null $exception A throwable */ - public function collect(Request $request, Response $response, \Throwable $exception = null): void + public function collect(Request $request, Response $response, ?\Throwable $exception = null): void { } /** * Return the list of statsd operations * - * @return array operations list + * @return array operations list */ public function getClients(): array { @@ -104,7 +101,7 @@ public function getOperations(): int * * @return string data collector name */ - public function getName() + public function getName(): string { return 'statsd'; } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 2c07d4e..1c39a23 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -48,7 +48,9 @@ private function addServersSection(ArrayNodeDefinition $rootNode): void ->scalarNode('address') ->isRequired() ->validate() - ->ifTrue(function ($v) {return substr($v, 0, 6) !== 'udp://'; }) + ->ifTrue(function ($v) { + return substr($v, 0, 6) !== 'udp://'; + }) ->thenInvalid("address parameter should begin with 'udp://'") ->end() ->end() @@ -187,13 +189,8 @@ private function getClientsGroupsEvents() return $eventsNode; } - private function getRootNode(TreeBuilder $treeBuilder, $name) + private function getRootNode(TreeBuilder $treeBuilder, string $name): ArrayNodeDefinition { - // BC layer for symfony/config 4.1 and older - if (!\method_exists($treeBuilder, 'getRootNode')) { - return $treeBuilder->root($name); - } - return $treeBuilder->getRootNode(); } } diff --git a/DependencyInjection/M6WebStatsdPrometheusExtension.php b/DependencyInjection/M6WebStatsdPrometheusExtension.php index 2db9158..9306fb1 100644 --- a/DependencyInjection/M6WebStatsdPrometheusExtension.php +++ b/DependencyInjection/M6WebStatsdPrometheusExtension.php @@ -23,26 +23,17 @@ class M6WebStatsdPrometheusExtension extends ConfigurableExtension { public const CONFIG_ROOT_KEY = 'm6web_statsd_prometheus'; - /** @var ContainerBuilder */ - private $container; + private ContainerBuilder $container; - /** @var string */ - private $metricsPrefix = ''; + private string $metricsPrefix = ''; - /** @var array */ - private $clientServiceIds = []; + private array $clientServiceIds = []; - /** @var array */ - private $servers; + private array $servers; - /** @var array */ - private $clients; + private array $clients; - /** @var array */ - private $tags; - - /** @var array */ - private $dispatchedEvents; + private array $dispatchedEvents; public function loadInternal(array $config, ContainerBuilder $container): void { @@ -51,14 +42,14 @@ public function loadInternal(array $config, ContainerBuilder $container): void $this->metricsPrefix = $config['metrics']['prefix'] ?? ''; $this->servers = $config['servers'] ?? []; $this->clients = $config['clients'] ?? []; - $this->tags = $config['tags'] ?? []; + $tags = $config['tags'] ?? []; $this->dispatchedEvents = $config['dispatched_events']; foreach ($this->clients as $alias => $clientConfig) { $this->clientServiceIds[] = $this->setEventListenerAsServiceAndGetServiceId( $alias, $clientConfig, - $this->tags + $tags ); } @@ -245,6 +236,7 @@ protected function getClientServerDefinition(string $clientName, string $serverN if (!\array_key_exists($serverName, $this->servers)) { throw new InvalidConfigurationException(sprintf('M6WebStatsd client %s used server %s which is not defined in the servers section', $clientName, $serverName)); } + // Matched server configurations. return new Definition(Server::class, [ $serverName, diff --git a/Event/AbstractMonitoringEvent.php b/Event/AbstractMonitoringEvent.php index ecfc35e..92a9ce2 100644 --- a/Event/AbstractMonitoringEvent.php +++ b/Event/AbstractMonitoringEvent.php @@ -6,13 +6,13 @@ abstract class AbstractMonitoringEvent extends Event implements MonitoringEventInterface { - /** @var array */ - protected $parameters; + /** @var array */ + protected array $parameters; /** * AbstractMonitoringEvent constructor. * - * @param array $parameters parameters can contain metrics values, tags values or/and custom param values + * @param array $parameters parameters can contain metrics values, tags values or/and custom param values * * @see https://github.com/M6Web/StatsdPrometheusBundle/blob/master/Doc/usage.md */ diff --git a/Event/Console/ConsoleCommandMonitoringEvent.php b/Event/Console/ConsoleCommandMonitoringEvent.php index 79264ac..947995e 100644 --- a/Event/Console/ConsoleCommandMonitoringEvent.php +++ b/Event/Console/ConsoleCommandMonitoringEvent.php @@ -6,7 +6,7 @@ class ConsoleCommandMonitoringEvent extends AbstractMonitoringEvent { - public static function fromFacade(ConsoleMonitoringEventFacade $facade): ConsoleCommandMonitoringEvent + public static function fromFacade(ConsoleMonitoringEventFacade $facade): self { return new self($facade->toMonitoringArray()); } diff --git a/Event/Console/ConsoleErrorMonitoringEvent.php b/Event/Console/ConsoleErrorMonitoringEvent.php index a27406b..a5bf973 100644 --- a/Event/Console/ConsoleErrorMonitoringEvent.php +++ b/Event/Console/ConsoleErrorMonitoringEvent.php @@ -6,7 +6,7 @@ class ConsoleErrorMonitoringEvent extends AbstractMonitoringEvent { - public static function fromFacade(ConsoleMonitoringEventFacade $facade): ConsoleErrorMonitoringEvent + public static function fromFacade(ConsoleMonitoringEventFacade $facade): self { return new self($facade->toMonitoringArray()); } diff --git a/Event/Console/ConsoleExceptionMonitoringEvent.php b/Event/Console/ConsoleExceptionMonitoringEvent.php index e72657b..fa07eeb 100644 --- a/Event/Console/ConsoleExceptionMonitoringEvent.php +++ b/Event/Console/ConsoleExceptionMonitoringEvent.php @@ -6,7 +6,7 @@ class ConsoleExceptionMonitoringEvent extends AbstractMonitoringEvent { - public static function fromFacade(ConsoleMonitoringEventFacade $facade): ConsoleExceptionMonitoringEvent + public static function fromFacade(ConsoleMonitoringEventFacade $facade): self { return new self($facade->toMonitoringArray()); } diff --git a/Event/Console/ConsoleMonitoringEventFacade.php b/Event/Console/ConsoleMonitoringEventFacade.php index c9ca2f6..7b25c3b 100644 --- a/Event/Console/ConsoleMonitoringEventFacade.php +++ b/Event/Console/ConsoleMonitoringEventFacade.php @@ -22,7 +22,7 @@ public function __construct( ?float $executionTime, int $memoryPeakInBytes, ?string $commandName, - ?ConsoleEvent $originalEvent = null + ?ConsoleEvent $originalEvent = null, ) { $this->startTime = $startTime; $this->executionTime = $executionTime; @@ -31,7 +31,7 @@ public function __construct( $this->originalEvent = $originalEvent; } - public static function fromEvent(ConsoleEvent $event, ?float $startTime): ConsoleMonitoringEventFacade + public static function fromEvent(ConsoleEvent $event, ?float $startTime): self { return new self( $startTime, @@ -42,6 +42,9 @@ public static function fromEvent(ConsoleEvent $event, ?float $startTime): Consol ); } + /** + * @return array + */ public function toMonitoringArray(): array { return [ diff --git a/Event/Console/ConsoleTerminateMonitoringEvent.php b/Event/Console/ConsoleTerminateMonitoringEvent.php index 70380e2..5356021 100644 --- a/Event/Console/ConsoleTerminateMonitoringEvent.php +++ b/Event/Console/ConsoleTerminateMonitoringEvent.php @@ -6,7 +6,7 @@ class ConsoleTerminateMonitoringEvent extends AbstractMonitoringEvent { - public static function fromFacade(ConsoleMonitoringEventFacade $facade): ConsoleTerminateMonitoringEvent + public static function fromFacade(ConsoleMonitoringEventFacade $facade): self { return new self($facade->toMonitoringArray()); } diff --git a/Event/Kernel/KernelTerminateMonitoringEvent.php b/Event/Kernel/KernelTerminateMonitoringEvent.php index 9036080..0b2d171 100644 --- a/Event/Kernel/KernelTerminateMonitoringEvent.php +++ b/Event/Kernel/KernelTerminateMonitoringEvent.php @@ -7,13 +7,13 @@ class KernelTerminateMonitoringEvent extends AbstractMonitoringEvent { - public static function createFromKernelTerminateEvent(TerminateEvent $event): KernelTerminateMonitoringEvent + public static function createFromKernelTerminateEvent(TerminateEvent $event): self { return new self([ 'host' => $event->getRequest()->getHost(), 'method' => $event->getRequest()->getMethod(), 'memory' => memory_get_peak_usage(true), - 'route' => $event->getRequest()->get('_route', 'undefined'), + 'route' => $event->getRequest()->attributes->get('_route', 'undefined'), 'status' => $event->getResponse()->getStatusCode(), 'timing' => microtime(true) - $event->getRequest()->server->get('REQUEST_TIME_FLOAT'), // The original event is sent as a parameter, just in case diff --git a/Listener/ConsoleEventsSubscriber.php b/Listener/ConsoleEventsSubscriber.php index 397f820..bb80384 100644 --- a/Listener/ConsoleEventsSubscriber.php +++ b/Listener/ConsoleEventsSubscriber.php @@ -15,17 +15,12 @@ class ConsoleEventsSubscriber implements EventSubscriberInterface { - /** @var EventDispatcherInterface */ - protected $eventDispatcher = null; + protected EventDispatcherInterface $eventDispatcher; - /** - * Time when command started - * - * @var float - */ - protected $startTime = null; + /** Time when command started, used to compute command duration in the end of the command */ + protected ?float $startTime = null; - public static function getSubscribedEvents() + public static function getSubscribedEvents(): array { return [ ConsoleEvents::COMMAND => 'onCommand', @@ -51,7 +46,7 @@ public function onCommand(ConsoleEvent $event): void public function onTerminate(ConsoleTerminateEvent $event): void { - if ($event->getExitCode() != 0) { + if ($event->getExitCode() !== 0) { // For non-0 exit command, fire an ERROR event $this->eventDispatcher->dispatch( ConsoleErrorMonitoringEvent::fromFacade( diff --git a/Listener/EventListener.php b/Listener/EventListener.php index ebe7540..7cf2a03 100644 --- a/Listener/EventListener.php +++ b/Listener/EventListener.php @@ -11,14 +11,12 @@ class EventListener { - /** @var array */ - protected $listenedEvents = []; + /** @var array */ + protected array $listenedEvents = []; - /** @var PropertyAccess\PropertyAccessorInterface */ - protected $propertyAccessor; + protected PropertyAccess\PropertyAccessorInterface $propertyAccessor; - /** @var MetricHandler */ - protected $metricHandler; + protected MetricHandler $metricHandler; public function __construct(MetricHandler $metricHandler) { @@ -57,8 +55,7 @@ public function onKernelResponse(ResponseEvent $event): void // We only need the master request in order to keep all the original request headers // This will be used to resolve advanced configuration tags. // such as '@=request.get('queryParam')' - $isMainRequest = method_exists($event, 'isMainRequest') ? $event->isMainRequest() : $event->isMasterRequest(); - if ($isMainRequest) { + if ($event->isMainRequest()) { $this->metricHandler->setRequest($event->getRequest()); } } @@ -73,6 +70,9 @@ public function onConsoleTerminate(ConsoleTerminateEvent $event): void $this->metricHandler->sendMetrics(); } + /** + * @param array $eventConfig + */ public function addEventToListen(string $eventName, array $eventConfig): self { $this->listenedEvents[$eventName] = $eventConfig; diff --git a/Listener/KernelEventsSubscriber.php b/Listener/KernelEventsSubscriber.php index 9730a60..40669a9 100644 --- a/Listener/KernelEventsSubscriber.php +++ b/Listener/KernelEventsSubscriber.php @@ -14,14 +14,13 @@ class KernelEventsSubscriber implements EventSubscriberInterface { - /** @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; - /** @var array */ - private $routesForWhichKernelTerminateEventWontBeDispatched; + /** @var array */ + private array $routesForWhichKernelTerminateEventWontBeDispatched; - /** @var array */ - private $routesForWhichKernelExceptionEventWontBeDispatched; + /** @var array */ + private array $routesForWhichKernelExceptionEventWontBeDispatched; public static function getSubscribedEvents(): array { @@ -31,10 +30,14 @@ public static function getSubscribedEvents(): array ]; } + /** + * @param array $routesForWhichKernelTerminateEventWontBeDispatched + * @param array $routesForWhichKernelExceptionEventWontBeDispatched + */ public function __construct( EventDispatcherInterface $dispatcher, array $routesForWhichKernelTerminateEventWontBeDispatched, - array $routesForWhichKernelExceptionEventWontBeDispatched + array $routesForWhichKernelExceptionEventWontBeDispatched, ) { $this->dispatcher = $dispatcher; $this->routesForWhichKernelTerminateEventWontBeDispatched = $routesForWhichKernelTerminateEventWontBeDispatched; @@ -54,9 +57,8 @@ public function onKernelTerminate(TerminateEvent $event): void public function onKernelException(ExceptionEvent $event): void { - $isMainRequest = method_exists($event, 'isMainRequest') ? $event->isMainRequest() : $event->isMasterRequest(); if ( - !$isMainRequest + !$event->isMainRequest() || \in_array($event->getRequest()->attributes->get('_route'), $this->routesForWhichKernelExceptionEventWontBeDispatched, true) ) { return; diff --git a/M6WebStatsdPrometheusBundle.php b/M6WebStatsdPrometheusBundle.php index df929db..1150715 100644 --- a/M6WebStatsdPrometheusBundle.php +++ b/M6WebStatsdPrometheusBundle.php @@ -30,5 +30,7 @@ public function getContainerExtension(): ?ExtensionInterface if ($this->extension) { return $this->extension; } + + return null; } } diff --git a/Makefile b/Makefile index 747d3fd..bf42b2c 100644 --- a/Makefile +++ b/Makefile @@ -71,14 +71,13 @@ test: phpunit .PHONY: phpunit phpunit: $(call printSection,PHPUNIT) - ${BIN_DIR}/simple-phpunit + ${BIN_DIR}/phpunit vendor/bin/.phpunit: phpunit .PHONY: phpstan -phpstan: vendor/bin/.phpunit - ${BIN_DIR}/phpstan.phar analyse - +phpstan: + ${BIN_DIR}/phpstan.phar analyse --memory-limit=-1 # QUALITY .PHONY: cs cs: diff --git a/Metric/Metric.php b/Metric/Metric.php index cbe49a4..9b04781 100644 --- a/Metric/Metric.php +++ b/Metric/Metric.php @@ -38,10 +38,10 @@ class Metric implements MetricInterface /** @var string */ private $paramValue; - /** @var array */ + /** @var array */ private $configurationTags = []; - /** @var array */ + /** @var array */ private $tags; /** @var ExpressionLanguage */ @@ -50,7 +50,8 @@ class Metric implements MetricInterface /** * Metric constructor. * - * @param object $event + * @param object $event + * @param array $metricConfig */ public function __construct($event, array $metricConfig = []) { @@ -77,14 +78,12 @@ public function getResolvedName(): string // We want to get all the matching results with the 1st parenthesis in the Regex. // $placeholders[0] will give the entire string with matching pattern // $placeholders[1] will give only matching pattern results: that's what we want - if (isset($placeholders[1])) { - try { - $resolvedName = $this->resolvePlaceholdersInMetricName($resolvedName, $placeholders[1]); - } catch (\Exception $e) { - // We try to throw only MetricExceptions to shut bad configurations exceptions - // We consider that we are supposed to know what we do (professional power). - throw new MetricException($e->getMessage()); - } + try { + $resolvedName = $this->resolvePlaceholdersInMetricName($resolvedName, $placeholders[1]); + } catch (\Exception $e) { + // We try to throw only MetricExceptions to shut bad configurations exceptions + // We consider that we are supposed to know what we do (professional power). + throw new MetricException($e->getMessage()); } } @@ -131,6 +130,11 @@ public function getResolvedType(): string throw new MetricException('This metric type is not handled'); } + /** + * @param array $resolvers + * + * @return array + */ public function getResolvedTags(array $resolvers = []): array { $resolvedTags = []; @@ -138,7 +142,7 @@ public function getResolvedTags(array $resolvers = []): array // Add global parameters (configured in client or group) foreach (array_merge($this->configurationTags, $this->tags) as $tagName => $tagValue) { $resolvedTag = $this->resolveTagValue( - // By default (~), we look for the parameter with the same name as the tag. + // By default, (~), we look for the parameter with the same name as the tag. !is_null($tagValue) ? $tagValue : self::TAG_PARAMETER_KEY.$tagName, $resolvers ); @@ -151,6 +155,9 @@ public function getResolvedTags(array $resolvers = []): array return $resolvedTags; } + /** + * @param array $placeholders + */ private function resolvePlaceholdersInMetricName(string $metricName, array $placeholders): string { foreach ($placeholders as $placeholder) { @@ -167,18 +174,21 @@ private function resolvePlaceholdersInMetricName(string $metricName, array $plac return $metricName; } + /** + * @param array $resolvers + */ private function resolveTagValue(string $valueToResolve, array $resolvers): ?string { switch (true) { - case strpos($valueToResolve, self::TAG_SERVICE_RESOLUTION) === 0: + case str_starts_with($valueToResolve, self::TAG_SERVICE_RESOLUTION): return $this->expressionLanguage->evaluate(substr($valueToResolve, strlen(self::TAG_SERVICE_RESOLUTION)), $resolvers); - case strpos($valueToResolve, self::TAG_PROPERTY_ACCESSOR) === 0: + case str_starts_with($valueToResolve, self::TAG_PROPERTY_ACCESSOR): try { return $this->propertyAccessor->getValue($this->event, substr($valueToResolve, strlen(self::TAG_PROPERTY_ACCESSOR))); } catch (\Exception $e) { return null; } - case strpos($valueToResolve, self::TAG_PARAMETER_KEY) === 0: + case str_starts_with($valueToResolve, self::TAG_PARAMETER_KEY): $parameter = substr($valueToResolve, strlen(self::TAG_PARAMETER_KEY)); if (!$this->event instanceof MonitoringEventInterface) { return null; diff --git a/Metric/MetricHandler.php b/Metric/MetricHandler.php index 84421f6..0a23262 100644 --- a/Metric/MetricHandler.php +++ b/Metric/MetricHandler.php @@ -20,7 +20,7 @@ class MetricHandler /** @var Request|null */ protected $request; - /** @var \SplQueue */ + /** @var \SplQueue */ protected $metrics; /** @var int */ @@ -58,8 +58,8 @@ public function hasToSendMetrics(): bool public function isMaxNumberOfMetricsReached(): bool { return - !empty($this->maxNumberOfMetricToQueue) && - ($this->getMetrics()->count() >= $this->maxNumberOfMetricToQueue); + !empty($this->maxNumberOfMetricToQueue) + && ($this->getMetrics()->count() >= $this->maxNumberOfMetricToQueue); } public function sendMetrics(): bool @@ -101,6 +101,9 @@ public function setRequest(Request $request): void $this->request = $request; } + /** + * @param \SplQueue $queue + */ public function setMetricsQueue(\SplQueue $queue): void { $this->metrics = $queue; @@ -113,6 +116,9 @@ public function setMaxNumberOfMetricToQueue(int $maxNumberOfMetricToQueue): self return $this; } + /** + * @return \SplQueue + */ public function getMetrics(): \SplQueue { return $this->metrics; @@ -144,16 +150,16 @@ protected function clearMetricsQueue(): self /** * Format data to send to the server + * + * @return array */ protected function getMetricsAsArray(): array { $metrics = []; foreach ($this->getMetrics() as $metric) { - if ($metric instanceof MetricInterface) { - try { - $metrics[] = $this->getFormattedMetric($metric); - } catch (MetricException $e) { - } + try { + $metrics[] = $this->getFormattedMetric($metric); + } catch (MetricException $e) { } } @@ -181,6 +187,9 @@ public function getFormattedMetric(MetricInterface $metric): string ]); } + /** + * @param array $data + */ protected function getFormattedMetricFromData(array $data): string { return str_replace(array_keys($data), array_values($data), self::METRIC_FORMAT); @@ -188,6 +197,8 @@ protected function getFormattedMetricFromData(array $data): string /** * Format metric tags on format "|#tag1:value1,tag2:value2,tag3:value3" + * + * @param array $tags */ protected function formatTagsInline(array $tags): string { diff --git a/Metric/MetricInterface.php b/Metric/MetricInterface.php index 9c90023..352564f 100644 --- a/Metric/MetricInterface.php +++ b/Metric/MetricInterface.php @@ -22,10 +22,12 @@ public function getResolvedValue(): string; public function getResolvedType(): string; /** - * @param array $resolvers an associative array of resolvers - * ['resolver1' => $resolver1] - * Used to inject services in tag names: - * format: '@=my_service.myFunction()' + * @param array $resolvers an associative array of resolvers + * ['resolver1' => $resolver1] + * Used to inject services in tag names: + * format: '@=my_service.myFunction()' + * + * @return array */ public function getResolvedTags(array $resolvers): array; } diff --git a/Tests/DependencyInjection/M6WebStatsdPrometheusExtensionTest.php b/Tests/DependencyInjection/M6WebStatsdPrometheusExtensionTest.php index bcccddc..4f6089d 100644 --- a/Tests/DependencyInjection/M6WebStatsdPrometheusExtensionTest.php +++ b/Tests/DependencyInjection/M6WebStatsdPrometheusExtensionTest.php @@ -3,24 +3,24 @@ namespace M6Web\Bundle\StatsdPrometheusBundle\Tests\DependencyInjection; use M6Web\Bundle\StatsdPrometheusBundle\DependencyInjection\M6WebStatsdPrometheusExtension; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Yaml\Yaml; -class M6WebStatsdPrometheusExtensionTest extends TestCase +final class M6WebStatsdPrometheusExtensionTest extends TestCase { - use \Symfony\Component\VarDumper\Test\VarDumperTestTrait; + private ContainerBuilder $container; - /** @var ContainerBuilder */ - private $container; - - /** @var M6WebStatsdPrometheusExtension */ - private $extension; + private M6WebStatsdPrometheusExtension $extension; /** - * @dataProvider dataProviderForGetServersReturnsExpectation + * @param array $config + * @param array $expected */ + #[DataProvider('dataProviderForGetServersReturnsExpectation')] public function testGetServersReturnsExpectation(array $config, array $expected): void { // -- When -- @@ -30,8 +30,10 @@ public function testGetServersReturnsExpectation(array $config, array $expected) } /** - * @dataProvider dataProviderForGetClientsReturnsExpectation + * @param array $config + * @param array $expected */ + #[DataProvider('dataProviderForGetClientsReturnsExpectation')] public function testGetClientsReturnsExpectation(array $config, array $expected): void { // -- When -- @@ -40,9 +42,7 @@ public function testGetClientsReturnsExpectation(array $config, array $expected) $this->assertEquals($expected, $this->extension->getClients()); } - /** - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] public function testLoadCorrectTagsConfigurationDoesNoesNotThrowException(): void { // -- Given -- @@ -95,7 +95,10 @@ public function testLoadWrongYmlConfigurationFileThrowsException(): void $this->extension->load([$config[M6WebStatsdPrometheusExtension::CONFIG_ROOT_KEY]], $this->container); } - public function dataProviderForGetServersReturnsExpectation(): array + /** + * @return array + */ + public static function dataProviderForGetServersReturnsExpectation(): array { return [ 'test1' => [ @@ -119,7 +122,10 @@ public function dataProviderForGetServersReturnsExpectation(): array ]; } - public function dataProviderForGetClientsReturnsExpectation(): array + /** + * @return array + */ + public static function dataProviderForGetClientsReturnsExpectation(): array { return [ 'test1' => [ diff --git a/Tests/Fixtures/CustomEventTest.php b/Tests/Fixtures/CustomEventTest.php index fc03750..a0a6552 100644 --- a/Tests/Fixtures/CustomEventTest.php +++ b/Tests/Fixtures/CustomEventTest.php @@ -15,7 +15,7 @@ class CustomEventTest extends Event /** @var string|null */ private $placeHolder2; - public function __construct(?float $value, string $placeHolder1 = null, string $placeHolder2 = null) + public function __construct(?float $value, ?string $placeHolder1 = null, ?string $placeHolder2 = null) { $this->value = $value; $this->placeHolder1 = $placeHolder1; diff --git a/Tests/Metric/MetricHandlerTest.php b/Tests/Metric/MetricHandlerTest.php index 9bf6520..4251079 100644 --- a/Tests/Metric/MetricHandlerTest.php +++ b/Tests/Metric/MetricHandlerTest.php @@ -6,16 +6,20 @@ use M6Web\Bundle\StatsdPrometheusBundle\Client\UdpClient; use M6Web\Bundle\StatsdPrometheusBundle\Metric\Metric; use M6Web\Bundle\StatsdPrometheusBundle\Metric\MetricHandler; +use M6Web\Bundle\StatsdPrometheusBundle\Metric\MetricInterface; use M6Web\Bundle\StatsdPrometheusBundle\Tests\Fixtures\CustomEventTest; use M6Web\Bundle\StatsdPrometheusBundle\Tests\TestMonitoringEvent; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\KernelEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Kernel; use Symfony\Contracts\EventDispatcher\Event; -class MetricHandlerTest extends TestCase +final class MetricHandlerTest extends TestCase { public function testGetMetricsReturnsExpectedWhenAddMetric(): void { @@ -214,10 +218,10 @@ public function testHasToSendMetricsReturnsFalseWhenFlushMetricsQueueIsFalse(): } /** - * @dataProvider getDataEventsWithFormattedMetrics - * - * @param Event|KernelEvent $event + * @param Event|KernelEvent $event + * @param array $metricConfig */ + #[DataProvider('getDataEventsWithFormattedMetrics')] public function testGetFormattedMetricsReturnsExpected($event, Request $masterRequest, array $metricConfig, string $expectedResult): void { // -- Given -- @@ -228,7 +232,10 @@ public function testGetFormattedMetricsReturnsExpected($event, Request $masterRe $this->assertSame($expectedResult, $metricHandler->getFormattedMetric($metric)); } - protected function getMetricHandlerObject(ClientInterface $client = null, \SplQueue $metricsQueue = null): MetricHandler + /** + * @param \SplQueue|null $metricsQueue + */ + protected function getMetricHandlerObject(?ClientInterface $client = null, ?\SplQueue $metricsQueue = null): MetricHandler { $metricHandler = new MetricHandler(); if ($client) { @@ -242,7 +249,7 @@ protected function getMetricHandlerObject(ClientInterface $client = null, \SplQu } /** - * @return \PHPUnit\Framework\MockObject\MockObject|\SplQueue + * @return MockObject&\SplQueue */ private function getMetricsQueueMock(bool $isEmpty, int $count = 0) { @@ -256,17 +263,22 @@ private function getMetricsQueueMock(bool $isEmpty, int $count = 0) } /** - * @return UdpClient|\PHPUnit\Framework\MockObject\MockObject + * @return UdpClient&MockObject */ private function getUdpClientMock() { return $this->createMock(UdpClient::class); } - public function getDataEventsWithFormattedMetrics(): array + /** + * @return array + */ + public static function getDataEventsWithFormattedMetrics(): array { $defaultRequest = new Request([], ['country' => 'fr']); + $stubKernel = self::createStub(HttpKernelInterface::class); + return [ // Increment: object Event (no tags) [ @@ -284,7 +296,7 @@ public function getDataEventsWithFormattedMetrics(): array // Increment: object Event (computed configuration tag with unknown value) [ 'event' => new KernelEvent( - $this->getMockBuilder(Kernel::class)->disableOriginalConstructor()->getMock(), + $stubKernel, new Request([], ['country' => 'be']), HttpKernelInterface::SUB_REQUEST ), @@ -293,7 +305,7 @@ public function getDataEventsWithFormattedMetrics(): array 'type' => 'increment', 'name' => 'http.status.200', 'configurationTags' => [ - 'IamUnknown' => '@=request ? request.get("IamUnknown", "unknown") : "unknown"', + 'IamUnknown' => '@=request ? request.request.get("IamUnknown", "unknown") : "unknown"', ], 'tags' => [], ], @@ -303,7 +315,7 @@ public function getDataEventsWithFormattedMetrics(): array // Increment: object Event (computed configuration tag) [ 'event' => new KernelEvent( - $this->getMockBuilder(Kernel::class)->disableOriginalConstructor()->getMock(), + $stubKernel, new Request([], ['country' => 'be']), HttpKernelInterface::SUB_REQUEST ), @@ -312,7 +324,7 @@ public function getDataEventsWithFormattedMetrics(): array 'type' => 'increment', 'name' => 'http.status.200', 'configurationTags' => [ - 'country' => '@=request ? request.get("country", "unknown") : "unknown"', + 'country' => '@=request ? request.request.get("country", "unknown") : "unknown"', ], 'tags' => [], ], diff --git a/Tests/Metric/MetricTest.php b/Tests/Metric/MetricTest.php index a8ee7ba..e4bee42 100644 --- a/Tests/Metric/MetricTest.php +++ b/Tests/Metric/MetricTest.php @@ -5,16 +5,18 @@ use M6Web\Bundle\StatsdPrometheusBundle\Metric\Metric; use M6Web\Bundle\StatsdPrometheusBundle\Tests\Fixtures\CustomEventTest; use M6Web\Bundle\StatsdPrometheusBundle\Tests\TestMonitoringEvent; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Contracts\EventDispatcher\Event; -class MetricTest extends TestCase +final class MetricTest extends TestCase { /** - * @dataProvider dataProviderGetMetricsName + * @param array $metricConfig */ + #[DataProvider('dataProviderGetMetricsName')] public function testGetResolvedNameReturnsExpected(Event $event, array $metricConfig, string $expectedResult): void { // -- Given -- @@ -23,7 +25,10 @@ public function testGetResolvedNameReturnsExpected(Event $event, array $metricCo $this->assertSame($expectedResult, $metric->getResolvedName()); } - public function dataProviderGetMetricsName(): array + /** + * @return array + */ + public static function dataProviderGetMetricsName(): array { return [ [ @@ -50,8 +55,9 @@ public function dataProviderGetMetricsName(): array } /** - * @dataProvider dataProviderGetMetricsType + * @param array $metricConfig */ + #[DataProvider('dataProviderGetMetricsType')] public function testGetResolvedTypeReturnsExpected(Event $event, array $metricConfig, string $expectedResult): void { // -- Given -- @@ -60,7 +66,10 @@ public function testGetResolvedTypeReturnsExpected(Event $event, array $metricCo $this->assertSame($expectedResult, $metric->getResolvedType()); } - public function dataProviderGetMetricsType(): array + /** + * @return array + */ + public static function dataProviderGetMetricsType(): array { return [ [ @@ -107,8 +116,9 @@ public function dataProviderGetMetricsType(): array } /** - * @dataProvider dataProviderGetMetricsValue + * @param array $metricConfig */ + #[DataProvider('dataProviderGetMetricsValue')] public function testGetResolvedValueReturnsExpected(Event $event, array $metricConfig, string $expectedResult): void { // -- Given --TestMonitoringEvent @@ -117,7 +127,10 @@ public function testGetResolvedValueReturnsExpected(Event $event, array $metricC $this->assertSame($expectedResult, $metric->getResolvedValue()); } - public function dataProviderGetMetricsValue(): array + /** + * @return array + */ + public static function dataProviderGetMetricsValue(): array { return [ 'increment value' => [ @@ -175,7 +188,7 @@ public function dataProviderGetMetricsValue(): array '12045465', ], 'custom value from object' => [ - new class() extends Event { + new class () extends Event { public function getCustomValue(): int { return 10; @@ -191,7 +204,7 @@ public function getCustomValue(): int '10', ], 'custom value from object corrected by 1000' => [ - new class() extends Event { + new class () extends Event { public function getCustomValue(): float { return 10.002; @@ -218,8 +231,11 @@ public function getCustomValue(): float '0', ], 'custom value from object return null' => [ - new class() extends Event { - public function getCustomValue(): ?int + new class () extends Event { + /** + * @return null + */ + public function getCustomValue() { return null; } @@ -237,8 +253,11 @@ public function getCustomValue(): ?int } /** - * @dataProvider dataProviderGetMetricsTag + * @param array $metricConfig + * @param array $resolvers + * @param array $expectedResult */ + #[DataProvider('dataProviderGetMetricsTag')] public function testGetResolvedTagsReturnsExpected(Event $event, array $metricConfig, array $resolvers, array $expectedResult): void { // -- Given -- @@ -247,7 +266,7 @@ public function testGetResolvedTagsReturnsExpected(Event $event, array $metricCo $this->assertSame($expectedResult, $metric->getResolvedTags($resolvers)); } - public function dataProviderGetMetricsTag(): \Generator + public static function dataProviderGetMetricsTag(): \Generator { $resolvers = []; diff --git a/composer.json b/composer.json index dc305b5..0e2d676 100644 --- a/composer.json +++ b/composer.json @@ -17,24 +17,25 @@ "sort-packages": true }, "require": { - "php": "^7.2|^8.0", + "php": "^8.1", "psr/container": "^1.0 || ^2.0", "psr/event-dispatcher": "^1.0", - "symfony/contracts": "^1.1 || ^2.0 || ^3.0", - "symfony/expression-language": "^4.4 || ^5.0 || ^6.0", - "symfony/framework-bundle": "^4.4 || ^5.0 || ^6.0", - "symfony/property-access": "^4.4 || ^5.0 || ^6.0", - "symfony/yaml": "^4.4 || ^5.0 || ^6.0" + "symfony/contracts": "^3.0", + "symfony/expression-language": "^6.4 || ^7.0", + "symfony/framework-bundle": "^6.4 || ^7.0", + "symfony/property-access": "^6.4 || ^7.0", + "symfony/yaml": "^6.4 || ^7.0" }, "require-dev": { "m6web/php-cs-fixer-config": "^2.0", - "phpstan/phpstan": "0.12.*", - "phpstan/phpstan-phpunit": "0.12.*", - "symfony/phpunit-bridge": "5.1 || ^6.0" + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^10.0", + "phpstan/phpstan-phpunit": "^2.0", + "symfony/phpunit-bridge": "^6.4 || ^7.0" }, "suggest": { - "symfony/console": "^4.3", - "symfony/http-kernel": "^4.3" + "symfony/console": "^6.4 || ^7.0", + "symfony/http-kernel": "^6.4 || ^7.0" }, "autoload": { "psr-4": { "M6Web\\Bundle\\StatsdPrometheusBundle\\": "" }, diff --git a/phpstan.dist.neon b/phpstan.dist.neon new file mode 100644 index 0000000..e7ade7e --- /dev/null +++ b/phpstan.dist.neon @@ -0,0 +1,16 @@ +includes: + - 'vendor/phpstan/phpstan-phpunit/extension.neon' + - 'vendor/phpstan/phpstan-phpunit/rules.neon' + +parameters: + bootstrapFiles: + - vendor/autoload.php + level: 8 + paths: + - 'Client' + - 'DataCollector' + - 'Event' + - 'Exception' + - 'Listener' + - 'Metric' + - 'Tests' diff --git a/phpstan.neon.dist b/phpstan.neon.dist deleted file mode 100644 index fdaae65..0000000 --- a/phpstan.neon.dist +++ /dev/null @@ -1,23 +0,0 @@ -includes: - - 'vendor/phpstan/phpstan-phpunit/extension.neon' - - 'vendor/phpstan/phpstan-phpunit/rules.neon' - -parameters: - bootstrapFiles: - - vendor/bin/.phpunit/phpunit/vendor/autoload.php - checkGenericClassInNonGenericObjectType: false - checkMissingIterableValueType: false - level: 'max' - paths: - - 'Client' - - 'DataCollector' - - 'Event' - - 'Exception' - - 'Listener' - - 'Metric' - - 'Tests' - ignoreErrors: - # Symfony 4 compatibility code is seen as an error on symfony 6 - - message: '#Call to an undefined method Symfony\\Component\\HttpKernel\\Event\\[A-Za-z]+Event::isMasterRequest\(\)\.#' - path: Listener/* - count: 2 diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 92bb9f2..057b4a7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,19 +1,21 @@ - + ./Tests + ./Tests/Fixtures - - + + . - - ./Tests - ./vendor - - - + + + ./Tests + ./vendor + +