$context * @param array $extra */ protected function logEntry( int|string|Level $level = Level::Warning, string|Stringable $message = "test", array $context = [], string $channel = "test", DateTimeImmutable $datetime = new JsonSerializableDateTimeImmutable(true), array $extra = [], ): LogRecord { return new LogRecord( datetime: $datetime, channel: $channel, level: Logger::toMonologLevel($level), message: (string) $message, context: $context, extra: $extra, ); } protected function getReflection( object|string $object, string $methodName = '', ): ReflectionMethod { if (($methodName === '' || $methodName === '0') && is_string($object)) { $method = new ReflectionMethod($object); } else { $method = new ReflectionMethod($object, $methodName); } return $method; } /** * Returns a reflection of the given class. * * @api * @noinspection PhpUnused */ protected function noOperation(): void { // This method intentionally left blank. // It can be used to indicate a no-operation in tests. } /** * Create a LogRecord with simplified parameters. * * @param array $context * @param array $extra */ protected function createLogRecord( string $message = TestConstants::MESSAGE_DEFAULT, array $context = [], Level $level = Level::Info, string $channel = 'test', ?DateTimeImmutable $datetime = null, array $extra = [] ): LogRecord { return new LogRecord( datetime: $datetime ?? new DateTimeImmutable(), channel: $channel, level: $level, message: $message, context: $context, extra: $extra ); } /** * Create a GdprProcessor with common defaults. * * @param array $patterns * @param array $fieldPaths * @param array $customCallbacks * @param array $dataTypeMasks * @param array $conditionalRules */ protected function createProcessor( array $patterns = [], array $fieldPaths = [], array $customCallbacks = [], ?callable $auditLogger = null, int $maxDepth = 100, array $dataTypeMasks = [], array $conditionalRules = [] ): GdprProcessor { return new GdprProcessor( $patterns, $fieldPaths, $customCallbacks, $auditLogger, $maxDepth, $dataTypeMasks, $conditionalRules ); } /** * Create a GdprProcessor with default patterns. * * @param array $fieldPaths * @param array $customCallbacks */ protected function createProcessorWithDefaults( array $fieldPaths = [], array $customCallbacks = [] ): GdprProcessor { return new GdprProcessor( DefaultPatterns::get(), $fieldPaths, $customCallbacks ); } /** * Create an audit logger that stores calls in an array. * * @param array $storage * * @psalm-return \Closure(string, mixed, mixed):void */ protected function createAuditLogger(array &$storage): \Closure { return function (string $path, mixed $original, mixed $masked) use (&$storage): void { $storage[] = [ 'path' => $path, 'original' => $original, TestConstants::DATA_MASKED => $masked, ]; }; } /** * Clear RateLimiter state for clean tests. */ protected function clearRateLimiter(): void { RateLimiter::clearAll(); } /** * Clear PatternValidator cache for clean tests. */ protected function clearPatternCache(): void { /** @psalm-suppress DeprecatedMethod - Test helper for deprecated cache API */ PatternValidator::clearCache(); } /** * Get common test pattern for email masking. * * @return array */ protected function getEmailPattern(): array { return [TestConstants::PATTERN_EMAIL_FULL => MaskConstants::MASK_EMAIL]; } /** * Get common test pattern for SSN masking. * * @return array */ protected function getSsnPattern(): array { return ['/\b\d{3}-\d{2}-\d{4}\b/' => MaskConstants::MASK_SSN]; } /** * Get common test pattern for credit card masking. * * @return array */ protected function getCreditCardPattern(): array { return ['/\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/' => MaskConstants::MASK_CARD]; } /** * Get all common test patterns. * * @return string[] */ protected function getCommonPatterns(): array { return array_merge( $this->getEmailPattern(), $this->getSsnPattern(), $this->getCreditCardPattern() ); } /** * Assert that a message contains masked value and not original. */ protected function assertMasked( string $maskedValue, string $originalValue, string $actualMessage ): void { $this->assertStringContainsString($maskedValue, $actualMessage); $this->assertStringNotContainsString($originalValue, $actualMessage); } /** * Measure execution time and memory of a callable. * * @return array{duration_ms: float, memory_kb: float, result: mixed} */ protected function measurePerformance(callable $callable): array { $startTime = microtime(true); $startMemory = memory_get_usage(true); $result = $callable(); $endTime = microtime(true); $endMemory = memory_get_usage(true); return [ 'duration_ms' => ($endTime - $startTime) * 1000.0, 'memory_kb' => ((float) $endMemory - (float) $startMemory) / 1024.0, 'result' => $result, ]; } }