From d420db9d098a78e56c64f6339bd41a6d2d051e9d Mon Sep 17 00:00:00 2001 From: sikofitt Date: Thu, 29 Nov 2018 12:34:59 -0800 Subject: [PATCH] Changed separators to constants, added set,get unique and set,get separator --- README.md | 12 ++ .../Command/GenerateMacCommand.php | 23 +-- src/GenerateMac/Mac.php | 134 ++++++++++++++---- tests/GenerateMacCommandTest.php | 6 +- tests/MacTest.php | 15 +- 5 files changed, 147 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 2a96f18..63638f0 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,18 @@ composer require sikofitt/generate-mac use Sikofitt\GenerateMac\Mac; $mac = new Mac(); // default is ':' +// or +$mac->setSeparator(':'); $address = $mac->getAddress(); // ab:cd:ef:01:23:45 $mac = new Mac('-'); +// or +$mac->setSeparator('-'); $address = $mac->getAddress(); // ab-cd-ef-01-23-45 $mac = new Mac(''); +// or +$mac->setSeparator(''); $address = $mac->getAddress(); // abcdef012345 ``` @@ -26,6 +32,9 @@ If you don't care that it is unique you can remove the check for private mac pre ```php $mac = new Mac(':', false); +// or +$mac->setUnique(false); + $address = $mac->getAddress(); // '52:54:00:ab:cd:ef', QEMU virtual NIC prefix 52:54:00 @@ -51,6 +60,9 @@ var_dump($addresses); * 9 => '32:73:c0:b3:62:27', * ); */ + +// if you call this with 1 as the count it will still +// return an array [0 => '32:73:c0:b3:62:27'] ``` #### Test diff --git a/src/GenerateMac/Command/GenerateMacCommand.php b/src/GenerateMac/Command/GenerateMacCommand.php index ac18535..23a21eb 100644 --- a/src/GenerateMac/Command/GenerateMacCommand.php +++ b/src/GenerateMac/Command/GenerateMacCommand.php @@ -21,12 +21,15 @@ namespace Sikofitt\GenerateMac\Command; use Sikofitt\GenerateMac\Mac; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Exception\{ + InvalidArgumentException, + RuntimeException +}; use Symfony\Component\Console\Input\{ InputInterface, InputOption }; +use Symfony\Component\Console\Output\ConsoleOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -41,7 +44,7 @@ class GenerateMacCommand extends Command ->addOption('separator', 's', InputOption::VALUE_REQUIRED, 'The separator to use for mac addresses.') ; - parent::configure(); // TODO: Change the autogenerated stub + parent::configure(); } /** @@ -49,7 +52,7 @@ class GenerateMacCommand extends Command * @param \Symfony\Component\Console\Output\OutputInterface $output * * @throws \Exception - * @return int|null + * @return int */ public function execute(InputInterface $input, OutputInterface $output): int { @@ -58,6 +61,7 @@ class GenerateMacCommand extends Command if ($count <= 0) { throw new RuntimeException('$count should be a positive number greater than zero.'); } + $separator = strtolower($input->getOption('separator') ?? 'colon'); if (!\in_array($separator, ['colon','none','dash'], true)) { @@ -67,19 +71,22 @@ class GenerateMacCommand extends Command $outputFormat = strtolower($input->getOption('output') ?? 'string'); $io = new SymfonyStyle($input, $output); + switch ($separator) { case 'colon': default: - $separator = ':'; + $separator = Mac::SEPARATOR_COLON; break; case 'none': - $separator = ''; + $separator = Mac::SEPARATOR_NONE; break; case 'dash': - $separator = '-'; + $separator = Mac::SEPARATOR_DASH; break; } + $mac = new Mac($separator); + $macAddresses = $mac->getMacAddresses($count); switch ($outputFormat) { @@ -93,7 +100,7 @@ class GenerateMacCommand extends Command $io->writeln(\json_encode($result, JSON_PRETTY_PRINT)); break; case 'plain': - $io->writeln($macAddresses); + $io->writeln($macAddresses, SymfonyStyle::OUTPUT_PLAIN | SymfonyStyle::VERBOSITY_NORMAL); break; } diff --git a/src/GenerateMac/Mac.php b/src/GenerateMac/Mac.php index 8b62e55..56d6f0b 100644 --- a/src/GenerateMac/Mac.php +++ b/src/GenerateMac/Mac.php @@ -21,21 +21,25 @@ namespace Sikofitt\GenerateMac; class Mac { + /** + * Private mac address prefixes that are used + * internally or with virtual machines and containers. + */ private const UNAVAILABLE_LOCAL_PREFIXES = [ '02bb01', // Octothorpe '02aa3c', // Olivetti Telecomm SPA (olteco) '02608c', // 3com '027001', // Racal-datacom - '021c7c', //Perq Systems - '02e6d3', //Nixdorf Computer - '020701', //Racal-datacom - '029d8e', //Cardiac Recorders - '0270b3', //Data Recall - '02cf1c', //Communication Machinery - '02c08c', //3com - '0270b0', //M/a-com Companies - '026086', //Logic Replacement TECH. - '525400', //QEMU virtual NIC + '021c7c', // Perq Systems + '02e6d3', // Nixdorf Computer + '020701', // Racal-datacom + '029d8e', // Cardiac Recorders + '0270b3', // Data Recall + '02cf1c', // Communication Machinery + '02c08c', // 3com + '0270b0', // M/a-com Companies + '026086', // Logic Replacement TECH. + '525400', // QEMU virtual NIC 'aa0000', // Digital Equipment 'aa0001', // Digital Equipment 'aa0002', // Digital Equipment @@ -44,42 +48,46 @@ class Mac 'deadca', // PearPC virtual NIC ]; + /** + * Reserved mac prefixes for private devices. + */ private const AVAILABLE_PREFIXES = [ 'x2xxxx', 'x6xxxx', 'xaxxxx', - 'xaxxxx', + 'xexxxx', ]; + public const SEPARATOR_COLON = 0; + public const SEPARATOR_DASH = 1; + public const SEPARATOR_NONE = 2; + /** - * @var bool + * @internal + * @var bool For testing that we get a prefix that is not used. */ protected $isTest = false; /** - * @var string + * @var int The mac address separator, can be self::SEPARATOR_* */ private $separator; /** - * @var bool + * @var bool If we care if we get an already used prefix or not. */ private $unique; /** * Mac constructor. * - * @param string $separator - * @param bool $unique + * @param int $separator The mac address separator, one of ':', '-', or '' + * @param bool $unique Whether or not we care if we get a non unique prefix. */ - public function __construct(string $separator = ':', bool $unique = true) + public function __construct(int $separator = self::SEPARATOR_COLON, bool $unique = true) { - if (!\in_array($separator, [':', '', '-'], true)) { - throw new \InvalidArgumentException('Separator is invalid. Acceptable values: ":", "-", or ""'); - } - - $this->unique = $unique; - $this->separator = $separator; + $this->setUnique($unique); + $this->setSeparator($separator); } /** @@ -95,7 +103,7 @@ class Mac $prefix = '02bb01'; } - if ($this->unique) { + if ($this->getUnique()) { while ($this->isTaken($prefix)) { $prefix = $this->generateString($template); } @@ -107,7 +115,9 @@ class Mac } /** - * @param int $count + * Note: if count is 1 it will still be returned as an array. [0 => $macAddress] + * + * @param int $count The number of mac addresses to generate. * * @throws \Exception * @return array @@ -124,7 +134,53 @@ class Mac } /** - * @param string $prefix + * @param bool $unique + * + * @return \Sikofitt\GenerateMac\Mac + */ + public function setUnique(bool $unique = true): Mac + { + $this->unique = $unique; + + return $this; + } + + /** + * @return bool + */ + public function getUnique(): bool + { + return $this->unique; + } + + /** + * @param int $separator + * + * @return \Sikofitt\GenerateMac\Mac + */ + public function setSeparator(int $separator): Mac + { + if (!\in_array($separator, [self::SEPARATOR_COLON, self::SEPARATOR_DASH, self::SEPARATOR_NONE], true)) { + throw new \InvalidArgumentException('Separator is invalid. Acceptable values: ":", "-", or ""'); + } + + $this->separator = $separator; + + return $this; + } + + /** + * @return int + */ + public function getSeparator(): int + { + return $this->separator; + } + + /** + * Test to see if we have a unique prefix. + * + * @param string $prefix The current prefix. * * @return bool */ @@ -134,12 +190,14 @@ class Mac } /** - * @param string $template + * Generates a string + * + * @param string $template The template to use xexxxx. * * @throws \Exception - * @return mixed|string + * @return string */ - private function generateString(string $template) + private function generateString(string $template): string { $bytes = sodium_bin2hex(\random_bytes(32)); @@ -161,13 +219,29 @@ class Mac return \current($prefixes); } + private function getSeparatorAsString(): string + { + switch($this->getSeparator()) { + default: + case self::SEPARATOR_COLON: + return ':'; + case self::SEPARATOR_DASH: + return '-'; + case self::SEPARATOR_NONE: + return ''; + } + } + /** + * Inserts the chosen separator. + * * @param string $macAddress * * @return string */ private function insertSeparator(string $macAddress): string { - return implode($this->separator, str_split($macAddress, 2)); + + return implode($this->getSeparatorAsString(), str_split($macAddress, 2)); } } diff --git a/tests/GenerateMacCommandTest.php b/tests/GenerateMacCommandTest.php index 75e2d47..2655fb5 100644 --- a/tests/GenerateMacCommandTest.php +++ b/tests/GenerateMacCommandTest.php @@ -83,10 +83,10 @@ class GenerateMacCommandTest extends TestCase $this->commandTester->execute(['--separator' => 'none', '--output' => 'plain']); $output = $this->commandTester->getDisplay(true); - // 13 because it adds a new line - $this->assertSame(13, strlen($output)); + // 13 because it adds a new line with SymfonyStyle::writeLn(); + $this->assertSame(13, \strlen($output)); // just to make sure trim it. - $this->assertSame(12, strlen(trim($output))); + $this->assertSame(12, \strlen(trim($output))); $this->commandTester->execute(['--separator' => 'dash', '--output' => 'plain']); $output = $this->commandTester->getDisplay(); diff --git a/tests/MacTest.php b/tests/MacTest.php index ad9d4ca..da99e7b 100644 --- a/tests/MacTest.php +++ b/tests/MacTest.php @@ -40,12 +40,20 @@ class MacTest extends TestCase } $this->assertRegExp(self::REGEX, $mac->getMacAddress()); + $this->assertSame(Mac::SEPARATOR_COLON, $mac->getSeparator()); + $this->assertTrue($mac->getUnique()); } public function testSeparator(): void { - $mac = new Mac('-'); + $mac = new Mac(Mac::SEPARATOR_DASH); + + $this->assertSame(Mac::SEPARATOR_DASH, $mac->getSeparator()); $this->assertRegExp(self::REGEX, $mac->getMacAddress()); + $mac->setSeparator(Mac::SEPARATOR_COLON); + $this->assertSame(Mac::SEPARATOR_COLON, $mac->getSeparator()); + $this->assertRegExp(self::REGEX, $mac->getMacAddress()); + $this->assertNotFalse(strpos($mac->getMacAddress(), ':')); } public function testUnique(): void @@ -53,14 +61,17 @@ class MacTest extends TestCase $class = new class extends Mac { protected $isTest = true; }; + $this->assertTrue($class->getUnique()); $macAddress = $class->getMacAddress(); $this->assertStringStartsNotWith(self::NON_UNIQ_PREFIX, $macAddress); + $class->setUnique(false); + $this->assertFalse($class->getUnique()); } public function testThrowsOnInvalidPrefix(): void { $this->expectException(\InvalidArgumentException::class); - $mac = new Mac('_'); + $mac = new Mac(4); } }