$data The policy array `Generators\RobotsGenerator` takes, * plus `owner`, `contact`, `license_label`, `license_url` * and `links` (`context`, `summary`, `humans`, `security`). * @return string File body, `\n` separated, with a trailing newline. */ public function generate( array $data ): string { $blocks = [ $this->header( $data ), ( new RobotsGenerator() )->generate_groups( $data ), $this->content_signal_line( $data ), $this->footer( $data ), ]; return implode( "\n\n", array_filter( $blocks, static fn( string $block ): bool => '' !== $block ) ) . "\n"; } /** * The comment block that opens the file. * * @param array $data Input array. * @return string Header comment lines. */ private function header( array $data ): string { $site_name = $this->line( $data['site_name'] ?? '' ); $title = '# AI crawler policy'; if ( '' !== $site_name ) { $title .= ' for ' . $site_name; } return implode( "\n", [ $title, self::CANONICAL_NOTE, self::MARKER ] ); } /** * The `Content-Signal` directive, serialized exactly as robots.txt serializes it. * * @param array $data Input array. * @return string Line, or an empty string when there is no signal. */ private function content_signal_line( array $data ): string { $signal = RobotsGenerator::content_signal_line( is_array( $data['content_signal'] ?? null ) ? $data['content_signal'] : [] ); return '' === $signal ? '' : 'Content-Signal: ' . $signal; } /** * The comment block that closes the file: who owns the content and where to read more. * * @param array $data Input array. * @return string Comment lines, or an empty string when nothing is known. */ private function footer( array $data ): string { $links = is_array( $data['links'] ?? null ) ? $data['links'] : []; $lines = []; foreach ( [ 'Owner' => $data['owner'] ?? '', 'Contact' => $data['contact'] ?? '', 'License' => $this->license( $data ), 'Context' => $links['context'] ?? '', 'Summary' => $links['summary'] ?? '', 'Humans' => $links['humans'] ?? '', 'Security' => $links['security'] ?? '', ] as $name => $value ) { $value = $this->line( $value ); if ( '' !== $value ) { $lines[] = '# ' . $name . ': ' . $value; } } return implode( "\n", $lines ); } /** * The content licence, as a label followed by its URL. * * A custom licence is stored as a URL and its label is that same URL, so the * two are only joined when they differ. * * @param array $data Input array. * @return string Licence, empty when none is set. */ private function license( array $data ): string { $label = $this->line( $data['license_label'] ?? '' ); $url = $this->line( $data['license_url'] ?? '' ); if ( '' === $url || $url === $label ) { return $label; } return '' !== $label ? $label . ' — ' . $url : $url; } /** * A value as a single trimmed line. * * @param mixed $value Raw value. * @return string Line, empty when the value is not text. */ private function line( $value ): string { if ( ! is_scalar( $value ) ) { return ''; } return trim( (string) preg_replace( '/\s+/', ' ', (string) $value ) ); } }