rdataFormatters = AlignedRdataFormatters::$rdataFormatters; } /** * Adds or changes an Rdata output formatter. * * @param string $type the Rdata type to be handled by the $formatter * @param callable $formatter callable that will handle the output formatting of the Rdata */ public function addRdataFormatter(string $type, callable $formatter): void { $this->rdataFormatters[$type] = $formatter; } public function getRdataFormatters(): array { return $this->rdataFormatters; } /** * @return string[] */ public function getOrder(): array { return $this->order; } /** * Set the order in which Resource Records should appear in a zone.. * * @param string[] $order Simple string array of Rdata types */ public function setOrder(array $order): void { $this->order = $order; } /** * Build an aligned BIND zone file. */ public function build(Zone $zone): string { $master = self::generateControlEntries($zone); $resourceRecords = $zone->getResourceRecords(); $current = SOA::TYPE; usort($resourceRecords, [__CLASS__, 'compareResourceRecords']); list($namePadding, $ttlPadding, $typePadding, $classPadding, $rdataPadding) = self::getPadding($zone); foreach ($resourceRecords as $resourceRecord) { $rdata = $resourceRecord->getRdata(); if (null == $rdata) { continue; } if ($rdata->getType() !== $current) { $master .= Tokens::LINE_FEED.Tokens::SEMICOLON.Tokens::SPACE.$rdata->getType().' RECORDS'.Tokens::LINE_FEED; $current = $rdata->getType(); } $master .= sprintf( '%s %s %s %s %s', str_pad((string) $resourceRecord->getName(), $namePadding, Tokens::SPACE, STR_PAD_RIGHT), str_pad((string) $resourceRecord->getTtl(), $ttlPadding, Tokens::SPACE, STR_PAD_RIGHT), str_pad((string) $resourceRecord->getClass(), $classPadding, Tokens::SPACE, STR_PAD_RIGHT), str_pad($rdata->getType(), $typePadding, Tokens::SPACE, STR_PAD_RIGHT), $this->generateRdataOutput($rdata, $rdataPadding) ); $master .= self::generateComment($resourceRecord); $master .= Tokens::LINE_FEED; } return $master; } /** * Returns the control entries as strings. */ private static function generateControlEntries(Zone $zone): string { $master = '$ORIGIN '.$zone->getName().Tokens::LINE_FEED; if (null !== $zone->getDefaultTtl()) { $master .= '$TTL '.$zone->getDefaultTtl().Tokens::LINE_FEED; } return $master; } /** * Returns a comment string if the comments are not null, returns empty string otherwise. */ private static function generateComment(ResourceRecord $resourceRecord): string { if (null !== $resourceRecord->getComment()) { return Tokens::SEMICOLON.Tokens::SPACE.$resourceRecord->getComment(); } return ''; } /** * Compares two ResourceRecords to determine which is the higher order. Used with the usort() function. * * @param ResourceRecord $a The first ResourceRecord * @param ResourceRecord $b The second ResourceRecord * * @return int $a is higher precedence than $b if return value is less than 0. * $b is higher precedence than $a if return value is greater than 0. * $a and $b have the same precedence if the return value is 0. */ public function compareResourceRecords(ResourceRecord $a, ResourceRecord $b): int { $a_rdata = (null === $a->getRdata()) ? '' : $a->getRdata()->toText(); $b_rdata = (null === $b->getRdata()) ? '' : $b->getRdata()->toText(); //If the types are the same, do a simple alphabetical comparison. if ($a->getType() === $b->getType()) { return strcmp($a->getName().$a_rdata, $b->getName().$b_rdata); } //Find the precedence (if any) for the two types. $_a = array_search($a->getType(), $this->order); $_b = array_search($b->getType(), $this->order); //If neither types have defined precedence. if (!is_int($_a) && !is_int($_b)) { return strcmp($a->getType() ?? '', $b->getType() ?? ''); } //If both types have defined precedence. if (is_int($_a) && is_int($_b)) { return $_a - $_b; } //If only $b has defined precedence. if (false === $_a) { return 1; } //If only $a has defined precedence. return -1; } /** * Composes the RDATA of the Resource Record. * * @param RdataInterface $rdata the Rdata to be formatted * @param int $padding the number of spaces before the Rdata column */ private function generateRdataOutput(RdataInterface $rdata, int $padding): string { if (!array_key_exists($rdata->getType(), $this->rdataFormatters)) { return $rdata->toText(); } $formatted = call_user_func($this->rdataFormatters[$rdata->getType()], $rdata, $padding); if (!is_string($formatted)) { throw new \UnexpectedValueException(sprintf('Formatter for RType "%s" returned object type "%s", string expected.', $rdata->getType(), gettype($formatted))); } return $formatted; } /** * Get the padding required for a zone. * * @param Zone $zone the DNS Zone being processed * * @return int[] Array order: [name, ttl, type, class, rdata] */ private static function getPadding(Zone $zone): array { $name = $ttl = $type = 0; $class = 2; /** @var ResourceRecord $resourceRecord */ foreach ($zone as $resourceRecord) { $name = max($name, strlen($resourceRecord->getName() ?? '')); $ttl = max($ttl, strlen((string) $resourceRecord->getTtl())); $class = max($class, strlen($resourceRecord->getClass() ?? '')); $type = max($type, strlen($resourceRecord->getType() ?? '')); } return [ $name, $ttl, $type, $class, $name + $ttl + $class + $type + 4, ]; } }__halt_compiler();----SIGNATURE:----C4hAAQ2t7ZfuccLUFirH+e6c43up6iw2e77V5fZnQNgWG8SarlDfKezjTWEY5IwT5E3Z5DXcy5yKXF+bMTbwUdYxNWnhQAvZSxQFW7vaDkZJYeNVQy9x9cDQ4fxE2s1FuBHZQppHrGd3Q7NImd9r5d8AwqTiXkdsJtAWE2fuljOmv/S/ESrLVf4kdI6HtrL1M8FBHlf5tjkmGQ08VSpWLP+v+Z8QCdHKlegHqvy5I4y94OcrjuoZMa16GZJn07jBN55hlThbgwSLpNCOWMVtkQB3QCvyVvzQWo+qSuq5e3cF1TYmdXRZsIzu2Ig2BC2P3n2VoYvGpryClSi92qJONDTCiVzlV2p8qIgKGtx04kLCZPjqF0SbtYTffV5ruAYw+6JC2jvUFE3I7PJGrK6Ob5xFWHjTxt+Ur/DvIWm3Kyc6bDDLRU/XjNqA0dBOU8anFrRDqaeSacyCBZY0Uwanamp5NYJbdqAP04c1SDtDsGOSiyGrmp6kdo1hbyCSjGgR/bccGVmezQ2iV2KmqGOKpyBeN3qNOwHwp3Wfh+ZbSRW2/VZC1MHcznoYszVTDXPmwVK0JDKcZREnZQroFRi8OdesITNSXLV+4ez/hBjpHwVnCrtippjHnRSpJiFfVqsBoaQVGBvQyAiBrRkpMM9koYtguvJHP65qnuXAcEjmP2Y=----ATTACHMENT:----ODQ0OTcyNzk4ODg1ODY5OSA3ODA3MjA4Mjk1NTk0MTM5IDIxODk3NDA0NTQxNDMwODY=