<?php
 declare(strict_types=1); namespace SpomkyLabs\Pki\X509\Certificate; use ArrayIterator; use Countable; use IteratorAggregate; use SpomkyLabs\Pki\CryptoEncoding\PEM; use SpomkyLabs\Pki\CryptoEncoding\PEMBundle; use function count; final class CertificateBundle implements Countable, IteratorAggregate { private array $certs; private ?array $keyIdMap = null; private function __construct(Certificate ...$certs) { $this->certs = $certs; } public function __clone() { $this->keyIdMap = null; } public static function create(Certificate ...$certs): self { return new self(...$certs); } public static function fromPEMs(PEM ...$pems): self { $certs = array_map(Certificate::fromPEM(...), $pems); return self::create(...$certs); } public static function fromPEMBundle(PEMBundle $pem_bundle): self { return self::fromPEMs(...$pem_bundle->all()); } public function withCertificates(Certificate ...$cert): self { $obj = clone $this; $obj->certs = array_merge($obj->certs, $cert); return $obj; } public function withPEMBundle(PEMBundle $pem_bundle): self { $certs = $this->certs; foreach ($pem_bundle as $pem) { $certs[] = Certificate::fromPEM($pem); } return self::create(...$certs); } public function withPEM(PEM $pem): self { $certs = $this->certs; $certs[] = Certificate::fromPEM($pem); return self::create(...$certs); } public function contains(Certificate $cert): bool { $id = self::_getCertKeyId($cert); $map = $this->_getKeyIdMap(); if (! isset($map[$id])) { return false; } foreach ($map[$id] as $c) { if ($cert->equals($c)) { return true; } } return false; } public function allBySubjectKeyIdentifier(string $id): array { $map = $this->_getKeyIdMap(); if (! isset($map[$id])) { return []; } return $map[$id]; } public function all(): array { return $this->certs; } public function count(): int { return count($this->certs); } public function getIterator(): ArrayIterator { return new ArrayIterator($this->certs); } private function _getKeyIdMap(): array { if (! isset($this->keyIdMap)) { $this->keyIdMap = []; foreach ($this->certs as $cert) { $id = self::_getCertKeyId($cert); if (! isset($this->keyIdMap[$id])) { $this->keyIdMap[$id] = []; } array_push($this->keyIdMap[$id], $cert); } } return $this->keyIdMap; } private static function _getCertKeyId(Certificate $cert): string { $exts = $cert->tbsCertificate() ->extensions(); if ($exts->hasSubjectKeyIdentifier()) { return $exts->subjectKeyIdentifier() ->keyIdentifier(); } return $cert->tbsCertificate() ->subjectPublicKeyInfo() ->keyIdentifier(); } } 