* SPDX-License-Identifier: AGPL-3.0-or-later */ namespace KTXF\Mail\Service; /** * Mail Service Location Implementation * * Unified implementation supporting URI-based and socket-based connections. * * @since 2025.05.01 */ class ServiceLocation implements IServiceLocation { /** * @param string $type Location type (uri, socket-single, socket-split) * @param string|null $uri URI for API-based services * @param string|null $inboundHost Inbound/primary host * @param int|null $inboundPort Inbound/primary port * @param string|null $inboundSecurity Inbound security (none, ssl, tls, starttls) * @param string|null $outboundHost Outbound host (for split-socket) * @param int|null $outboundPort Outbound port (for split-socket) * @param string|null $outboundSecurity Outbound security (for split-socket) */ public function __construct( private string $type, private ?string $uri = null, private ?string $inboundHost = null, private ?int $inboundPort = null, private ?string $inboundSecurity = null, private ?string $outboundHost = null, private ?int $outboundPort = null, private ?string $outboundSecurity = null, ) {} /** * Creates a URI-based location (for API services) * * @since 2025.05.01 * * @param string $uri * * @return self */ public static function uri(string $uri): self { return new self(self::TYPE_URI, $uri); } /** * Creates a single-socket location (e.g., SMTP only) * * @since 2025.05.01 * * @param string $host * @param int $port * @param string $security * * @return self */ public static function socket(string $host, int $port, string $security = self::SECURITY_TLS): self { return new self( self::TYPE_SOCKET_SINGLE, null, $host, $port, $security ); } /** * Creates a split-socket location (IMAP + SMTP) * * @since 2025.05.01 * * @param string $inboundHost IMAP host * @param int $inboundPort IMAP port * @param string $inboundSecurity IMAP security * @param string $outboundHost SMTP host * @param int $outboundPort SMTP port * @param string $outboundSecurity SMTP security * * @return self */ public static function splitSocket( string $inboundHost, int $inboundPort, string $inboundSecurity, string $outboundHost, int $outboundPort, string $outboundSecurity ): self { return new self( self::TYPE_SOCKET_SPLIT, null, $inboundHost, $inboundPort, $inboundSecurity, $outboundHost, $outboundPort, $outboundSecurity ); } /** * Creates from array data * * @since 2025.05.01 * * @param array $data * * @return self */ public static function fromArray(array $data): self { return new self( $data['type'] ?? self::TYPE_SOCKET_SINGLE, $data['uri'] ?? null, $data['inboundHost'] ?? $data['host'] ?? null, $data['inboundPort'] ?? $data['port'] ?? null, $data['inboundSecurity'] ?? $data['security'] ?? null, $data['outboundHost'] ?? null, $data['outboundPort'] ?? null, $data['outboundSecurity'] ?? null, ); } /** * @inheritDoc */ public function getType(): string { return $this->type; } /** * @inheritDoc */ public function getUri(): ?string { return $this->uri; } /** * @inheritDoc */ public function getInboundHost(): ?string { return $this->inboundHost; } /** * @inheritDoc */ public function getInboundPort(): ?int { return $this->inboundPort; } /** * @inheritDoc */ public function getInboundSecurity(): ?string { return $this->inboundSecurity; } /** * @inheritDoc */ public function getOutboundHost(): ?string { return $this->outboundHost ?? $this->inboundHost; } /** * @inheritDoc */ public function getOutboundPort(): ?int { return $this->outboundPort ?? $this->inboundPort; } /** * @inheritDoc */ public function getOutboundSecurity(): ?string { return $this->outboundSecurity ?? $this->inboundSecurity; } /** * @inheritDoc */ public function jsonSerialize(): array { $data = ['type' => $this->type]; if ($this->type === self::TYPE_URI) { $data['uri'] = $this->uri; } else { $data['inboundHost'] = $this->inboundHost; $data['inboundPort'] = $this->inboundPort; $data['inboundSecurity'] = $this->inboundSecurity; if ($this->type === self::TYPE_SOCKET_SPLIT) { $data['outboundHost'] = $this->outboundHost; $data['outboundPort'] = $this->outboundPort; $data['outboundSecurity'] = $this->outboundSecurity; } } return array_filter($data, fn($v) => $v !== null); } }