exact(4, true); if ($head === null) return null; if (!ctype_xdigit($head)) throw new GitException('不正なpkt-line:'.bin2hex($head)); $len = hexdec($head); if ($len < 4) return $len; if ($len > self::MAX) throw new GitException('pkt-lineが長過ぎます'); $data = $this->exact($len - 4, false); return $keepNewline ? $data : (str_ends_with($data, "\n") ? substr($data, 0, -1) : $data); } /** 次のflushまでの行 */ public function readUntilFlush(): array { $out = []; while (true) { $l = $this->read(); if ($l === null || $l === self::FLUSH) return $out; $out[] = $l; } } private function exact(int $n, bool $eofOk): ?string { $s = ''; while (strlen($s) < $n) { $chunk = fread($this->in, $n - strlen($s)); if ($chunk === false || $chunk === '') { if (feof($this->in)) { if ($s === '' && $eofOk) return null; throw new GitException('接続が途中で切れました'); } continue; } $s .= $chunk; } return $s; } /** pkt-lineにする */ public static function encode(string $data): string { if (strlen($data) + 4 > self::MAX) throw new GitException('pkt-lineが長過ぎます'); return sprintf('%04x', strlen($data) + 4).$data; } public static function flush(): string { return '0000'; } public static function delim(): string { return '0001'; } /** * サイドバンドのpkt-line(1:データ、2:進捗、3:エラー) * * @param int $max 1つの最大(side-band-64kは65520、side-bandは1000) */ public static function band(int $band, string $data, int $max = self::MAX): string { $out = ''; $step = $max - 5; for ($i = 0, $n = strlen($data); $i < $n; $i += $step) $out .= self::encode(chr($band).substr($data, $i, $step)); return $out; } }