MAC Address Generator — PHP source
Generate random EUI-48 MAC addresses with a chosen separator, optional OUI prefix, uppercase formatting, and a locally-administered flag. Runs entirely in your browser with crypto-grade randomness.
This is the PHP implementation — the same logic the interactive tool runs, in a shareable, citable form.
<?php
/**
* mac-address-generator — random EUI-48 MAC address generator.
*
* Language: PHP (8.1+, standard library only)
* Source: CosmoDev polyglot showcase port of the MAC Address Generator tool,
* ported from src/lib/mac-generator.ts (the canonical TypeScript
* implementation), kept in lock-step with the Go twin at
* cli/mac-address-generator/mac-address-generator.go.
* License: display source — part of CosmoDev's polyglot tool pages.
*
* Design goals:
* - Pure + deterministic; throws only on a malformed OUI (never on randomness).
* - Functionally equivalent to the TS reference: same inputs -> same outputs.
* - Self-contained: stdlib only (no Composer packages). The default RNG uses
* random_bytes — PHP's cryptographic random source.
*
* API note: this port mirrors the TS canonical lib's public API, the
* deterministic, RNG-injectable superset of the Go twin's. The Go twin draws
* from crypto/rand directly (non-injectable), so its own suite asserts only
* structural properties (regex shape, OUI prefix, U/L bit). Like the TS lib,
* this port accepts an injected rng (a callable returning a float in [0, 1)),
* letting the showcase tests below assert exact MAC strings.
*
* Pipeline: (optional OUI -> 3 octets) -> 3 random octets -> (force U/L bit) ->
* join 6 octets as %02x with the separator -> (uppercase). A malformed OUI (not
* 6 hex digits after stripping non-hex chars) throws InvalidArgumentException.
*/
declare(strict_types=1);
/**
* Default RNG: a 32-bit draw from random_bytes mapped to [0, 1) — mirrors the
* TS default crypto.getRandomValues(new Uint32Array(1))[0] / 2**32.
*
* @return float
*/
function mac_default_rng(): float
{
// random_bytes(4) is a 4-byte cryptographically secure string; 'N' unpacks
// it as an unsigned 32-bit big-endian integer. PHP ints are 64-bit on
// 64-bit builds, so 2**32 fits and the division stays exact.
return unpack('N', random_bytes(4))[1] / 4294967296.0;
}
/**
* Two-digit lowercase hex, mirroring TS n.toString(16).padStart(2, '0').
*
* @param int $n
* @return string
*/
function mac_hex(int $n): string
{
return sprintf('%02x', $n);
}
/**
* Options for generate_mac(). Each key is optional; defaults match the TS source.
*
* - separator: string default ':' joining chars; '' concatenates
* - uppercase: bool default false uppercase hex letters
* - oui: string default null 6-hex-digit OUI prefix (separators tolerated)
* - locallyAdministered: bool default false force the U/L bit (0x02)
* - rng: callable default mac_default_rng returns a float in [0, 1)
*
* @return array
*/
function mac_default_options(): array
{
return [
'separator' => ':',
'uppercase' => false,
'oui' => null,
'locallyAdministered' => false,
'rng' => 'mac_default_rng',
];
}
/**
* Build a random EUI-48 MAC address.
*
* The PHP twin of generateMac() in src/lib/mac-generator.ts. Throws
* InvalidArgumentException on a malformed OUI; never throws otherwise.
*
* @param array $options
* @return string
* @throws \InvalidArgumentException
*/
function generate_mac(array $options = []): string
{
$options = array_merge(mac_default_options(), $options);
// Distinguish "not provided"/null (-> ':') from "provided as ''" (-> concat),
// exactly like the TS `?? ':'` and Go's nil-vs-non-nil Separator.
$sep = $options['separator'] ?? ':';
$rng = $options['rng'];
$octets = [];
// Octets 0..2 — OUI (validated) or random. TS `if (opts.oui)` truthiness:
// null and '' both take the random path.
if (!empty($options['oui'])) {
$clean = preg_replace('/[^0-9a-fA-F]/', '', $options['oui']);
if (strlen($clean) !== 6) {
throw new \InvalidArgumentException(
'OUI must be 6 hex digits, got "' . $options['oui'] . '"'
);
}
$octets[] = hexdec(substr($clean, 0, 2));
$octets[] = hexdec(substr($clean, 2, 2));
$octets[] = hexdec(substr($clean, 4, 2));
} else {
$octets[] = (int) floor($rng() * 256);
$octets[] = (int) floor($rng() * 256);
$octets[] = (int) floor($rng() * 256);
}
// Octets 3..5 — always random.
$octets[] = (int) floor($rng() * 256);
$octets[] = (int) floor($rng() * 256);
$octets[] = (int) floor($rng() * 256);
if ($options['locallyAdministered']) {
$octets[0] = ($octets[0] & 0xFD) | 0x02; // set U/L bit (0x02), clear multicast
}
$mac = implode($sep, array_map('mac_hex', $octets));
if ($options['uppercase']) {
$mac = strtoupper($mac);
}
return $mac;
}
// ---------- showcase tests (mirror src/lib/mac-generator.test.ts) ----------
// Run: php php.php (guarded so the file is still importable as a module).
if (!debug_backtrace()) {
$check = function (string $label, bool $ok): void {
if (!$ok) {
throw new \AssertionError("mac-address-generator showcase failed: {$label}");
}
};
$zero = static fn () => 0.0;
$check('zero rng', generate_mac(['rng' => $zero]) === '00:00:00:00:00:00');
$check('dash', generate_mac(['rng' => $zero, 'separator' => '-']) === '00-00-00-00-00-00');
$check('concat', generate_mac(['rng' => $zero, 'separator' => '']) === '000000000000');
// floor(0.7 * 256) == 179 == 0xb3
$check('uppercase', generate_mac(['rng' => static fn () => 0.7, 'uppercase' => true]) === 'B3:B3:B3:B3:B3:B3');
// OUI prefix (embedded separators stripped)
$check('oui', generate_mac(['rng' => $zero, 'oui' => 'aa-bb-cc']) === 'aa:bb:cc:00:00:00');
// locally administered: floor(0.5 * 256) == 0x80 -> 0x82
$check('local', generate_mac(['rng' => static fn () => 0.5, 'locallyAdministered' => true]) === '82:80:80:80:80:80');
// malformed OUI -> InvalidArgumentException
try {
generate_mac(['rng' => $zero, 'oui' => 'abc']);
throw new \Exception('expected InvalidArgumentException');
} catch (\InvalidArgumentException $e) {
// expected
}
echo "ok\n";
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →