Skip to content

Instantly share code, notes, and snippets.

@aldoyh
Last active October 30, 2025 17:11
Show Gist options
  • Select an option

  • Save aldoyh/f48315ff61c94e7d75376952e193c6cf to your computer and use it in GitHub Desktop.

Select an option

Save aldoyh/f48315ff61c94e7d75376952e193c6cf to your computer and use it in GitHub Desktop.

Revisions

  1. aldoyh revised this gist Oct 30, 2025. No changes.
  2. aldoyh revised this gist Oct 30, 2025. No changes.
  3. aldoyh revised this gist Oct 30, 2025. No changes.
  4. aldoyh revised this gist Oct 30, 2025. 1 changed file with 2 additions and 3 deletions.
    5 changes: 2 additions & 3 deletions PassGen.php
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,8 @@
    <?php
    /**
    * TRIVIA TREE version 1.0
    * Password Generator
    *
    * A fun and interactive trivia game that tests your knowledge on various topics.
    * Players can choose from different categories and compete for high scores.
    * A simple password generator written in PHP.
    *
    * Hand crafted by: [HASAN ALDOY](@aldoyh) for Radio Bahrain @RadioBahrain)
    * RELEASED: 2025-09-23
  5. aldoyh revised this gist Sep 23, 2025. 1 changed file with 263 additions and 1 deletion.
    264 changes: 263 additions & 1 deletion PassGen.php
    Original file line number Diff line number Diff line change
    @@ -1 +1,263 @@
    <?php
    /**
    * TRIVIA TREE version 1.0
    *
    * A fun and interactive trivia game that tests your knowledge on various topics.
    * Players can choose from different categories and compete for high scores.
    *
    * 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 } ?>
  6. aldoyh created this gist Sep 23, 2025.
    1 change: 1 addition & 0 deletions PassGen.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@