?'; public function __construct(?LoggerInterface $logger = null) { parent::__construct($logger); } public function getId(): string { return self::GENERATOR_ID; } public function getVersion(): string { return '1.1.0'; } public function getName(): string { return 'High Entropy'; } public function getDescription(): string { return 'Cryptographically secure random passwords with customizable character sets'; } /** * Centralized option definitions */ public function getOptionDefinitions(): array { $options = [ 'length' => [ 'type' => 'integer', 'default' => 16, 'min' => 8, 'max' => 128, 'required' => false, 'group' => 'basic', 'description' => 'Total length of the generated password' ], 'include_uppercase' => [ 'type' => 'boolean', 'default' => true, 'required' => false, 'group' => 'character_set', 'description' => 'Include uppercase letters (A-Z)' ], 'include_lowercase' => [ 'type' => 'boolean', 'default' => true, 'required' => false, 'group' => 'character_set', 'description' => 'Include lowercase letters (a-z)' ], 'include_numbers' => [ 'type' => 'boolean', 'default' => true, 'required' => false, 'group' => 'character_set', 'description' => 'Include numbers (0-9)' ], 'include_symbols' => [ 'type' => 'boolean', 'default' => true, 'required' => false, 'group' => 'character_set', 'description' => 'Include symbols (e.g., !@#$%)' ], ]; return $options; } public function generate(int $count = 1, array $options = []): array { $options = array_merge($this->getDefaultOptions(), $options); $passwords = []; for ($i = 0; $i < $count; $i++) { $passwords[] = $this->generateSingle($options); } return $passwords; } private function generateSingle(array $options): string { $characterSet = $this->buildCharacterSet($options); $length = $options['length']; if (empty($characterSet)) { throw new \RuntimeException('No character types selected'); } // Generate password ensuring at least one character from each selected type $password = $this->generateWithGuaranteedTypes($characterSet, $length, $options); // Shuffle the password to randomize position of guaranteed characters return $this->shuffleString($password); } private function buildCharacterSet(array $options): string { $characterSet = ''; if ($options['include_lowercase'] ?? false) { $characterSet .= self::LOWERCASE; } if ($options['include_uppercase'] ?? false) { $characterSet .= self::UPPERCASE; } if ($options['include_numbers'] ?? false) { $characterSet .= self::NUMBERS; } if ($options['include_symbols'] ?? false) { $characterSet .= self::SYMBOLS; } return $characterSet; } private function generateWithGuaranteedTypes(string $characterSet, int $length, array $options): string { $password = ''; $guaranteedChars = []; // Add at least one character from each selected type if ($options['include_lowercase'] ?? false) { $guaranteedChars[] = self::LOWERCASE[random_int(0, strlen(self::LOWERCASE) - 1)]; } if ($options['include_uppercase'] ?? false) { $guaranteedChars[] = self::UPPERCASE[random_int(0, strlen(self::UPPERCASE) - 1)]; } if ($options['include_numbers'] ?? false) { $guaranteedChars[] = self::NUMBERS[random_int(0, strlen(self::NUMBERS) - 1)]; } if ($options['include_symbols'] ?? false) { $guaranteedChars[] = self::SYMBOLS[random_int(0, strlen(self::SYMBOLS) - 1)]; } // Add guaranteed characters $password = implode('', $guaranteedChars); // Fill remaining length with random characters from full set $remainingLength = $length - count($guaranteedChars); for ($i = 0; $i < $remainingLength; $i++) { $password .= $characterSet[random_int(0, strlen($characterSet) - 1)]; } return $password; } private function shuffleString(string $string): string { $characters = str_split($string); // Fisher-Yates shuffle using cryptographically secure random for ($i = count($characters) - 1; $i > 0; $i--) { $j = random_int(0, $i); [$characters[$i], $characters[$j]] = [$characters[$j], $characters[$i]]; } return implode('', $characters); } }