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;
$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

View File

@ -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;
}

View File

@ -21,6 +21,10 @@ 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)
@ -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));
}
}

View File

@ -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();

View File

@ -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);
}
}