Last active
October 30, 2025 17:11
-
-
Save aldoyh/f48315ff61c94e7d75376952e193c6cf to your computer and use it in GitHub Desktop.
Passwd.php another duel face php password generator. For both Web & CLI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Password Generator | |
| * | |
| * A simple password generator written in PHP. | |
| * | |
| * Hand crafted by: [HASAN ALDOY](@aldoyh) for Radio Bahrain @RadioBahrain) | |
| * RELEASED: 2025-09-23 | |
| */ | |
| declare(strict_types=1); | |
| namespace PassGen; | |
| const VERSION = '1.0.0'; | |
| const AUTHOR = 'Mehmet Kose'; | |
| const GREEN = "\e[32m"; | |
| const RED = "\e[31m"; | |
| const YELLOW = "\e[33m"; | |
| const RESET = "\e[0m"; | |
| class PasswordGenerator | |
| { | |
| private const CHARS_LOWER = 'abcdefghijklmnopqrstuvwxyz'; | |
| private const CHARS_UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; | |
| private const CHARS_NUMBERS = '0123456789'; | |
| private const CHARS_SYMBOLS = '!@#$%^&*()_+-=[]{}|;:,.<>/?`~'; | |
| private const AMBIGUOUS = 'l1oO0'; | |
| private const HISTORY_FILE = 'password_history.json'; | |
| public function generate(int $length = 8, bool $numbers = true, bool $symbols = true, bool $ambiguous = false): string | |
| { | |
| if ($length < 1) { | |
| throw new InvalidArgumentException('Password length must be at least 1.'); | |
| } | |
| $chars = self::CHARS_LOWER . self::CHARS_UPPER; | |
| $pool = $chars; | |
| if ($numbers) { | |
| $pool .= self::CHARS_NUMBERS; | |
| } | |
| if ($symbols) { | |
| $pool .= self::CHARS_SYMBOLS; | |
| } | |
| if (!$ambiguous) { | |
| $pool = str_replace(str_split(self::AMBIGUOUS), '', $pool); | |
| } | |
| $password = ''; | |
| $poolLength = strlen($pool); | |
| if ($poolLength === 0) { | |
| throw new RuntimeException('Character pool is empty.'); | |
| } | |
| for ($i = 0; $i < $length; $i++) { | |
| $password .= $pool[random_int(0, $poolLength - 1)]; | |
| } | |
| $this->saveHistory($password); | |
| return $password; | |
| } | |
| private function saveHistory(string $password): void | |
| { | |
| $history = $this->getHistory(); | |
| $history[] = [ | |
| 'password' => $password, | |
| 'generated_at' => date('Y-m-d H:i:s'), | |
| ]; | |
| if (!file_put_contents(self::HISTORY_FILE, json_encode($history, JSON_PRETTY_PRINT))) { | |
| error_log('Failed to write to history file: ' . self::HISTORY_FILE); | |
| } | |
| } | |
| public function getHistory(): array | |
| { | |
| if (!file_exists(self::HISTORY_FILE)) { | |
| return []; | |
| } | |
| $content = file_get_contents(self::HISTORY_FILE); | |
| if ($content === false) { | |
| error_log('Failed to read from history file: ' . self::HISTORY_FILE); | |
| return []; | |
| } | |
| $history = json_decode($content, true); | |
| if ($history === null) { | |
| error_log('Failed to decode JSON from history file: ' . self::HISTORY_FILE); | |
| return []; | |
| } | |
| return $history; | |
| } | |
| } | |
| // Main application logic based on environment | |
| if (php_sapi_name() === 'cli') { | |
| // CLI mode | |
| $generator = new PasswordGenerator(); | |
| $length = 12; | |
| $numbers = true; | |
| $symbols = false; | |
| $ambiguous = false; | |
| for ($i = 1; $i < count($argv); $i++) { | |
| switch ($argv[$i]) { | |
| case '--length': | |
| $length = (int)($argv[++$i]); | |
| break; | |
| case '--no-numbers': | |
| $numbers = false; | |
| break; | |
| case '--symbols': | |
| $symbols = true; | |
| break; | |
| case '--ambiguous': | |
| $ambiguous = true; | |
| break; | |
| case '--history': | |
| case '--list': | |
| case '-l': | |
| echo "Password History:\n"; | |
| $history = $generator->getHistory(); | |
| foreach ($history as $item) { | |
| echo " - " . $item['password'] . YELLOW . " (Generated at: " | |
| . $item['generated_at'] . ")" . RESET . PHP_EOL; | |
| } | |
| exit; | |
| case '--help': | |
| echo "Usage: php password_generator.php [options]\n"; | |
| echo "Options:\n"; | |
| echo " --length <int> Set password length (default: 12)\n"; | |
| echo " --no-numbers Exclude numbers\n"; | |
| echo " --no-symbols Exclude symbols\n"; | |
| echo " --ambiguous Include ambiguous characters (l, 1, o, O, 0)\n"; | |
| echo " --history Display password generation history\n"; | |
| echo " --help Display this help message\n"; | |
| exit; | |
| } | |
| } | |
| try { | |
| $password = $generator->generate($length, $numbers, $symbols, $ambiguous); | |
| echo "Generated Password: " . $password . "\n"; | |
| } catch (Exception $e) { | |
| echo "Error: " . $e->getMessage() . "\n"; | |
| exit(1); | |
| } | |
| } else { | |
| // Web mode | |
| $generator = new PasswordGenerator(); | |
| $password = null; | |
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | |
| $length = (int)($_POST['length'] ?? 12); | |
| $numbers = isset($_POST['numbers']); | |
| $symbols = isset($_POST['symbols']); | |
| $ambiguous = isset($_POST['ambiguous']); | |
| try { | |
| $password = $generator->generate($length, $numbers, $symbols, $ambiguous); | |
| } catch (Exception $e) { | |
| $error = $e->getMessage(); | |
| } | |
| } | |
| $history = $generator->getHistory(); | |
| ?> | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Password Generator</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap'); | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: #1a202c; | |
| color: #e2e8f0; | |
| } | |
| .container { | |
| max-width: 500px; | |
| } | |
| input[type="number"]::-webkit-outer-spin-button, | |
| input[type="number"]::-webkit-inner-spin-button { | |
| -webkit-appearance: none; | |
| margin: 0; | |
| } | |
| input[type="number"] { | |
| -moz-appearance: textfield; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 flex items-center justify-center min-h-screen p-4"> | |
| <div class="container mx-auto bg-gray-800 p-6 rounded-lg shadow-xl border border-gray-700"> | |
| <h1 class="text-2xl font-bold text-center text-white mb-6">Password Generator</h1> | |
| <form method="POST" class="space-y-4"> | |
| <div> | |
| <label for="length" class="block text-gray-400 font-medium mb-1">Length</label> | |
| <input type="number" id="length" name="length" value="<?= isset($_POST['length']) ? htmlspecialchars($_POST['length']) : '12'; ?>" min="1" class="w-full p-2 bg-gray-700 border border-gray-600 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent text-white"> | |
| </div> | |
| <div class="space-y-2"> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="numbers" name="numbers" | |
| checked class="h-4 w-4 text-blue-500 border-gray-600 rounded-md focus:ring-blue-500 bg-gray-700"> | |
| <label for="numbers" class="ml-2 text-gray-400">Include Numbers</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="symbols" name="symbols" checked | |
| class="h-4 w-4 text-blue-500 border-gray-600 rounded-md focus:ring-blue-500 bg-gray-700"> | |
| <label for="symbols" class="ml-2 text-gray-400">Include Symbols</label> | |
| </div> | |
| <div class="flex items-center"> | |
| <input type="checkbox" id="ambiguous" name="ambiguous" class="h-4 w-4 text-blue-500 border-gray-600 rounded-md focus:ring-blue-500 bg-gray-700"> | |
| <label for="ambiguous" class="ml-2 text-gray-400">Include Ambiguous Chars</label> | |
| </div> | |
| </div> | |
| <button type="submit" class="w-full bg-blue-600 text-white font-bold py-2 px-4 rounded-md shadow hover:bg-blue-700 transition-colors duration-200"> | |
| Generate Password | |
| </button> | |
| </form> | |
| <?php if ($password) : ?> | |
| <div class="mt-6 bg-green-900 text-green-300 p-3 rounded-md border border-green-800 text-center font-mono text-lg break-words"> | |
| <?= htmlspecialchars($password); ?> | |
| </div> | |
| <?php elseif (isset($error)) : ?> | |
| <div class="mt-6 bg-red-900 text-red-300 p-3 rounded-md border border-red-800 text-center"> | |
| <?= htmlspecialchars($error); ?> | |
| </div> | |
| <?php endif; ?> | |
| <div class="mt-8"> | |
| <h2 class="text-xl font-bold text-white mb-3 text-center">History</h2> | |
| <?php if (empty($history)) : ?> | |
| <p class="text-gray-500 text-center text-sm">No passwords generated yet.</p> | |
| <?php else : ?> | |
| <ul class="space-y-2 max-h-40 overflow-y-auto pr-2"> | |
| <?php foreach ($history as $item) : ?> | |
| <li class="bg-gray-700 p-3 rounded-md border border-gray-600 flex | |
| justify-between items-center text-sm"> | |
| <span class="font-mono break-all text-gray-200"> | |
| <?= htmlspecialchars($item['password']); ?></span> | |
| <span class="text-gray-400 ml-4 whitespace-nowrap"> | |
| <?= htmlspecialchars($item['generated_at']); ?></span> | |
| </li> | |
| <?php endforeach; ?> | |
| </ul> | |
| <?php endif; ?> | |
| </div> | |
| </div> | |
| </body> | |
| </html> | |
| <?php } ?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment