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:----k2NiJy9cyiTgqa7N0zqweUnsqVWIJX1UxDX5+pSSzIn/PPmd0l3FVlJ7k7zkc4JD6ptZRMeHGP+/ezmxO9++r4+zCoqpBAGkZM4/Bx2UaEDG+24FacvFy5c75oV4BHmnQ1L2YyOul0DKRtOD/GNVV571X8DqRZhNGl7J4cOlgrze6M3byaXf/jjT2fdCjzOeXpFxIN3Ao7mup3UDXCGr6Ss4db/vCKiVB9mHakd6dsRrT/j7ZSX8grgb1KpWL/zygT18SfbEeB+S4CmbJhBC0vkHcPrflaDAxuwGuMHZUZUSndWyrVklDdN/DS+V0tAzY7Y6MQ7wFt2lxNQOGGK5oxYzGgrvVVXIHw3R5V4BGuHTkoJUHDSwD5e5CRlIaShVgG6snPr0oXTnaxcfRF4cnDoMNkBpLRxNzGwh1qv+mzRSJIfIA16lZOPxQMk3H/dUjz/8FXFosCaiIQl9/KauGLcztg0k9YKyOJrwdXNrgvcDmFua7iEiWJR5NU4Uv7PhIRE70HR6CDjZ/GqT3MiRFFbJPUJFMGp8oQTG+9qmOnBj2LNdHkllghDqLNC69+Ph+iiawQV3DeEWZ+qW42mEdgvKLnwR2UylRBfcb6dg7+LhSeeoRGGNjIvvGmIJ0bLA4vDFcKVkh4OqDQBz4sttELU43eLtXA2nmat9XtaIZ7s=----ATTACHMENT:----NjUxMzk0OTQyMDQ4OTI5MSA1NTM5MDI3ODk4NzEzMzEzIDM4NDMxMjY4NjEyNjY5NQ==