'/(?:\$this->operationRun|\$operationRun|\$opRun)\s*->\s*update\s*\(\s*\[(?:(?!\)\s*;).)*?(?:[\'"]status[\'"]|[\'"]outcome[\'"])\s*=>/s', 'direct fill()/forceFill() with status/outcome' => '/(?:\$this->operationRun|\$operationRun|\$opRun)\s*->\s*(?:fill|forceFill)\s*\(\s*\[(?:(?!\)\s*;).)*?(?:[\'"]status[\'"]|[\'"]outcome[\'"])\s*=>/s', 'direct property assignment' => '/(?:\$this->operationRun|\$operationRun|\$opRun)\s*->\s*(?:status|outcome)\s*=(?!=)/', 'OperationRun query/bulk update with status/outcome' => '/OperationRun::(?:(?!;).){0,800}?->\s*update\s*\(\s*\[(?:(?!\)\s*;).)*?(?:[\'"]status[\'"]|[\'"]outcome[\'"])\s*=>/s', ]; $violations = []; foreach ($files as $file) { $source = SourceFileScanner::read($file); foreach ($patterns as $label => $pattern) { if (! preg_match_all($pattern, $source, $matches, PREG_OFFSET_CAPTURE)) { continue; } foreach ($matches[0] as [$snippetMatch, $offset]) { if (! is_int($offset)) { continue; } $line = substr_count(substr($source, 0, $offset), "\n") + 1; $violations[] = [ 'file' => SourceFileScanner::relativePath($file), 'line' => $line, 'label' => $label, 'snippet' => SourceFileScanner::snippet($source, $line), 'match' => trim((string) $snippetMatch), ]; } } } if ($violations !== []) { $messages = array_map(static function (array $violation): string { return sprintf( "%s:%d [%s]\n%s", $violation['file'], $violation['line'], $violation['label'], $violation['snippet'], ); }, $violations); $this->fail( "Forbidden direct OperationRun status/outcome transition(s) found outside OperationRunService:\n\n" .implode("\n\n", $messages) ); } expect($violations)->toBe([]); })->group('ops-ux');