Changed separators to constants, added set,get unique and set,get separator

This commit is contained in:
R. Eric Wheeler 2018-11-29 12:34:59 -08:00
parent 7bef10a2a5
commit d420db9d09
5 changed files with 147 additions and 43 deletions

View File

@ -13,12 +13,18 @@ composer require sikofitt/generate-mac
use Sikofitt\GenerateMac\Mac; use Sikofitt\GenerateMac\Mac;
$mac = new Mac(); // default is ':' $mac = new Mac(); // default is ':'
// or
$mac->setSeparator(':');
$address = $mac->getAddress(); // ab:cd:ef:01:23:45 $address = $mac->getAddress(); // ab:cd:ef:01:23:45
$mac = new Mac('-'); $mac = new Mac('-');
// or
$mac->setSeparator('-');
$address = $mac->getAddress(); // ab-cd-ef-01-23-45 $address = $mac->getAddress(); // ab-cd-ef-01-23-45
$mac = new Mac(''); $mac = new Mac('');
// or
$mac->setSeparator('');
$address = $mac->getAddress(); // abcdef012345 $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 ```php
$mac = new Mac(':', false); $mac = new Mac(':', false);
// or
$mac->setUnique(false);
$address = $mac->getAddress(); $address = $mac->getAddress();
// '52:54:00:ab:cd:ef', QEMU virtual NIC prefix 52:54:00 // '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', * 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 #### Test

View File

@ -21,12 +21,15 @@ namespace Sikofitt\GenerateMac\Command;
use Sikofitt\GenerateMac\Mac; use Sikofitt\GenerateMac\Mac;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Exception\{
use Symfony\Component\Console\Exception\RuntimeException; InvalidArgumentException,
RuntimeException
};
use Symfony\Component\Console\Input\{ use Symfony\Component\Console\Input\{
InputInterface, InputInterface,
InputOption InputOption
}; };
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; 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.') ->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 * @param \Symfony\Component\Console\Output\OutputInterface $output
* *
* @throws \Exception * @throws \Exception
* @return int|null * @return int
*/ */
public function execute(InputInterface $input, OutputInterface $output): int public function execute(InputInterface $input, OutputInterface $output): int
{ {
@ -58,6 +61,7 @@ class GenerateMacCommand extends Command
if ($count <= 0) { if ($count <= 0) {
throw new RuntimeException('$count should be a positive number greater than zero.'); throw new RuntimeException('$count should be a positive number greater than zero.');
} }
$separator = strtolower($input->getOption('separator') ?? 'colon'); $separator = strtolower($input->getOption('separator') ?? 'colon');
if (!\in_array($separator, ['colon','none','dash'], true)) { if (!\in_array($separator, ['colon','none','dash'], true)) {
@ -67,19 +71,22 @@ class GenerateMacCommand extends Command
$outputFormat = strtolower($input->getOption('output') ?? 'string'); $outputFormat = strtolower($input->getOption('output') ?? 'string');
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
switch ($separator) { switch ($separator) {
case 'colon': case 'colon':
default: default:
$separator = ':'; $separator = Mac::SEPARATOR_COLON;
break; break;
case 'none': case 'none':
$separator = ''; $separator = Mac::SEPARATOR_NONE;
break; break;
case 'dash': case 'dash':
$separator = '-'; $separator = Mac::SEPARATOR_DASH;
break; break;
} }
$mac = new Mac($separator); $mac = new Mac($separator);
$macAddresses = $mac->getMacAddresses($count); $macAddresses = $mac->getMacAddresses($count);
switch ($outputFormat) { switch ($outputFormat) {
@ -93,7 +100,7 @@ class GenerateMacCommand extends Command
$io->writeln(\json_encode($result, JSON_PRETTY_PRINT)); $io->writeln(\json_encode($result, JSON_PRETTY_PRINT));
break; break;
case 'plain': case 'plain':
$io->writeln($macAddresses); $io->writeln($macAddresses, SymfonyStyle::OUTPUT_PLAIN | SymfonyStyle::VERBOSITY_NORMAL);
break; break;
} }

View File

@ -21,6 +21,10 @@ namespace Sikofitt\GenerateMac;
class Mac class Mac
{ {
/**
* Private mac address prefixes that are used
* internally or with virtual machines and containers.
*/
private const UNAVAILABLE_LOCAL_PREFIXES = [ private const UNAVAILABLE_LOCAL_PREFIXES = [
'02bb01', // Octothorpe '02bb01', // Octothorpe
'02aa3c', // Olivetti Telecomm SPA (olteco) '02aa3c', // Olivetti Telecomm SPA (olteco)
@ -44,42 +48,46 @@ class Mac
'deadca', // PearPC virtual NIC 'deadca', // PearPC virtual NIC
]; ];
/**
* Reserved mac prefixes for private devices.
*/
private const AVAILABLE_PREFIXES = [ private const AVAILABLE_PREFIXES = [
'x2xxxx', 'x2xxxx',
'x6xxxx', 'x6xxxx',
'xaxxxx', '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; protected $isTest = false;
/** /**
* @var string * @var int The mac address separator, can be self::SEPARATOR_*
*/ */
private $separator; private $separator;
/** /**
* @var bool * @var bool If we care if we get an already used prefix or not.
*/ */
private $unique; private $unique;
/** /**
* Mac constructor. * Mac constructor.
* *
* @param string $separator * @param int $separator The mac address separator, one of ':', '-', or ''
* @param bool $unique * @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)) { $this->setUnique($unique);
throw new \InvalidArgumentException('Separator is invalid. Acceptable values: ":", "-", or ""'); $this->setSeparator($separator);
}
$this->unique = $unique;
$this->separator = $separator;
} }
/** /**
@ -95,7 +103,7 @@ class Mac
$prefix = '02bb01'; $prefix = '02bb01';
} }
if ($this->unique) { if ($this->getUnique()) {
while ($this->isTaken($prefix)) { while ($this->isTaken($prefix)) {
$prefix = $this->generateString($template); $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 * @throws \Exception
* @return array * @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 * @return bool
*/ */
@ -134,12 +190,14 @@ class Mac
} }
/** /**
* @param string $template * Generates a string
*
* @param string $template The template to use xexxxx.
* *
* @throws \Exception * @throws \Exception
* @return mixed|string * @return string
*/ */
private function generateString(string $template) private function generateString(string $template): string
{ {
$bytes = sodium_bin2hex(\random_bytes(32)); $bytes = sodium_bin2hex(\random_bytes(32));
@ -161,13 +219,29 @@ class Mac
return \current($prefixes); 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 * @param string $macAddress
* *
* @return string * @return string
*/ */
private function insertSeparator(string $macAddress): string private function insertSeparator(string $macAddress): string
{ {
return implode($this->separator, str_split($macAddress, 2));
return implode($this->getSeparatorAsString(), str_split($macAddress, 2));
} }
} }

View File

@ -83,10 +83,10 @@ class GenerateMacCommandTest extends TestCase
$this->commandTester->execute(['--separator' => 'none', '--output' => 'plain']); $this->commandTester->execute(['--separator' => 'none', '--output' => 'plain']);
$output = $this->commandTester->getDisplay(true); $output = $this->commandTester->getDisplay(true);
// 13 because it adds a new line // 13 because it adds a new line with SymfonyStyle::writeLn();
$this->assertSame(13, strlen($output)); $this->assertSame(13, \strlen($output));
// just to make sure trim it. // 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']); $this->commandTester->execute(['--separator' => 'dash', '--output' => 'plain']);
$output = $this->commandTester->getDisplay(); $output = $this->commandTester->getDisplay();

View File

@ -40,12 +40,20 @@ class MacTest extends TestCase
} }
$this->assertRegExp(self::REGEX, $mac->getMacAddress()); $this->assertRegExp(self::REGEX, $mac->getMacAddress());
$this->assertSame(Mac::SEPARATOR_COLON, $mac->getSeparator());
$this->assertTrue($mac->getUnique());
} }
public function testSeparator(): void 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()); $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 public function testUnique(): void
@ -53,14 +61,17 @@ class MacTest extends TestCase
$class = new class extends Mac { $class = new class extends Mac {
protected $isTest = true; protected $isTest = true;
}; };
$this->assertTrue($class->getUnique());
$macAddress = $class->getMacAddress(); $macAddress = $class->getMacAddress();
$this->assertStringStartsNotWith(self::NON_UNIQ_PREFIX, $macAddress); $this->assertStringStartsNotWith(self::NON_UNIQ_PREFIX, $macAddress);
$class->setUnique(false);
$this->assertFalse($class->getUnique());
} }
public function testThrowsOnInvalidPrefix(): void public function testThrowsOnInvalidPrefix(): void
{ {
$this->expectException(\InvalidArgumentException::class); $this->expectException(\InvalidArgumentException::class);
$mac = new Mac('_'); $mac = new Mac(4);
} }
} }