id = $id; $t->headers = self::parseHeaders($data, $t->message); foreach ($t->headers as [$k, $v]) { match ($k) { 'object' => $t->object = $v, 'type' => $t->type = $v, 'tag' => $t->tag = $v, 'tagger' => $t->tagger = Person::parse($v), default => null, }; } return $t; } public static function create(string $object, string $type, string $name, ?Person $tagger, string $message): self { $t = new self(); $t->object = $object; $t->type = $type; $t->tag = $name; $t->tagger = $tagger; $t->message = $message; $t->headers = [['object', $object], ['type', $type], ['tag', $name]]; if ($tagger !== null) $t->headers[] = ['tagger', (string)$tagger]; return $t; } public function toString(): string { return self::buildHeaders($this->headers).$this->message; } /** * ヘッダーとメッセージ(コミットとタグで同じ形) * * @return array [名前, 値]の配列 */ public static function parseHeaders(string $data, ?string &$message): array { $end = strpos($data, "\n\n"); $head = $end === false ? rtrim($data, "\n") : substr($data, 0, $end); $message = $end === false ? '' : substr($data, $end + 2); $headers = []; foreach ($head === '' ? [] : explode("\n", $head) as $line) { if ($line !== '' && $line[0] === ' ' && !empty($headers)) { // 前のヘッダーの続き(gpgsig等) $headers[count($headers) - 1][1] .= "\n".substr($line, 1); continue; } $sp = strpos($line, ' '); $headers[] = $sp === false ? [$line, ''] : [substr($line, 0, $sp), substr($line, $sp + 1)]; } return $headers; } public static function buildHeaders(array $headers): string { $out = ''; foreach ($headers as [$k, $v]) $out .= $k.' '.str_replace("\n", "\n ", $v)."\n"; return $out."\n"; } }