├── .gitignore ├── .travis.yml ├── Exception ├── CharactersNotFoundException.php ├── FileNotFoundException.php ├── ImpossibleMinMaxLimitsException.php ├── ImpossiblePasswordLengthException.php ├── InvalidOptionException.php ├── InvalidOptionTypeException.php ├── NotEnoughWordsException.php └── WordsNotFoundException.php ├── Generator ├── AbstractPasswordGenerator.php ├── ComputerPasswordGenerator.php ├── DummyPasswordGenerator.php ├── HumanPasswordGenerator.php ├── HybridPasswordGenerator.php ├── PasswordGeneratorInterface.php ├── PronounceablePasswordGenerator.php └── RequirementPasswordGenerator.php ├── LICENSE ├── Makefile ├── Model ├── CharacterSet.php └── Option │ ├── BooleanOption.php │ ├── IntegerOption.php │ ├── Option.php │ ├── OptionInterface.php │ └── StringOption.php ├── README.md ├── RandomGenerator ├── NoRandomGenerator.php ├── Php5RandomGenerator.php ├── Php7RandomGenerator.php └── RandomGeneratorInterface.php ├── Tests ├── Generator │ ├── AbstractPasswordGeneratorClass.php │ ├── AbstractPasswordGeneratorTest.php │ ├── ComputerPasswordGeneratorTest.php │ ├── Data │ │ └── WordList │ │ │ ├── empty.txt │ │ │ └── simple.txt │ ├── DummyPasswordGeneratorTest.php │ ├── HumanPasswordGeneratorClass.php │ ├── HumanPasswordGeneratorTest.php │ ├── HybridPasswordGeneratorTest.php │ ├── PronounceablePasswordGeneratorTest.php │ └── RequirementPasswordGeneratorTest.php ├── Model │ ├── CharacterSetTest.php │ └── Option │ │ ├── BooleanOptionTest.php │ │ ├── IntegerOptionTest.php │ │ ├── OptionClass.php │ │ ├── OptionTest.php │ │ └── StringOptionTest.php ├── RandomGenerator │ ├── NoRandomGeneratorTest.php │ ├── Php5RandomGeneratorTest.php │ └── Php7RandomGeneratorTest.php ├── ReadMeTest.php └── bootstrap.php ├── composer.json ├── phpunit.xml.dist └── travis.phpunit.xml /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | /.php_cs.cache 4 | .phpunit 5 | .phpunit.result.cache 6 | /phpunit.xml 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.3 5 | - 7.4 6 | - 8.0 7 | - nightly 8 | 9 | env: 10 | global: 11 | - PATH="$HOME/.composer/vendor/bin:$PATH" 12 | 13 | matrix: 14 | fast_finish: true 15 | include: 16 | - php: 8.0 17 | env: CS_FIXER=run 18 | - php: 7.3 19 | env: CS_FIXER=run 20 | - php: 7.3 21 | env: COMPOSER_FLAGS="--prefer-lowest" 22 | allow_failures: 23 | - php: nightly 24 | 25 | sudo: false 26 | 27 | cache: 28 | directories: 29 | - $HOME/.composer/cache 30 | 31 | before_script: 32 | - composer selfupdate 33 | - composer config -g github-oauth.github.com $GITHUB_OAUTH_TOKEN 34 | - composer global require phpunit/phpunit fabpot/php-cs-fixer --no-update 35 | - composer global update --prefer-dist --no-interaction 36 | - composer update --prefer-dist --no-interaction $COMPOSER_FLAGS 37 | 38 | script: 39 | - make test 40 | -------------------------------------------------------------------------------- /Exception/CharactersNotFoundException.php: -------------------------------------------------------------------------------- 1 | generatePassword(); 40 | } 41 | 42 | return $passwords; 43 | } 44 | 45 | /** 46 | * Set password generator option. 47 | * 48 | * @param string $option 49 | * @param array $optionSettings 50 | * 51 | * @return $this 52 | * @throws InvalidOptionTypeException 53 | */ 54 | public function setOption($option, $optionSettings) 55 | { 56 | $type = isset($optionSettings['type']) ? $optionSettings['type'] : ''; 57 | 58 | $this->options[$option] = Option::createFromType($type, $optionSettings); 59 | 60 | if ($this->options[$option] === null) { 61 | throw new InvalidOptionTypeException('Invalid Option Type'); 62 | } 63 | 64 | return $this; 65 | } 66 | 67 | /** 68 | * Remove Option. 69 | * 70 | * @param string $option 71 | * 72 | * @return $this 73 | */ 74 | public function removeOption($option) 75 | { 76 | unset($this->options[$option]); 77 | 78 | return $this; 79 | } 80 | 81 | /** 82 | * Get option. 83 | * 84 | * @param $option 85 | * 86 | * @return mixed 87 | */ 88 | public function getOption($option) 89 | { 90 | if (!isset($this->options[$option])) { 91 | return; 92 | } 93 | 94 | return $this->options[$option]; 95 | } 96 | 97 | /** 98 | * Set password generator option value. 99 | * 100 | * @param string $option 101 | * @param $value 102 | * 103 | * @return $this 104 | */ 105 | public function setOptionValue($option, $value) 106 | { 107 | if (!isset($this->options[$option])) { 108 | throw new InvalidOptionException('Invalid Option'); 109 | } 110 | 111 | $this->options[$option]->setValue($value); 112 | 113 | return $this; 114 | } 115 | 116 | /** 117 | * Get option value. 118 | * 119 | * @param $option 120 | * 121 | * @return mixed 122 | */ 123 | public function getOptionValue($option) 124 | { 125 | if (!isset($this->options[$option])) { 126 | throw new InvalidOptionException('Invalid Option'); 127 | } 128 | 129 | return $this->options[$option]->getValue(); 130 | } 131 | 132 | /** 133 | * @param string $parameter 134 | * @param mixed $value 135 | * 136 | * @return $this 137 | */ 138 | public function setParameter($parameter, $value) 139 | { 140 | $this->parameters[$parameter] = $value; 141 | 142 | return $this; 143 | } 144 | 145 | /** 146 | * @param string $parameter 147 | * @param mixed $default 148 | * 149 | * @return null|mixed 150 | */ 151 | public function getParameter($parameter, $default = null) 152 | { 153 | if (!isset($this->parameters[$parameter])) { 154 | return $default; 155 | } 156 | 157 | return $this->parameters[$parameter]; 158 | } 159 | 160 | /** 161 | * Possible options. 162 | * 163 | * @return array 164 | */ 165 | public function getOptions() 166 | { 167 | return $this->options; 168 | } 169 | 170 | /** 171 | * Set source of randomness. 172 | * 173 | * @param RandomGeneratorInterface $randomGenerator 174 | * 175 | * @return $this 176 | */ 177 | public function setRandomGenerator(RandomGeneratorInterface $randomGenerator) 178 | { 179 | $this->randomGenerator = $randomGenerator; 180 | 181 | return $this; 182 | } 183 | 184 | /** 185 | * Generate a random value 186 | * Fallback to mt_rand if none provided. 187 | * 188 | * @param int $min 189 | * @param int $max 190 | * 191 | * @return int 192 | */ 193 | public function randomInteger($min, $max) 194 | { 195 | if (!$this->randomGenerator) { 196 | $this->randomGenerator = new Php7RandomGenerator(); 197 | } 198 | 199 | return $this->randomGenerator->randomInteger($min, $max); 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /Generator/ComputerPasswordGenerator.php: -------------------------------------------------------------------------------- 1 | setOption(self::OPTION_UPPER_CASE, array('type' => Option::TYPE_BOOLEAN, 'default' => true)) 30 | ->setOption(self::OPTION_LOWER_CASE, array('type' => Option::TYPE_BOOLEAN, 'default' => true)) 31 | ->setOption(self::OPTION_NUMBERS, array('type' => Option::TYPE_BOOLEAN, 'default' => true)) 32 | ->setOption(self::OPTION_SYMBOLS, array('type' => Option::TYPE_BOOLEAN, 'default' => false)) 33 | ->setOption(self::OPTION_AVOID_SIMILAR, array('type' => Option::TYPE_BOOLEAN, 'default' => true)) 34 | ->setOption(self::OPTION_LENGTH, array('type' => Option::TYPE_INTEGER, 'default' => 10)) 35 | ->setParameter(self::PARAMETER_UPPER_CASE, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') 36 | ->setParameter(self::PARAMETER_LOWER_CASE, 'abcdefghijklmnopqrstuvwxyz') 37 | ->setParameter(self::PARAMETER_NUMBERS, '0123456789') 38 | ->setParameter(self::PARAMETER_SYMBOLS, '!@$%^&*()<>,.?/[]{}-=_+') 39 | ->setParameter(self::PARAMETER_SIMILAR, 'iIl1Oo0') 40 | ; 41 | } 42 | 43 | /** 44 | * Generate character list for us in generating passwords. 45 | * 46 | * @return CharacterSet Character list 47 | * 48 | * @throws CharactersNotFoundException 49 | */ 50 | public function getCharacterList() 51 | { 52 | $characters = ''; 53 | 54 | if ($this->getOptionValue(self::OPTION_UPPER_CASE)) { 55 | $characters .= $this->getParameter(self::PARAMETER_UPPER_CASE, ''); 56 | } 57 | 58 | if ($this->getOptionValue(self::OPTION_LOWER_CASE)) { 59 | $characters .= $this->getParameter(self::PARAMETER_LOWER_CASE, ''); 60 | } 61 | 62 | if ($this->getOptionValue(self::OPTION_NUMBERS)) { 63 | $characters .= $this->getParameter(self::PARAMETER_NUMBERS, ''); 64 | } 65 | 66 | if ($this->getOptionValue(self::OPTION_SYMBOLS)) { 67 | $characters .= $this->getParameter(self::PARAMETER_SYMBOLS, ''); 68 | } 69 | 70 | if ($this->getOptionValue(self::OPTION_AVOID_SIMILAR)) { 71 | $removeCharacters = \mb_str_split($this->getParameter(self::PARAMETER_SIMILAR, '')); 72 | $characters = \str_replace($removeCharacters, '', $characters); 73 | } 74 | 75 | if (!$characters) { 76 | throw new CharactersNotFoundException('No character sets selected.'); 77 | } 78 | 79 | return new CharacterSet($characters); 80 | } 81 | 82 | /** 83 | * Generate one password based on options. 84 | * 85 | * @return string password 86 | */ 87 | public function generatePassword() 88 | { 89 | $characterList = $this->getCharacterList()->getCharacters(); 90 | $characters = \mb_strlen($characterList); 91 | $password = ''; 92 | 93 | $length = $this->getLength(); 94 | 95 | for ($i = 0; $i < $length; ++$i) { 96 | $randomIndex = $this->randomInteger(0, $characters - 1); 97 | $password .= \mb_substr($characterList, $randomIndex, 1); 98 | } 99 | 100 | return $password; 101 | } 102 | 103 | /** 104 | * Password length. 105 | */ 106 | public function getLength() 107 | { 108 | return $this->getOptionValue(self::OPTION_LENGTH); 109 | } 110 | 111 | /** 112 | * Set length of desired password(s). 113 | * 114 | * @param int $characterCount 115 | * 116 | * @return $this 117 | * 118 | * @throws \InvalidArgumentException 119 | */ 120 | public function setLength($characterCount) 121 | { 122 | if (!is_int($characterCount) || $characterCount < 1) { 123 | throw new \InvalidArgumentException('Expected positive integer'); 124 | } 125 | 126 | $this->setOptionValue(self::OPTION_LENGTH, $characterCount); 127 | 128 | return $this; 129 | } 130 | 131 | /** 132 | * Are Uppercase characters enabled? 133 | * 134 | * @return bool 135 | */ 136 | public function getUppercase() 137 | { 138 | return $this->getOptionValue(self::OPTION_UPPER_CASE); 139 | } 140 | 141 | /** 142 | * Enable uppercase characters. 143 | * 144 | * @param bool $enable 145 | * 146 | * @return $this 147 | * 148 | * @throws \InvalidArgumentException 149 | */ 150 | public function setUppercase($enable = true) 151 | { 152 | if (!is_bool($enable)) { 153 | throw new \InvalidArgumentException('Expected boolean'); 154 | } 155 | 156 | $this->setOptionValue(self::OPTION_UPPER_CASE, $enable); 157 | 158 | return $this; 159 | } 160 | 161 | /** 162 | * Are Lowercase characters enabled? 163 | * 164 | * @return string 165 | */ 166 | public function getLowercase() 167 | { 168 | return $this->getOptionValue(self::OPTION_LOWER_CASE); 169 | } 170 | 171 | /** 172 | * Enable lowercase characters. 173 | * 174 | * @param bool $enable 175 | * 176 | * @return $this 177 | * 178 | * @throws \InvalidArgumentException 179 | */ 180 | public function setLowercase($enable = true) 181 | { 182 | if (!is_bool($enable)) { 183 | throw new \InvalidArgumentException('Expected boolean'); 184 | } 185 | 186 | $this->setOptionValue(self::OPTION_LOWER_CASE, $enable); 187 | 188 | return $this; 189 | } 190 | 191 | /** 192 | * Are Numbers enabled? 193 | * 194 | * @return string 195 | */ 196 | public function getNumbers() 197 | { 198 | return $this->getOptionValue(self::OPTION_NUMBERS); 199 | } 200 | 201 | /** 202 | * Enable numbers. 203 | * 204 | * @param bool $enable 205 | * 206 | * @return $this 207 | * 208 | * @throws \InvalidArgumentException 209 | */ 210 | public function setNumbers($enable = true) 211 | { 212 | if (!is_bool($enable)) { 213 | throw new \InvalidArgumentException('Expected boolean'); 214 | } 215 | 216 | $this->setOptionValue(self::OPTION_NUMBERS, $enable); 217 | 218 | return $this; 219 | } 220 | 221 | /** 222 | * Are Symbols enabled? 223 | * 224 | * @return string 225 | */ 226 | public function getSymbols() 227 | { 228 | return $this->getOptionValue(self::OPTION_SYMBOLS); 229 | } 230 | 231 | /** 232 | * Enable symbol characters. 233 | * 234 | * @param bool $enable 235 | * 236 | * @return $this 237 | * 238 | * @throws \InvalidArgumentException 239 | */ 240 | public function setSymbols($enable = true) 241 | { 242 | if (!is_bool($enable)) { 243 | throw new \InvalidArgumentException('Expected boolean'); 244 | } 245 | 246 | $this->setOptionValue(self::OPTION_SYMBOLS, $enable); 247 | 248 | return $this; 249 | } 250 | 251 | /** 252 | * Avoid similar characters enabled? 253 | * 254 | * @return string 255 | */ 256 | public function getAvoidSimilar() 257 | { 258 | return $this->getOptionValue(self::OPTION_AVOID_SIMILAR); 259 | } 260 | 261 | /** 262 | * Enable characters to be removed when avoiding similar characters. 263 | * 264 | * @param bool $enable 265 | * 266 | * @return $this 267 | * 268 | * @throws \InvalidArgumentException 269 | */ 270 | public function setAvoidSimilar($enable = true) 271 | { 272 | if (!is_bool($enable)) { 273 | throw new \InvalidArgumentException('Expected boolean'); 274 | } 275 | 276 | $this->setOptionValue(self::OPTION_AVOID_SIMILAR, $enable); 277 | 278 | return $this; 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /Generator/DummyPasswordGenerator.php: -------------------------------------------------------------------------------- 1 | setOption(self::OPTION_LENGTH, array('type' => Option::TYPE_INTEGER, 'default' => 10)) 17 | ; 18 | } 19 | 20 | public function generatePassword() 21 | { 22 | $length = $this->getOptionValue(self::OPTION_LENGTH); 23 | 24 | if ($length < 8) { 25 | return \mb_substr('password', 0, $length); 26 | } 27 | 28 | return str_pad('password', $length, '?'); 29 | } 30 | 31 | /** 32 | * Password length. 33 | * 34 | * @return int 35 | */ 36 | public function getLength() 37 | { 38 | return $this->getOptionValue(self::OPTION_LENGTH); 39 | } 40 | 41 | /** 42 | * Set length of desired password(s). 43 | * 44 | * @param int $characterCount 45 | * 46 | * @return $this 47 | * 48 | * @throws \InvalidArgumentException 49 | */ 50 | public function setLength($characterCount) 51 | { 52 | if (!is_int($characterCount) || $characterCount < 1) { 53 | throw new \InvalidArgumentException('Expected positive integer'); 54 | } 55 | 56 | $this->setOptionValue(self::OPTION_LENGTH, $characterCount); 57 | 58 | return $this; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Generator/HumanPasswordGenerator.php: -------------------------------------------------------------------------------- 1 | setOption(self::OPTION_LENGTH, array('type' => Option::TYPE_INTEGER, 'default' => null)) 29 | ->setOption(self::OPTION_WORDS, array('type' => Option::TYPE_INTEGER, 'default' => 4)) 30 | ->setOption(self::OPTION_MIN_WORD_LENGTH, array('type' => Option::TYPE_INTEGER, 'default' => 3)) 31 | ->setOption(self::OPTION_MAX_WORD_LENGTH, array('type' => Option::TYPE_INTEGER, 'default' => 20)) 32 | ->setParameter(self::PARAMETER_WORD_SEPARATOR, '') 33 | ; 34 | } 35 | 36 | /** 37 | * Generate word list for us in generating passwords. 38 | * 39 | * @return string[] Words 40 | * 41 | * @throws WordsNotFoundException 42 | */ 43 | public function generateWordList() 44 | { 45 | if ($this->getParameter(self::PARAMETER_WORD_CACHE) !== null) { 46 | $this->findWordListLength(); 47 | 48 | return $this->getParameter(self::PARAMETER_WORD_CACHE); 49 | } 50 | 51 | $words = explode("\n", \file_get_contents($this->getWordList())); 52 | 53 | $minWordLength = $this->getOptionValue(self::OPTION_MIN_WORD_LENGTH); 54 | $maxWordLength = $this->getOptionValue(self::OPTION_MAX_WORD_LENGTH); 55 | 56 | foreach ($words as $i => $word) { 57 | $words[$i] = trim($word); 58 | $wordLength = \mb_strlen($word); 59 | 60 | if ($wordLength > $maxWordLength || $wordLength < $minWordLength) { 61 | unset($words[$i]); 62 | } 63 | } 64 | 65 | $words = \array_values($words); 66 | 67 | if (!$words) { 68 | throw new WordsNotFoundException('No words selected.'); 69 | } 70 | 71 | $this->setParameter(self::PARAMETER_WORD_CACHE, $words); 72 | $this->findWordListLength(); 73 | 74 | return $words; 75 | } 76 | 77 | private function findWordListLength() 78 | { 79 | $words = $this->getParameter(self::PARAMETER_WORD_CACHE); 80 | 81 | $this->minWordLength = INF; 82 | $this->maxWordLength = 0; 83 | 84 | foreach ($words as $word) { 85 | $wordLength = \mb_strlen($word); 86 | 87 | $this->minWordLength = min($wordLength, $this->minWordLength); 88 | $this->maxWordLength = max($wordLength, $this->maxWordLength); 89 | } 90 | } 91 | 92 | private function generateWordListSubset($min, $max) 93 | { 94 | $wordList = $this->generateWordList(); 95 | $newWordList = array(); 96 | 97 | foreach ($wordList as $word) { 98 | $wordLength = mb_strlen($word); 99 | 100 | if ($wordLength < $min || $wordLength > $max) { 101 | continue; 102 | } 103 | 104 | $newWordList[] = $word; 105 | } 106 | 107 | return $newWordList; 108 | } 109 | 110 | /** 111 | * Generate one password based on options. 112 | * 113 | * @return string password 114 | * 115 | * @throws WordsNotFoundException 116 | * @throws ImpossiblePasswordLengthException 117 | */ 118 | public function generatePassword() 119 | { 120 | $wordList = $this->generateWordList(); 121 | 122 | $words = \count($wordList); 123 | 124 | if (!$words) { 125 | throw new WordsNotFoundException('No words selected.'); 126 | } 127 | 128 | $password = ''; 129 | $wordCount = $this->getWordCount(); 130 | 131 | if ( 132 | $this->getLength() > 0 && 133 | ( 134 | $this->getMinPasswordLength() > $this->getLength() 135 | || 136 | $this->getMaxPasswordLength() < $this->getLength() 137 | ) 138 | ) { 139 | throw new ImpossiblePasswordLengthException(); 140 | } 141 | 142 | if (!$this->getLength()) { 143 | for ($i = 0; $i < $wordCount; $i++) { 144 | if ($i) { 145 | $password .= $this->getWordSeparator(); 146 | } 147 | 148 | $password .= $this->randomWord(); 149 | } 150 | 151 | return $password; 152 | } 153 | 154 | while(--$wordCount) { 155 | $thisMin = $this->getLength() - mb_strlen($password) - ($wordCount * $this->getMaxWordLength()) - (mb_strlen($this->getWordSeparator()) * $wordCount); 156 | $thisMax = $this->getLength() - mb_strlen($password) - ($wordCount * $this->getMinWordLength()) - (mb_strlen($this->getWordSeparator()) * $wordCount); 157 | 158 | if ($thisMin < 1) { 159 | $thisMin = $this->getMinWordLength(); 160 | } 161 | 162 | if ($thisMax > $this->getMaxWordLength()) { 163 | $thisMax = $this->getMaxWordLength(); 164 | } 165 | 166 | $length = $this->randomInteger($thisMin, $thisMax); 167 | 168 | $password .= $this->randomWord($length, $length); 169 | 170 | if ($wordCount) { 171 | $password .= $this->getWordSeparator(); 172 | } 173 | } 174 | 175 | $desiredLength = $this->getLength() - mb_strlen($password); 176 | $password .= $this->randomWord($desiredLength, $desiredLength); 177 | 178 | return $password; 179 | } 180 | 181 | /** 182 | * @param null|int $minLength 183 | * @param null|int $maxLength 184 | * 185 | * @return string 186 | * 187 | * @throws NotEnoughWordsException 188 | */ 189 | public function randomWord($minLength = null, $maxLength = null) 190 | { 191 | if (is_null($minLength)) { 192 | $minLength = $this->getMinWordLength(); 193 | } 194 | 195 | if (is_null($maxLength)) { 196 | $maxLength = $this->getMaxWordLength(); 197 | } 198 | 199 | $wordList = $this->generateWordListSubset($minLength, $maxLength); 200 | $words = \count($wordList); 201 | 202 | if (!$words) { 203 | throw new NotEnoughWordsException(sprintf('No words with a length between %d and %d', $minLength, $maxLength)); 204 | } 205 | 206 | return $wordList[$this->randomInteger(0, $words - 1)]; 207 | } 208 | 209 | /** 210 | * Get number of words in desired password. 211 | * 212 | * @return int 213 | */ 214 | public function getWordCount() 215 | { 216 | return $this->getOptionValue(self::OPTION_WORDS); 217 | } 218 | 219 | /** 220 | * Set number of words in desired password(s). 221 | * 222 | * @param int $characterCount 223 | * 224 | * @return $this 225 | * 226 | * @throws \InvalidArgumentException 227 | */ 228 | public function setWordCount($characterCount) 229 | { 230 | if (!is_int($characterCount) || $characterCount < 1) { 231 | throw new \InvalidArgumentException('Expected positive integer'); 232 | } 233 | 234 | $this->setOptionValue(self::OPTION_WORDS, $characterCount); 235 | 236 | return $this; 237 | } 238 | 239 | /** 240 | * get max word length. 241 | * 242 | * @return int 243 | */ 244 | public function getMaxWordLength() 245 | { 246 | if (is_null($this->maxWordLength)) { 247 | return $this->getOptionValue(self::OPTION_MAX_WORD_LENGTH); 248 | } 249 | 250 | return min( 251 | $this->maxWordLength, 252 | $this->getOptionValue(self::OPTION_MAX_WORD_LENGTH) 253 | ); 254 | } 255 | 256 | /** 257 | * set max word length. 258 | * 259 | * @param int $length 260 | * 261 | * @return $this 262 | * 263 | * @throws \InvalidArgumentException 264 | */ 265 | public function setMaxWordLength($length) 266 | { 267 | if (!is_int($length) || $length < 1) { 268 | throw new \InvalidArgumentException('Expected positive integer'); 269 | } 270 | 271 | $this->setOptionValue(self::OPTION_MAX_WORD_LENGTH, $length); 272 | $this->setParameter(self::PARAMETER_WORD_CACHE, null); 273 | $this->minWordLength = null; 274 | $this->maxWordLength = null; 275 | 276 | return $this; 277 | } 278 | 279 | /** 280 | * get min word length. 281 | * 282 | * @return int 283 | */ 284 | public function getMinWordLength() 285 | { 286 | return max( 287 | $this->minWordLength, 288 | $this->getOptionValue(self::OPTION_MIN_WORD_LENGTH) 289 | ); 290 | } 291 | 292 | /** 293 | * set min word length. 294 | * 295 | * @param int $length 296 | * 297 | * @return $this 298 | * 299 | * @throws \InvalidArgumentException 300 | */ 301 | public function setMinWordLength($length) 302 | { 303 | if (!is_int($length) || $length < 1) { 304 | throw new \InvalidArgumentException('Expected positive integer'); 305 | } 306 | 307 | $this->setOptionValue(self::OPTION_MIN_WORD_LENGTH, $length); 308 | $this->setParameter(self::PARAMETER_WORD_CACHE, null); 309 | $this->minWordLength = null; 310 | $this->maxWordLength = null; 311 | 312 | return $this; 313 | } 314 | 315 | /** 316 | * Set word list. 317 | * 318 | * @param string $filename 319 | * 320 | * @return $this 321 | * 322 | * @throws \InvalidArgumentException 323 | * @throws FileNotFoundException 324 | */ 325 | public function setWordList($filename) 326 | { 327 | if (!is_string($filename)) { 328 | throw new \InvalidArgumentException('Expected string'); 329 | } elseif (!file_exists($filename)) { 330 | throw new FileNotFoundException('File not found'); 331 | } 332 | 333 | $this->setParameter(self::PARAMETER_DICTIONARY_FILE, $filename); 334 | $this->setParameter(self::PARAMETER_WORD_CACHE, null); 335 | 336 | return $this; 337 | } 338 | 339 | /** 340 | * Get word list filename. 341 | * 342 | * @throws FileNotFoundException 343 | * 344 | * @return string 345 | */ 346 | public function getWordList() 347 | { 348 | $filename = $this->getParameter(self::PARAMETER_DICTIONARY_FILE); 349 | 350 | if ($filename === null || !file_exists($filename)) { 351 | throw new FileNotFoundException($filename ?? 'No file provided'); 352 | } 353 | 354 | return $filename; 355 | } 356 | 357 | /** 358 | * Get word separator. 359 | * 360 | * @return string 361 | */ 362 | public function getWordSeparator() 363 | { 364 | return $this->getParameter(self::PARAMETER_WORD_SEPARATOR); 365 | } 366 | 367 | /** 368 | * Set word separator. 369 | * 370 | * @param string $separator 371 | * 372 | * @return $this 373 | * 374 | * @throws \InvalidArgumentException 375 | */ 376 | public function setWordSeparator($separator) 377 | { 378 | if (!is_string($separator)) { 379 | throw new \InvalidArgumentException('Expected string'); 380 | } 381 | 382 | $this->setParameter(self::PARAMETER_WORD_SEPARATOR, $separator); 383 | 384 | return $this; 385 | } 386 | 387 | /** 388 | * Password length 389 | * 390 | * @return integer 391 | */ 392 | public function getLength() 393 | { 394 | return $this->getOptionValue(self::OPTION_LENGTH); 395 | } 396 | 397 | /** 398 | * Set length of desired password(s) 399 | * 400 | * @param integer $characterCount 401 | * 402 | * @return $this 403 | * 404 | * @throws \InvalidArgumentException 405 | */ 406 | public function setLength($characterCount) 407 | { 408 | if (!is_int($characterCount) || $characterCount < 1) { 409 | throw new \InvalidArgumentException('Expected positive integer'); 410 | } 411 | 412 | $this->setOptionValue(self::OPTION_LENGTH, $characterCount); 413 | 414 | return $this; 415 | } 416 | 417 | /** 418 | * Calculate how long the password would be using minimum word length 419 | * 420 | * @return int 421 | */ 422 | public function getMinPasswordLength() 423 | { 424 | $wordCount = $this->getWordCount(); 425 | 426 | return ($this->getMinWordLength() * $wordCount) + (mb_strlen($this->getWordSeparator()) * ($wordCount - 1)); 427 | } 428 | 429 | /** 430 | * Calculate how long the password would be using maximum word length 431 | * 432 | * @return int 433 | */ 434 | public function getMaxPasswordLength() 435 | { 436 | $wordCount = $this->getWordCount(); 437 | 438 | return ($this->getMaxWordLength() * $wordCount) + (mb_strlen($this->getWordSeparator()) * ($wordCount - 1)); 439 | } 440 | } 441 | -------------------------------------------------------------------------------- /Generator/HybridPasswordGenerator.php: -------------------------------------------------------------------------------- 1 | removeOption(self::OPTION_LENGTH) 21 | ->setOption(self::OPTION_SEGMENT_COUNT, array('type' => Option::TYPE_INTEGER, 'default' => 4)) 22 | ->setOption(self::OPTION_SEGMENT_LENGTH, array('type' => Option::TYPE_INTEGER, 'default' => 3)) 23 | ->setParameter(self::PARAMETER_SEPARATOR, '-') 24 | ; 25 | } 26 | 27 | /** 28 | * Generate character list for us in generating passwords 29 | * and remove segment separator from character list pool. 30 | * 31 | * @return CharacterSet Character list 32 | * 33 | * @throws \Exception 34 | */ 35 | public function getCharacterList() 36 | { 37 | $characterList = parent::getCharacterList(); 38 | $characterList = \str_replace($this->getSegmentSeparator(), '', $characterList); 39 | 40 | return new CharacterSet($characterList); 41 | } 42 | 43 | /** 44 | * Generate one password based on options. 45 | * 46 | * @return string password 47 | */ 48 | public function generatePassword() 49 | { 50 | $characterList = $this->getCharacterList()->getCharacters(); 51 | $characters = \mb_strlen($characterList); 52 | $password = ''; 53 | 54 | $segmentCount = $this->getSegmentCount(); 55 | $segmentLength = $this->getSegmentLength(); 56 | 57 | for ($i = 0; $i < $segmentCount; ++$i) { 58 | if ($password) { 59 | $password .= $this->getSegmentSeparator(); 60 | } 61 | 62 | for ($j = 0; $j < $segmentLength; ++$j) { 63 | $password .= $characterList[$this->randomInteger(0, $characters - 1)]; 64 | } 65 | } 66 | 67 | return $password; 68 | } 69 | 70 | /** 71 | * Get number of words in desired password. 72 | * 73 | * @return int 74 | */ 75 | public function getLength() 76 | { 77 | return $this->getSegmentCount(); 78 | } 79 | 80 | /** 81 | * Set length of desired password(s). 82 | * 83 | * @param int $characterCount 84 | * 85 | * @return $this 86 | * 87 | * @throws \InvalidArgumentException 88 | */ 89 | public function setLength($characterCount) 90 | { 91 | $this->setSegmentCount($characterCount); 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * Get number of segments in desired password. 98 | * 99 | * @return int 100 | */ 101 | public function getSegmentCount() 102 | { 103 | return $this->getOptionValue(self::OPTION_SEGMENT_COUNT); 104 | } 105 | 106 | /** 107 | * Set number of segments in desired password(s). 108 | * 109 | * @param int $segmentCount 110 | * 111 | * @return $this 112 | * 113 | * @throws \InvalidArgumentException 114 | */ 115 | public function setSegmentCount($segmentCount) 116 | { 117 | if (!is_int($segmentCount) || $segmentCount < 1) { 118 | throw new \InvalidArgumentException('Expected positive integer'); 119 | } 120 | 121 | $this->setOptionValue(self::OPTION_SEGMENT_COUNT, $segmentCount); 122 | 123 | return $this; 124 | } 125 | 126 | /** 127 | * Get number of segments in desired password. 128 | * 129 | * @return int 130 | */ 131 | public function getSegmentLength() 132 | { 133 | return $this->getOptionValue(self::OPTION_SEGMENT_LENGTH); 134 | } 135 | 136 | /** 137 | * Set length of segment. 138 | * 139 | * @param int $segmentLength 140 | * 141 | * @return $this 142 | * 143 | * @throws \InvalidArgumentException 144 | */ 145 | public function setSegmentLength($segmentLength) 146 | { 147 | if (!is_int($segmentLength) || $segmentLength < 1) { 148 | throw new \InvalidArgumentException('Expected positive integer'); 149 | } 150 | 151 | $this->setOptionValue(self::OPTION_SEGMENT_LENGTH, $segmentLength); 152 | 153 | return $this; 154 | } 155 | 156 | /** 157 | * Get Segment Separator. 158 | * 159 | * @return string 160 | */ 161 | public function getSegmentSeparator() 162 | { 163 | return $this->getParameter(self::PARAMETER_SEPARATOR); 164 | } 165 | 166 | /** 167 | * Set segment separator. 168 | * 169 | * @param string $segmentSeparator 170 | * 171 | * @return \Hackzilla\PasswordGenerator\Generator\HybridPasswordGenerator 172 | * 173 | * @throws \InvalidArgumentException 174 | */ 175 | public function setSegmentSeparator($segmentSeparator) 176 | { 177 | if (!is_string($segmentSeparator)) { 178 | throw new \InvalidArgumentException('Expected string'); 179 | } 180 | 181 | $this->setParameter(self::PARAMETER_SEPARATOR, $segmentSeparator); 182 | 183 | return $this; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /Generator/PasswordGeneratorInterface.php: -------------------------------------------------------------------------------- 1 | setParameter(self::PARAMETER_VOWELS, 'aeiou'); 16 | } 17 | 18 | public function generatePassword() 19 | { 20 | $vowels = $this->getParameter(self::PARAMETER_VOWELS, ''); 21 | 22 | if (!mb_strlen($vowels)) { 23 | return parent::generatePassword(); 24 | } 25 | 26 | $characterList = $this->getCharacterList()->getCharacters(); 27 | $charactersCount = mb_strlen($characterList); 28 | $password = ''; 29 | 30 | $length = $this->getLength(); 31 | $isLastCharVowel = null; 32 | 33 | for ($i = 0; $i < $length; ++$i) { 34 | do { 35 | $char = $characterList[$this->randomInteger(0, $charactersCount - 1)]; 36 | $isVowel = false !== stripos($vowels, $char); 37 | 38 | } while (null !== $isLastCharVowel && $isVowel === $isLastCharVowel); 39 | 40 | $password .= $char; 41 | $isLastCharVowel = $isVowel; 42 | } 43 | 44 | return $password; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Generator/RequirementPasswordGenerator.php: -------------------------------------------------------------------------------- 1 | validOptions = array( 29 | self::OPTION_UPPER_CASE, 30 | self::OPTION_LOWER_CASE, 31 | self::OPTION_NUMBERS, 32 | self::OPTION_SYMBOLS, 33 | ); 34 | } 35 | 36 | /** 37 | * Generate one password based on options. 38 | * 39 | * @return string password 40 | * @throws ImpossibleMinMaxLimitsException 41 | * @throws \Hackzilla\PasswordGenerator\Exception\CharactersNotFoundException 42 | */ 43 | public function generatePassword() 44 | { 45 | if ($this->dirtyCheck) { 46 | if (!$this->validLimits()) { 47 | throw new ImpossibleMinMaxLimitsException(); 48 | } 49 | 50 | $this->dirtyCheck = false; 51 | } 52 | 53 | do { 54 | $password = parent::generatePassword(); 55 | } while (!$this->validatePassword($password)); 56 | 57 | return $password; 58 | } 59 | 60 | /** 61 | * Password minimum count for option. 62 | * 63 | * @param string $option Use class constants 64 | * 65 | * @return int|null 66 | */ 67 | public function getMinimumCount($option) 68 | { 69 | return isset($this->minimumCounts[$option]) ? $this->minimumCounts[$option] : null; 70 | } 71 | 72 | /** 73 | * Password maximum count for option. 74 | * 75 | * @param string $option Use class constants 76 | * 77 | * @return int|null 78 | */ 79 | public function getMaximumCount($option) 80 | { 81 | return isset($this->maximumCounts[$option]) ? $this->maximumCounts[$option] : null; 82 | } 83 | 84 | /** 85 | * Set minimum count of option for desired password(s). 86 | * 87 | * @param string $option Use class constants 88 | * @param int|null $characterCount 89 | * 90 | * @return $this 91 | * 92 | * @throws InvalidOptionException 93 | */ 94 | public function setMinimumCount($option, $characterCount) 95 | { 96 | $this->dirtyCheck = true; 97 | 98 | if (!$this->validOption($option)) { 99 | throw new InvalidOptionException('Invalid Option'); 100 | } 101 | 102 | if (is_null($characterCount)) { 103 | unset($this->minimumCounts[$option]); 104 | 105 | return $this; 106 | } 107 | 108 | if (!is_int($characterCount) || $characterCount < 0) { 109 | throw new \InvalidArgumentException('Expected non-negative integer'); 110 | } 111 | 112 | $this->minimumCounts[$option] = $characterCount; 113 | 114 | return $this; 115 | } 116 | 117 | /** 118 | * Set maximum count of option for desired password(s). 119 | * 120 | * @param string $option Use class constants 121 | * @param int|null $characterCount 122 | * 123 | * @return $this 124 | * 125 | * @throws InvalidOptionException 126 | */ 127 | public function setMaximumCount($option, $characterCount) 128 | { 129 | $this->dirtyCheck = true; 130 | 131 | if (!$this->validOption($option)) { 132 | throw new InvalidOptionException('Invalid Option'); 133 | } 134 | 135 | if (is_null($characterCount)) { 136 | unset($this->maximumCounts[$option]); 137 | 138 | return $this; 139 | } 140 | 141 | if (!is_int($characterCount) || $characterCount < 0) { 142 | throw new \InvalidArgumentException('Expected non-negative integer'); 143 | } 144 | 145 | $this->maximumCounts[$option] = $characterCount; 146 | 147 | return $this; 148 | } 149 | 150 | public function validLimits() 151 | { 152 | $elements = 0; 153 | 154 | if ($this->getOptionValue(self::OPTION_UPPER_CASE)) { 155 | $elements++; 156 | } 157 | 158 | if ($this->getOptionValue(self::OPTION_LOWER_CASE)) { 159 | $elements++; 160 | } 161 | 162 | if ($this->getOptionValue(self::OPTION_NUMBERS)) { 163 | $elements++; 164 | } 165 | 166 | if ($this->getOptionValue(self::OPTION_SYMBOLS)) { 167 | $elements++; 168 | } 169 | 170 | // check if there is wiggle room in minimums 171 | $total = 0; 172 | 173 | foreach ($this->minimumCounts as $minOption => $minCount) { 174 | $total += $minCount; 175 | } 176 | 177 | if ($total > $this->getLength()) { 178 | return false; 179 | } 180 | 181 | // check if there is wiggle room in maximums 182 | if ($elements <= count($this->maximumCounts)) { 183 | $total = 0; 184 | 185 | foreach ($this->maximumCounts as $maxOption => $maxCount) { 186 | $total += $maxCount; 187 | } 188 | 189 | if ($total < $this->getLength()) { 190 | return false; 191 | } 192 | } 193 | 194 | return true; 195 | } 196 | 197 | /** 198 | * @param string $option 199 | * 200 | * @return bool 201 | */ 202 | public function validOption($option) 203 | { 204 | return in_array($option, $this->validOptions, true); 205 | } 206 | 207 | /** 208 | * Generate $count number of passwords. 209 | * 210 | * @param int $count Number of passwords to return 211 | * 212 | * @return array 213 | * 214 | * @throws \InvalidArgumentException 215 | */ 216 | public function generatePasswords($count = 1) 217 | { 218 | if (!is_int($count)) { 219 | throw new \InvalidArgumentException('Expected integer'); 220 | } elseif ($count < 0) { 221 | throw new \InvalidArgumentException('Expected positive integer'); 222 | } 223 | 224 | $passwords = array(); 225 | 226 | for ($i = 0; $i < $count; $i++) { 227 | $passwords[] = $this->generatePassword(); 228 | } 229 | 230 | return $passwords; 231 | } 232 | 233 | /** 234 | * Check password is valid when comparing to minimum and maximum counts of options. 235 | * 236 | * @param string $password 237 | * 238 | * @return bool 239 | */ 240 | public function validatePassword($password) 241 | { 242 | foreach ($this->validOptions as $option) { 243 | $minCount = $this->getMinimumCount($option); 244 | $maxCount = $this->getMaximumCount($option); 245 | $count = mb_strlen(preg_replace('|[^'.preg_quote($this->getParameter($option)).']|u', '', $password)); 246 | 247 | if (!is_null($minCount) && $count < $minCount) { 248 | return false; 249 | } 250 | 251 | if (!is_null($maxCount) && $count > $maxCount) { 252 | return false; 253 | } 254 | } 255 | 256 | return true; 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Daniel Platt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | mkdir -p build/logs 3 | php vendor/bin/phpunit 4 | -------------------------------------------------------------------------------- /Model/CharacterSet.php: -------------------------------------------------------------------------------- 1 | characters = $characters; 17 | } 18 | 19 | /** 20 | * Get Character set 21 | * 22 | * @return string 23 | */ 24 | public function getCharacters(): string 25 | { 26 | return $this->characters; 27 | } 28 | 29 | /** 30 | * @return string 31 | */ 32 | public function __toString() 33 | { 34 | return $this->characters; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Model/Option/BooleanOption.php: -------------------------------------------------------------------------------- 1 | minRange = isset($settings['min']) ? $settings['min'] : ~PHP_INT_MAX; 20 | $this->maxRange = isset($settings['max']) ? $settings['max'] : PHP_INT_MAX; 21 | } 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function setValue($value) 27 | { 28 | if (!is_integer($value)) { 29 | throw new InvalidArgumentException('Integer required'); 30 | } 31 | 32 | if ($this->minRange > $value || $this->maxRange < $value) { 33 | throw new InvalidArgumentException('Invalid Value'); 34 | } 35 | 36 | parent::setValue($value); 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function getType() 43 | { 44 | return self::TYPE_INTEGER; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Model/Option/Option.php: -------------------------------------------------------------------------------- 1 | value = $settings['default']; 20 | } 21 | } 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function getValue() 27 | { 28 | return $this->value; 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | */ 34 | public function setValue($value) 35 | { 36 | $this->value = $value; 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public static function createFromType($type, array $settings = array()) 43 | { 44 | switch ($type) { 45 | case self::TYPE_STRING: 46 | return new StringOption($settings); 47 | 48 | case self::TYPE_INTEGER: 49 | return new IntegerOption($settings); 50 | 51 | case self::TYPE_BOOLEAN: 52 | return new BooleanOption($settings); 53 | } 54 | 55 | return; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Model/Option/OptionInterface.php: -------------------------------------------------------------------------------- 1 | minRange = isset($settings['min']) ? $settings['min'] : 0; 20 | $this->maxRange = isset($settings['max']) ? $settings['max'] : 255; 21 | } 22 | 23 | /** 24 | * {@inheritdoc} 25 | */ 26 | public function setValue($value) 27 | { 28 | if (!is_string($value)) { 29 | throw new InvalidArgumentException('String required'); 30 | } 31 | 32 | $strlen = mb_strlen($value); 33 | 34 | if ($this->minRange > $strlen || $this->maxRange < $strlen) { 35 | throw new InvalidArgumentException('Invalid Value'); 36 | } 37 | 38 | parent::setValue($value); 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function getType() 45 | { 46 | return self::TYPE_STRING; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Password Generator Library 2 | 3 | Simple library for generating random passwords. 4 | 5 | [![Latest Stable Version](https://poser.pugx.org/hackzilla/password-generator/v/stable.svg)](https://packagist.org/packages/hackzilla/password-generator) 6 | [![Total Downloads](https://poser.pugx.org/hackzilla/password-generator/downloads.svg)](https://packagist.org/packages/hackzilla/password-generator) 7 | [![Latest Unstable Version](https://poser.pugx.org/hackzilla/password-generator/v/unstable.svg)](https://packagist.org/packages/hackzilla/password-generator) 8 | [![License](https://poser.pugx.org/hackzilla/password-generator/license.svg)](https://packagist.org/packages/hackzilla/password-generator) 9 | 10 | 11 | ## Requirements 12 | 13 | * PHP >= 8.0 14 | * ext-mbstring 15 | 16 | ## Installation 17 | 18 | Install Composer 19 | 20 | ``` 21 | curl -sS https://getcomposer.org/installer | php 22 | mv composer.phar /usr/local/bin/composer 23 | ``` 24 | 25 | Now tell composer to download the library by running the command: 26 | 27 | ``` bash 28 | $ composer require hackzilla/password-generator 29 | ``` 30 | 31 | Composer will add the library to your composer.json file and install it into your project's `vendor/hackzilla` directory. 32 | 33 | 34 | ## Simple Usage 35 | 36 | ```php 37 | use Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator; 38 | 39 | $generator = new ComputerPasswordGenerator(); 40 | 41 | $generator 42 | ->setOptionValue(ComputerPasswordGenerator::OPTION_UPPER_CASE, true) 43 | ->setOptionValue(ComputerPasswordGenerator::OPTION_LOWER_CASE, true) 44 | ->setOptionValue(ComputerPasswordGenerator::OPTION_NUMBERS, true) 45 | ->setOptionValue(ComputerPasswordGenerator::OPTION_SYMBOLS, false) 46 | ; 47 | 48 | $password = $generator->generatePassword(); 49 | ``` 50 | 51 | 52 | ## More Passwords Usage 53 | 54 | If you want to generate 10 passwords that are 12 characters long. 55 | 56 | ```php 57 | use Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator; 58 | 59 | $generator = new ComputerPasswordGenerator(); 60 | 61 | $generator 62 | ->setUppercase() 63 | ->setLowercase() 64 | ->setNumbers() 65 | ->setSymbols(false) 66 | ->setLength(12); 67 | 68 | $password = $generator->generatePasswords(10); 69 | ``` 70 | 71 | ## Hybrid Password Generator Usage 72 | 73 | ```php 74 | use Hackzilla\PasswordGenerator\Generator\HybridPasswordGenerator; 75 | 76 | $generator = new HybridPasswordGenerator(); 77 | 78 | $generator 79 | ->setUppercase() 80 | ->setLowercase() 81 | ->setNumbers() 82 | ->setSymbols(false) 83 | ->setSegmentLength(3) 84 | ->setSegmentCount(4) 85 | ->setSegmentSeparator('-'); 86 | 87 | $password = $generator->generatePasswords(10); 88 | ``` 89 | 90 | If you can think of a better name for this password generator then let me know. 91 | 92 | The segment separator will be remove from the possible characters. 93 | 94 | 95 | ## Human Password Generator Usage 96 | 97 | ```php 98 | use Hackzilla\PasswordGenerator\Generator\HumanPasswordGenerator; 99 | 100 | $generator = new HumanPasswordGenerator(); 101 | 102 | $generator 103 | ->setWordList('/usr/share/dict/words') 104 | ->setWordCount(3) 105 | ->setWordSeparator('-'); 106 | 107 | $password = $generator->generatePasswords(10); 108 | ``` 109 | 110 | ## Requirement Password Generator Usage 111 | 112 | ```php 113 | use Hackzilla\PasswordGenerator\Generator\RequirementPasswordGenerator; 114 | 115 | $generator = new RequirementPasswordGenerator(); 116 | 117 | $generator 118 | ->setLength(16) 119 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 120 | ->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, true) 121 | ->setOptionValue(RequirementPasswordGenerator::OPTION_NUMBERS, true) 122 | ->setOptionValue(RequirementPasswordGenerator::OPTION_SYMBOLS, true) 123 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 2) 124 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_LOWER_CASE, 2) 125 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_NUMBERS, 2) 126 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_SYMBOLS, 2) 127 | ->setMaximumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 8) 128 | ->setMaximumCount(RequirementPasswordGenerator::OPTION_LOWER_CASE, 8) 129 | ->setMaximumCount(RequirementPasswordGenerator::OPTION_NUMBERS, 8) 130 | ->setMaximumCount(RequirementPasswordGenerator::OPTION_SYMBOLS, 8) 131 | ; 132 | 133 | $password = $generator->generatePassword(); 134 | ``` 135 | 136 | A limit can be removed by passing ```null``` 137 | 138 | ```php 139 | $generator 140 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, null) 141 | ->setMaximumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, null) 142 | ; 143 | ``` 144 | 145 | When setting the minimum and maximum values, be careful of unachievable settings. 146 | 147 | For example the following will end up in an infinite loop. 148 | ```php 149 | $generator 150 | ->setLength(4) 151 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 152 | ->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, false) 153 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 5) 154 | ->setMaximumCount(RequirementPasswordGenerator::OPTION_LOWER_CASE, 1) 155 | ; 156 | ``` 157 | 158 | For the moment you can call ```$generator->validLimits()``` to test whether the counts will cause problems. 159 | If the method returns true, then you can proceed. If false, then generatePassword() will likely cause an infinite loop. 160 | 161 | ## Example Implementations 162 | 163 | * Password Generator App [https://github.com/hackzilla/password-generator-app] 164 | * Password Generator Bundle [https://github.com/hackzilla/password-generator-bundle] 165 | 166 | ## Random Note 167 | 168 | Since version 1.5.0, the library depends on the presence of [random_int](http://www.php.net/random_int) which is found in PHP 7.0+ 169 | 170 | ## Contributions and Issues 171 | See all contributors on [GitHub](https://github.com/hackzilla/password-generator/graphs/contributors). 172 | 173 | Please report issues using GitHub's issue tracker: [GitHub Repo](https://github.com/hackzilla/password-generator) 174 | 175 | If you find this project useful, consider [buying me a coffee](https://www.buymeacoffee.com/hackzilla). 176 | 177 | ## License 178 | 179 | This bundle is released under the MIT license. See the [LICENSE](https://github.com/hackzilla/password-generator/blob/main/LICENSE) file for details. 180 | -------------------------------------------------------------------------------- /RandomGenerator/NoRandomGenerator.php: -------------------------------------------------------------------------------- 1 | setOption(self::OPTION_TEST_BOOLEAN, array('type' => Option::TYPE_BOOLEAN)) 20 | ->setOption(self::OPTION_TEST_INTEGER, array('type' => Option::TYPE_INTEGER, 'min' => -100, 'max' => 100)) 21 | ->setOption(self::OPTION_TEST_INTEGER_DEFAULT, array('type' => Option::TYPE_INTEGER, 'default' => 99)) 22 | ->setOption(self::OPTION_TEST_STRING, array('type' => Option::TYPE_STRING, 'min' => 1, 'max' => 3, 'default' => '')) 23 | ->setOption(self::OPTION_TEST_STRING_DEFAULT, array('type' => Option::TYPE_STRING, 'default' => 'test')); 24 | } 25 | 26 | public function generatePassword() 27 | { 28 | return ''; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Generator/AbstractPasswordGeneratorTest.php: -------------------------------------------------------------------------------- 1 | _object = new AbstractPasswordGeneratorClass(); 12 | } 13 | 14 | /** 15 | * @dataProvider generatePasswordsProvider 16 | * 17 | * @param $passwordCount 18 | */ 19 | public function testGeneratePasswordsException($passwordCount): void 20 | { 21 | $this->expectException(\InvalidArgumentException::class); 22 | $this->_object->generatePasswords($passwordCount); 23 | } 24 | 25 | public function generatePasswordsProvider() 26 | { 27 | return array( 28 | array(''), 29 | array(null), 30 | array(-1), 31 | array(0.1), 32 | array(true), 33 | ); 34 | } 35 | 36 | /** 37 | * @dataProvider lengthProvider 38 | * 39 | * @param $count 40 | */ 41 | public function testGeneratePasswords($count): void 42 | { 43 | $passwords = $this->_object->generatePasswords($count); 44 | 45 | $this->assertSame(\count($passwords), $count); 46 | } 47 | 48 | /** 49 | * @return array 50 | */ 51 | public function lengthProvider() 52 | { 53 | return array( 54 | array(1), 55 | array(4), 56 | array(8), 57 | array(16), 58 | ); 59 | } 60 | 61 | /** 62 | * @dataProvider optionProvider 63 | * 64 | * @param string $option 65 | */ 66 | public function testGetOption($option): void 67 | { 68 | $this->assertInstanceOf('Hackzilla\PasswordGenerator\Model\Option\OptionInterface', $this->_object->getOption($option)); 69 | } 70 | 71 | public function optionProvider() 72 | { 73 | return array( 74 | array(AbstractPasswordGeneratorClass::OPTION_TEST_BOOLEAN), 75 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER), 76 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER_DEFAULT), 77 | array(AbstractPasswordGeneratorClass::OPTION_TEST_STRING), 78 | array(AbstractPasswordGeneratorClass::OPTION_TEST_STRING_DEFAULT), 79 | ); 80 | } 81 | 82 | public function testGetPossibleOptions(): void 83 | { 84 | $this->assertTrue(is_array($this->_object->getOptions())); 85 | $this->assertSame(count($this->_object->getOptions()), 5); 86 | } 87 | 88 | public function testGetOptionValueDefault(): void 89 | { 90 | $this->assertSame(99, $this->_object->getOptionValue(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER_DEFAULT)); 91 | $this->assertSame('test', $this->_object->getOptionValue(AbstractPasswordGeneratorClass::OPTION_TEST_STRING_DEFAULT)); 92 | } 93 | 94 | /** 95 | * @dataProvider setOptionValueProvider 96 | * 97 | * @param $option 98 | * @param $value 99 | */ 100 | public function testSetOptionValue($option, $value): void 101 | { 102 | $this->_object->setOptionValue($option, $value); 103 | $this->assertSame($this->_object->getOptionValue($option), $value); 104 | } 105 | 106 | /** 107 | * @return array 108 | */ 109 | public function setOptionValueProvider() 110 | { 111 | return array( 112 | array(AbstractPasswordGeneratorClass::OPTION_TEST_BOOLEAN, true), 113 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, 0), 114 | ); 115 | } 116 | 117 | /** 118 | * @dataProvider setOptionExceptionProvider 119 | * 120 | * @param $option 121 | * @param $value 122 | */ 123 | public function testSetExceptionOption($option, $value): void 124 | { 125 | $this->expectException(\InvalidArgumentException::class); 126 | $this->_object->setOptionValue($option, $value); 127 | } 128 | 129 | /** 130 | * @return array 131 | */ 132 | public function setOptionExceptionProvider() 133 | { 134 | return array( 135 | array(AbstractPasswordGeneratorClass::OPTION_TEST_BOOLEAN, 99), 136 | array(AbstractPasswordGeneratorClass::OPTION_TEST_BOOLEAN, null), 137 | array(AbstractPasswordGeneratorClass::OPTION_TEST_BOOLEAN, 'test'), 138 | 139 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, true), 140 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, null), 141 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, 'test'), 142 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, -101), 143 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, 101), 144 | ); 145 | } 146 | 147 | public function testUnknownSetOption(): void 148 | { 149 | $this->expectException(\InvalidArgumentException::class); 150 | $this->_object->setOption('unknown', array()); 151 | } 152 | 153 | public function testUnknownOption(): void 154 | { 155 | $this->assertNull($this->_object->getOption('unknown', array())); 156 | } 157 | 158 | public function testUnknownSetOptionValue(): void 159 | { 160 | $this->expectException(\InvalidArgumentException::class); 161 | $this->_object->setOptionValue('unknown', true); 162 | } 163 | 164 | public function testUnknownOptionValue(): void 165 | { 166 | $this->expectException(\InvalidArgumentException::class); 167 | $this->_object->getOptionValue('unknown', true); 168 | } 169 | 170 | public function testUnknownParameter(): void 171 | { 172 | $this->assertNull($this->_object->getParameter('unknown')); 173 | } 174 | 175 | /** 176 | * @dataProvider parameterProvider 177 | * 178 | * @param $parameter 179 | * @param $value 180 | */ 181 | public function testParameter($parameter, $value): void 182 | { 183 | $this->_object->setParameter($parameter, $value); 184 | 185 | $this->assertSame($value, $this->_object->getParameter($parameter)); 186 | } 187 | 188 | public function parameterProvider() 189 | { 190 | return array( 191 | array('a', 1), 192 | array('ab', null), 193 | array('test', true), 194 | array('test2', 'value'), 195 | ); 196 | } 197 | 198 | /** 199 | * @dataProvider validateValueProvider 200 | * 201 | * @param $option 202 | * @param $value 203 | * @param $return 204 | */ 205 | public function testValidateValue($option, $value, $return): void 206 | { 207 | if (!$return) { 208 | $this->expectException('\InvalidArgumentException'); 209 | } 210 | 211 | $this->_object->setOptionValue($option, $value); 212 | 213 | if ($return) { 214 | $this->assertSame($value, $this->_object->getOptionValue($option)); 215 | } 216 | } 217 | 218 | /** 219 | * @return array 220 | */ 221 | public function validateValueProvider() 222 | { 223 | return array( 224 | array(AbstractPasswordGeneratorClass::OPTION_TEST_BOOLEAN, true, true), 225 | array(AbstractPasswordGeneratorClass::OPTION_TEST_BOOLEAN, 1, false), 226 | array(AbstractPasswordGeneratorClass::OPTION_TEST_BOOLEAN, null, false), 227 | 228 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, 0, true), 229 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, 100, true), 230 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, -100, true), 231 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, true, false), 232 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, null, false), 233 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER, '', false), 234 | 235 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER_DEFAULT, 2147483647, true), 236 | array(AbstractPasswordGeneratorClass::OPTION_TEST_INTEGER_DEFAULT, -2147483648, true), 237 | 238 | array(AbstractPasswordGeneratorClass::OPTION_TEST_STRING, 'abc', true), 239 | array(AbstractPasswordGeneratorClass::OPTION_TEST_STRING, 'a', true), 240 | array(AbstractPasswordGeneratorClass::OPTION_TEST_STRING, '', false), 241 | array(AbstractPasswordGeneratorClass::OPTION_TEST_STRING, 0, false), 242 | array(AbstractPasswordGeneratorClass::OPTION_TEST_STRING, null, false), 243 | array(AbstractPasswordGeneratorClass::OPTION_TEST_STRING, true, false), 244 | 245 | array(AbstractPasswordGeneratorClass::OPTION_TEST_STRING, 'ac', true), 246 | array(AbstractPasswordGeneratorClass::OPTION_TEST_STRING, 'abcd', false), 247 | array(AbstractPasswordGeneratorClass::OPTION_TEST_STRING, '', false), 248 | 249 | array('fail', '', false), 250 | ); 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /Tests/Generator/ComputerPasswordGeneratorTest.php: -------------------------------------------------------------------------------- 1 | _object = new ComputerPasswordGenerator(); 18 | 19 | $this->_object 20 | ->setOptionValue(ComputerPasswordGenerator::OPTION_UPPER_CASE, false) 21 | ->setOptionValue(ComputerPasswordGenerator::OPTION_LOWER_CASE, false) 22 | ->setOptionValue(ComputerPasswordGenerator::OPTION_NUMBERS, false) 23 | ->setOptionValue(ComputerPasswordGenerator::OPTION_SYMBOLS, false) 24 | ->setOptionValue(ComputerPasswordGenerator::OPTION_AVOID_SIMILAR, false); 25 | } 26 | 27 | /** 28 | * @dataProvider lengthProvider 29 | * 30 | * @param $length 31 | */ 32 | public function testLength($length): void 33 | { 34 | $this->_object->setLength($length); 35 | $this->assertSame($this->_object->getLength(), $length); 36 | } 37 | 38 | /** 39 | * @dataProvider lengthProvider 40 | * 41 | * @param $length 42 | */ 43 | public function testGeneratePassword($length): void 44 | { 45 | $this->_object 46 | ->setOptionValue(ComputerPasswordGenerator::OPTION_UPPER_CASE, true) 47 | ->setOptionValue(ComputerPasswordGenerator::OPTION_LOWER_CASE, true) 48 | ->setOptionValue(ComputerPasswordGenerator::OPTION_NUMBERS, true) 49 | ->setOptionValue(ComputerPasswordGenerator::OPTION_SYMBOLS, true) 50 | ->setOptionValue(ComputerPasswordGenerator::OPTION_AVOID_SIMILAR, true); 51 | 52 | $this->_object->setLength($length); 53 | $this->assertSame(\mb_strlen($this->_object->generatePassword()), $length); 54 | } 55 | 56 | /** 57 | * @dataProvider lengthProvider 58 | * 59 | * @param $length 60 | */ 61 | public function testGenerateMultiBytePassword($length): void 62 | { 63 | $this->_object 64 | ->setOptionValue(ComputerPasswordGenerator::OPTION_UPPER_CASE, true) 65 | ->setOptionValue(ComputerPasswordGenerator::OPTION_LOWER_CASE, true) 66 | ->setParameter(ComputerPasswordGenerator::PARAMETER_UPPER_CASE, 'ßAÄÖÜÉÑÇŠŽДЖЮИЫ') 67 | ->setParameter(ComputerPasswordGenerator::PARAMETER_LOWER_CASE, 'ßaäöüéñçšžджюиы') 68 | ; 69 | 70 | $this->_object->setLength($length); 71 | $generatedPassword = $this->_object->generatePassword(); 72 | 73 | $this->assertSame(\mb_strlen($generatedPassword), $length); 74 | 75 | $uppercase = $this->_object->getParameter(ComputerPasswordGenerator::PARAMETER_UPPER_CASE); 76 | $lowercase = $this->_object->getParameter(ComputerPasswordGenerator::PARAMETER_LOWER_CASE); 77 | 78 | // Check each character in the generated password 79 | foreach (mb_str_split($generatedPassword) as $char) { 80 | $this->assertTrue(mb_strpos($uppercase, $char) !== false || mb_strpos($lowercase, $char) !== false, "Character '{$char}' is not in the allowed character sets"); 81 | } 82 | } 83 | 84 | /** 85 | * @return array 86 | */ 87 | public function lengthProvider() 88 | { 89 | return array( 90 | array(1), 91 | array(4), 92 | array(8), 93 | array(16), 94 | ); 95 | } 96 | 97 | /** 98 | * @dataProvider getterSetterProvider 99 | * 100 | * @param $method 101 | */ 102 | public function testGetterSetters($method): void 103 | { 104 | $this->_object->{'set'.$method}(true); 105 | $this->assertTrue($this->_object->{'get'.$method}()); 106 | 107 | $this->_object->{'set'.$method}(false); 108 | $this->assertTrue(!$this->_object->{'get'.$method}()); 109 | } 110 | 111 | /** 112 | * @dataProvider lengthExceptionProvider 113 | * 114 | * @param $param 115 | */ 116 | public function testLengthException($param): void 117 | { 118 | $this->expectException(\InvalidArgumentException::class); 119 | $this->_object->setLength($param); 120 | } 121 | 122 | public function lengthExceptionProvider() 123 | { 124 | return array( 125 | array('a'), 126 | array(false), 127 | array(null), 128 | array(-1), 129 | ); 130 | } 131 | 132 | /** 133 | * @dataProvider getterSetterProvider 134 | * 135 | * @param $method 136 | */ 137 | public function testGetterSettersException($method): void 138 | { 139 | $this->expectException(\InvalidArgumentException::class); 140 | $this->_object->{'set'.$method}(1); 141 | } 142 | 143 | public function getterSetterProvider() 144 | { 145 | return array( 146 | array('Uppercase'), 147 | array('Lowercase'), 148 | array('Numbers'), 149 | array('Symbols'), 150 | array('AvoidSimilar'), 151 | ); 152 | } 153 | 154 | /** 155 | * @dataProvider optionProvider 156 | * 157 | * @param $option 158 | * @param $exists 159 | * @param $dontExist 160 | */ 161 | public function testSetOption($option, $exists, $dontExist): void 162 | { 163 | $this->_object->setOptionValue($option, true); 164 | $availableCharacters = $this->_object->getCharacterList()->getCharacters(); 165 | 166 | foreach ($exists as $check) { 167 | $this->assertStringContainsString($check, $availableCharacters); 168 | } 169 | foreach ($dontExist as $check) { 170 | $this->assertStringNotContainsString($check, $availableCharacters); 171 | } 172 | } 173 | 174 | /** 175 | * @return array 176 | */ 177 | public function optionProvider() 178 | { 179 | return array( 180 | array(ComputerPasswordGenerator::OPTION_UPPER_CASE, array('A', 'B', 'C'), array('a', 'b', 'c')), 181 | array(ComputerPasswordGenerator::OPTION_LOWER_CASE, array('a', 'b', 'c'), array('A', 'B', 'C')), 182 | array(ComputerPasswordGenerator::OPTION_NUMBERS, array('1', '2', '3'), array('a', 'b', 'c', 'A', 'B', 'C')), 183 | array(ComputerPasswordGenerator::OPTION_SYMBOLS, array('+', '=', '?'), array('a', 'b', 'c', 'A', 'B', 'C')), 184 | ); 185 | } 186 | 187 | public function testSetOptionSimilar(): void 188 | { 189 | $exists = array('a', 'b', 'c', 'A', 'B', 'C'); 190 | $dontExist = array('o', 'l', 'O'); 191 | 192 | $this->_object 193 | ->setOptionValue(ComputerPasswordGenerator::OPTION_UPPER_CASE, true) 194 | ->setOptionValue(ComputerPasswordGenerator::OPTION_LOWER_CASE, true) 195 | ->setOptionValue(ComputerPasswordGenerator::OPTION_AVOID_SIMILAR, true); 196 | 197 | $availableCharacters = $this->_object->getCharacterList()->getCharacters(); 198 | 199 | foreach ($exists as $check) { 200 | $this->assertStringContainsString($check, $availableCharacters); 201 | } 202 | foreach ($dontExist as $check) { 203 | $this->assertStringNotContainsString($check, $availableCharacters); 204 | } 205 | } 206 | 207 | /** 208 | * @dataProvider optionsProvider 209 | * 210 | * @param $option 211 | * @param $parameter 212 | */ 213 | public function testSetGet($option, $parameter): void 214 | { 215 | $this->_object 216 | ->setOptionValue($option, true) 217 | ->setParameter($parameter, 'A') 218 | ->setLength(4); 219 | 220 | $this->assertSame('AAAA', $this->_object->generatePassword()); 221 | } 222 | 223 | /** 224 | * @return array 225 | */ 226 | public function optionsProvider() 227 | { 228 | return array( 229 | array(ComputerPasswordGenerator::OPTION_UPPER_CASE, ComputerPasswordGenerator::PARAMETER_UPPER_CASE), 230 | array(ComputerPasswordGenerator::OPTION_LOWER_CASE, ComputerPasswordGenerator::PARAMETER_LOWER_CASE), 231 | array(ComputerPasswordGenerator::OPTION_NUMBERS, ComputerPasswordGenerator::PARAMETER_NUMBERS), 232 | array(ComputerPasswordGenerator::OPTION_SYMBOLS, ComputerPasswordGenerator::PARAMETER_SYMBOLS), 233 | ); 234 | } 235 | 236 | /** 237 | * 238 | */ 239 | public function testAvoidSimilar(): void 240 | { 241 | $this->_object 242 | ->setParameter(ComputerPasswordGenerator::PARAMETER_UPPER_CASE, 'AB') 243 | ->setParameter(ComputerPasswordGenerator::PARAMETER_SIMILAR, 'B') 244 | ->setOptionValue(ComputerPasswordGenerator::OPTION_UPPER_CASE, true) 245 | ->setOptionValue(ComputerPasswordGenerator::OPTION_AVOID_SIMILAR, true); 246 | 247 | $this->_object->setLength(4); 248 | 249 | $this->assertSame('AAAA', $this->_object->generatePassword()); 250 | } 251 | 252 | /** 253 | * 254 | */ 255 | public function testCharacterListException(): void 256 | { 257 | $this->expectException(CharactersNotFoundException::class); 258 | $this->_object->getCharacterList(); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /Tests/Generator/Data/WordList/empty.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackzilla/password-generator/743c8493d2c33ebf9496a1c3c248efeb568fe134/Tests/Generator/Data/WordList/empty.txt -------------------------------------------------------------------------------- /Tests/Generator/Data/WordList/simple.txt: -------------------------------------------------------------------------------- 1 | blancmange 2 | blancmange 3 | blancmange 4 | blancmange 5 | blancmange 6 | blancmange 7 | blancmange 8 | -------------------------------------------------------------------------------- /Tests/Generator/DummyPasswordGeneratorTest.php: -------------------------------------------------------------------------------- 1 | _object = new DummyPasswordGenerator(); 17 | } 18 | 19 | /** 20 | * @dataProvider lengthProvider 21 | * 22 | * @param $length 23 | * @param $comparePassword 24 | */ 25 | public function testGeneratePasswords($length, $comparePassword): void 26 | { 27 | $this->_object->setOptionValue(DummyPasswordGenerator::OPTION_LENGTH, $length); 28 | $passwords = $this->_object->generatePasswords($length); 29 | 30 | $this->assertSame(count($passwords), $length); 31 | 32 | foreach ($passwords as $password) { 33 | $this->assertSame($password, $comparePassword); 34 | } 35 | } 36 | 37 | /** 38 | * @dataProvider lengthProvider 39 | * 40 | * @param $length 41 | * @param $password 42 | */ 43 | public function testGeneratePassword($length, $password) 44 | { 45 | $this->_object->setOptionValue(DummyPasswordGenerator::OPTION_LENGTH, $length); 46 | $this->assertSame($this->_object->generatePassword(), $password); 47 | } 48 | 49 | /** 50 | * @dataProvider lengthProvider 51 | * 52 | * @param $length 53 | */ 54 | public function testLength($length): void 55 | { 56 | $this->_object->setLength($length); 57 | $this->assertSame($this->_object->getLength(), $length); 58 | } 59 | 60 | /** 61 | * @return array 62 | */ 63 | public function lengthProvider() 64 | { 65 | return array( 66 | array(1, 'p'), 67 | array(4, 'pass'), 68 | array(8, 'password'), 69 | array(16, 'password????????'), 70 | ); 71 | } 72 | 73 | /** 74 | * @dataProvider lengthExceptionProvider 75 | * 76 | * @param $param 77 | */ 78 | public function testLengthException($param): void 79 | { 80 | $this->expectException(\InvalidArgumentException::class); 81 | $this->_object->setLength($param); 82 | } 83 | 84 | public function lengthExceptionProvider() 85 | { 86 | return array( 87 | array('a'), 88 | array(false), 89 | array(null), 90 | array(-1), 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Tests/Generator/HumanPasswordGeneratorClass.php: -------------------------------------------------------------------------------- 1 | _object = new HumanPasswordGenerator(); 17 | $this->_object->setWordSeparator(''); 18 | } 19 | 20 | /** 21 | * 22 | */ 23 | public function testWordCount(): void 24 | { 25 | $this->_object->setWordCount(1); 26 | $this->assertSame($this->_object->getWordCount(), 1); 27 | 28 | $this->_object->setWordCount(6); 29 | $this->assertSame($this->_object->getWordCount(), 6); 30 | 31 | $this->expectException('InvalidArgumentException'); 32 | $this->_object->setWordCount(-6); 33 | 34 | $this->expectException('InvalidArgumentException'); 35 | $this->_object->setWordCount('fail'); 36 | } 37 | 38 | /** 39 | * 40 | */ 41 | public function testMinWordLength(): void 42 | { 43 | $this->_object->setMinWordLength(1); 44 | $this->assertSame($this->_object->getMinWordLength(), 1); 45 | 46 | $this->_object->setMinWordLength(6); 47 | $this->assertSame($this->_object->getMinWordLength(), 6); 48 | 49 | $this->expectException('InvalidArgumentException'); 50 | $this->_object->setMinWordLength(-6); 51 | 52 | $this->expectException('InvalidArgumentException'); 53 | $this->_object->setMinWordLength('fail'); 54 | } 55 | 56 | /** 57 | * 58 | */ 59 | public function testMaxWordLength(): void 60 | { 61 | $this->_object->setMaxWordLength(1); 62 | $this->assertSame($this->_object->getMaxWordLength(), 1); 63 | 64 | $this->_object->setMaxWordLength(6); 65 | $this->assertSame($this->_object->getMaxWordLength(), 6); 66 | 67 | $this->expectException('InvalidArgumentException'); 68 | $this->_object->setMaxWordLength(-6); 69 | 70 | $this->expectException('InvalidArgumentException'); 71 | $this->_object->setMaxWordLength('fail'); 72 | } 73 | 74 | /** 75 | * 76 | */ 77 | public function testWordSeparator(): void 78 | { 79 | $this->_object->setWordSeparator(''); 80 | $this->assertSame($this->_object->getWordSeparator(), ''); 81 | 82 | $this->_object->setWordSeparator('-'); 83 | $this->assertSame($this->_object->getWordSeparator(), '-'); 84 | 85 | $this->_object->setWordSeparator('-?*'); 86 | $this->assertSame($this->_object->getWordSeparator(), '-?*'); 87 | 88 | $this->expectException('InvalidArgumentException'); 89 | $this->_object->setWordSeparator(-6); 90 | 91 | $this->expectException('InvalidArgumentException'); 92 | $this->_object->setMaxWordLength(null); 93 | } 94 | 95 | /** 96 | * @return string 97 | */ 98 | public function getSimpleWordList() 99 | { 100 | $reflClass = new \ReflectionClass(get_class($this)); 101 | $filename = dirname($reflClass->getFileName()).DIRECTORY_SEPARATOR.'Data'.DIRECTORY_SEPARATOR.'WordList'.DIRECTORY_SEPARATOR.'simple.txt'; 102 | 103 | return $filename; 104 | } 105 | 106 | /** 107 | * @return string 108 | */ 109 | public function getEmptyWordList() 110 | { 111 | $reflClass = new \ReflectionClass(get_class($this)); 112 | $filename = dirname($reflClass->getFileName()).DIRECTORY_SEPARATOR.'Data'.DIRECTORY_SEPARATOR.'WordList'.DIRECTORY_SEPARATOR.'empty.txt'; 113 | 114 | return $filename; 115 | } 116 | 117 | /** 118 | * 119 | */ 120 | public function testWordList(): void 121 | { 122 | $filename = $this->getSimpleWordList(); 123 | 124 | $this->_object->setWordList($filename); 125 | $this->assertSame($this->_object->getWordList(), $filename); 126 | 127 | $this->expectException('InvalidArgumentException'); 128 | $this->_object->setWordList(7); 129 | } 130 | 131 | public function testWordListException() 132 | { 133 | $this->expectException(\Hackzilla\PasswordGenerator\Exception\WordsNotFoundException::class); 134 | $filename = $this->getEmptyWordList(); 135 | 136 | $this->_object->setWordList($filename); 137 | $this->_object->generatePassword(); 138 | } 139 | 140 | public function testUnknownWordList(): void 141 | { 142 | $this->expectException(\Hackzilla\PasswordGenerator\Exception\FileNotFoundException::class); 143 | $this->_object->setWordList('fail'); 144 | } 145 | 146 | public function testGenerateWordList(): void 147 | { 148 | $filename = $this->getSimpleWordList(); 149 | 150 | $this->_object->setWordList($filename); 151 | $words = $this->_object->generateWordList(); 152 | 153 | $this->assertSame(count($words), 7); 154 | $this->assertSame($words[0], 'blancmange'); 155 | } 156 | 157 | /** 158 | * @dataProvider lengthProvider 159 | * 160 | * @param $length 161 | */ 162 | public function testGeneratePasswords($length): void 163 | { 164 | $filename = $this->getSimpleWordList(); 165 | 166 | $this->_object->setWordList($filename); 167 | $this->_object->setWordCount($length); 168 | $passwords = $this->_object->generatePasswords($length); 169 | 170 | $this->assertSame(count($passwords), $length); 171 | 172 | foreach ($passwords as $password) { 173 | $this->assertSame($password, \str_repeat('blancmange', $length)); 174 | } 175 | } 176 | 177 | /** 178 | * @dataProvider lengthProvider 179 | * 180 | * @param $length 181 | */ 182 | public function testGeneratePassword($length): void 183 | { 184 | $filename = $this->getSimpleWordList(); 185 | 186 | $this->_object->setWordList($filename); 187 | $this->_object->setWordCount($length); 188 | $this->assertSame($this->_object->generatePassword(), \str_repeat('blancmange', $length)); 189 | } 190 | 191 | public function testGeneratePasswordException(): void 192 | { 193 | $this->expectException('\Hackzilla\PasswordGenerator\Exception\FileNotFoundException'); 194 | $this->_object->generatePassword(); 195 | } 196 | 197 | public function testGeneratePasswordImpossiblePasswordLengthException(): void 198 | { 199 | $this->expectException(\Hackzilla\PasswordGenerator\Exception\ImpossiblePasswordLengthException::class); 200 | 201 | $this->_object->setLength(20); 202 | $this->_object->setWordCount(3); 203 | $this->_object->setMinWordLength(10); 204 | $this->_object->setMaxWordLength(10); 205 | 206 | $filename = $this->getSimpleWordList(); 207 | $this->_object->setWordList($filename); 208 | 209 | $this->_object->generatePassword(); 210 | } 211 | 212 | public function testRandomWord(): void 213 | { 214 | $filename = $this->getSimpleWordList(); 215 | $this->_object->setWordList($filename); 216 | 217 | $this->assertEquals('blancmange', $this->_object->randomWord(5, 15)); 218 | } 219 | 220 | public function testRandomWordException(): void 221 | { 222 | $this->expectException(\Hackzilla\PasswordGenerator\Exception\NotEnoughWordsException::class); 223 | 224 | $filename = $this->getSimpleWordList(); 225 | $this->_object->setWordList($filename); 226 | 227 | $this->_object->randomWord(12, 15); 228 | } 229 | 230 | public function testEmptyException(): void 231 | { 232 | $this->expectException(\Hackzilla\PasswordGenerator\Exception\WordsNotFoundException::class); 233 | $generator = new HumanPasswordGeneratorClass(); 234 | 235 | $generator->generatePassword(); 236 | } 237 | 238 | public function testGetPasswordLength(): void 239 | { 240 | $this->_object->setLength(1); 241 | $this->_object->setWordCount(3); 242 | $this->_object->setMinWordLength(3); 243 | $this->_object->setMaxWordLength(6); 244 | $this->_object->setWordSeparator('-'); 245 | 246 | $this->assertEquals(11, $this->_object->getMinPasswordLength()); 247 | $this->assertEquals(20, $this->_object->getMaxPasswordLength()); 248 | } 249 | 250 | /** 251 | * @dataProvider lengthProvider 252 | * 253 | * @param $length 254 | */ 255 | public function testGeneratePasswordWithSeparator($length): void 256 | { 257 | $filename = $this->getSimpleWordList(); 258 | 259 | $this->_object->setWordList($filename); 260 | $this->_object->setWordCount($length); 261 | $this->_object->setWordSeparator('-'); 262 | $this->assertSame($this->_object->generatePassword(), $this->makePassword('blancmange', $length, '-')); 263 | } 264 | 265 | /** 266 | * @param $word 267 | * @param $length 268 | * @param $separator 269 | * 270 | * @return string 271 | */ 272 | private function makePassword($word, $length, $separator) 273 | { 274 | $password = ''; 275 | 276 | for ($i = 0; $i < $length; ++$i) { 277 | if ($i) { 278 | $password .= $separator; 279 | } 280 | 281 | $password .= $word; 282 | } 283 | 284 | return $password; 285 | } 286 | 287 | /** 288 | * @return array 289 | */ 290 | public function lengthProvider() 291 | { 292 | return array( 293 | array(1), 294 | array(4), 295 | array(8), 296 | array(16), 297 | ); 298 | } 299 | 300 | /** 301 | * @dataProvider lengthExceptionProvider 302 | */ 303 | public function testLengthException($length): void 304 | { 305 | $this->expectException(\InvalidArgumentException::class); 306 | $this->_object->setLength($length); 307 | } 308 | 309 | /** 310 | * @return array 311 | */ 312 | public function lengthExceptionProvider() 313 | { 314 | return array( 315 | array(null), 316 | array('A'), 317 | array(-1), 318 | ); 319 | } 320 | /** 321 | * @dataProvider fixedPasswordsProvider 322 | * @param $length 323 | */ 324 | public function testGeneratePasswordFixedLength($length): void 325 | { 326 | $this->_object->setMinWordLength(1); 327 | $this->_object->setMaxWordLength(10); 328 | 329 | $this->_object->setParameter(HumanPasswordGenerator::PARAMETER_WORD_CACHE, array( 330 | 'a', 331 | 'ab', 332 | 'abc', 333 | 'abcd', 334 | 'abcde', 335 | 'abcdef', 336 | 'abcdefg', 337 | 'abcdefgh', 338 | 'abcdefghi', 339 | 'abcdefghij', 340 | 'abcdefghijk', 341 | 'abcdefghijkl', 342 | 'abcdefghijklm', 343 | 'abcdefghijklmn', 344 | 'abcdefghijklmno', 345 | 'abcdefghijklmnop', 346 | )); 347 | 348 | $this->_object->setLength($length); 349 | $this->_object->setWordCount(3); 350 | $this->_object->setWordSeparator('-'); 351 | 352 | $this->assertEquals($length, \mb_strlen($this->_object->generatePassword())); 353 | } 354 | 355 | public function fixedPasswordsProvider() 356 | { 357 | return array( 358 | array(5), 359 | array(10), 360 | array(20), 361 | array(30), 362 | array(32), 363 | ); 364 | } 365 | } 366 | -------------------------------------------------------------------------------- /Tests/Generator/HybridPasswordGeneratorTest.php: -------------------------------------------------------------------------------- 1 | _object = new HybridPasswordGenerator(); 14 | 15 | $this->_object 16 | ->setOptionValue(HybridPasswordGenerator::OPTION_UPPER_CASE, false) 17 | ->setOptionValue(HybridPasswordGenerator::OPTION_LOWER_CASE, false) 18 | ->setOptionValue(HybridPasswordGenerator::OPTION_NUMBERS, false) 19 | ->setOptionValue(HybridPasswordGenerator::OPTION_SYMBOLS, false) 20 | ->setOptionValue(HybridPasswordGenerator::OPTION_AVOID_SIMILAR, false); 21 | } 22 | 23 | public function passwordProvider() 24 | { 25 | return array( 26 | array(3, 4, '-', '/[A-Za-z0-9]{3}\-[A-Za-z0-9]{3}\-[A-Za-z0-9]{3}\-[A-Za-z0-9]{3}/'), 27 | array(1, 1, '-', '/[A-Za-z0-9]{1}/'), 28 | array(1, 2, '', '/[A-Za-z0-9]{1}[A-Za-z0-9]{1}/'), 29 | array(3, 2, '*', '/[A-Za-z0-9]{3}\*[A-Za-z0-9]{3}/'), 30 | ); 31 | } 32 | 33 | /** 34 | * @dataProvider passwordProvider 35 | * 36 | * @param $segmentLength 37 | * @param $segmentCount 38 | * @param $segmentSeparator 39 | * @param $regExp 40 | */ 41 | public function testGeneratePassword($segmentLength, $segmentCount, $segmentSeparator, $regExp): void 42 | { 43 | $this->_object 44 | ->setOptionValue(HybridPasswordGenerator::OPTION_UPPER_CASE, true) 45 | ->setOptionValue(HybridPasswordGenerator::OPTION_LOWER_CASE, true) 46 | ->setOptionValue(HybridPasswordGenerator::OPTION_NUMBERS, true) 47 | ->setOptionValue(HybridPasswordGenerator::OPTION_AVOID_SIMILAR, true); 48 | 49 | $this->_object->setSegmentLength($segmentLength); 50 | $this->_object->setSegmentCount($segmentCount); 51 | $this->_object->setSegmentSeparator($segmentSeparator); 52 | 53 | $this->assertMatchesRegularExpression($regExp, $this->_object->generatePassword()); 54 | } 55 | 56 | /** 57 | * @dataProvider lengthProvider 58 | * 59 | * @param $count 60 | */ 61 | public function testSetSegmentCount($count): void 62 | { 63 | $this->_object->setSegmentCount($count); 64 | $this->assertSame($this->_object->getSegmentCount(), $count); 65 | $this->assertSame($this->_object->getLength(), $count); 66 | } 67 | 68 | public function testSetSegmentCountException(): void 69 | { 70 | $this->expectException(\InvalidArgumentException::class); 71 | $this->_object->setSegmentCount(-1); 72 | } 73 | 74 | /** 75 | * @dataProvider lengthProvider 76 | * 77 | * @param $length 78 | */ 79 | public function testSetSegmentLength($length): void 80 | { 81 | $this->_object->setSegmentLength($length); 82 | $this->assertSame($this->_object->getSegmentLength(), $length); 83 | } 84 | 85 | public function testSetSegmentLengthException(): void 86 | { 87 | $this->expectException(\InvalidArgumentException::class); 88 | $this->_object->setSegmentLength(-1); 89 | } 90 | 91 | /** 92 | * @dataProvider lengthProvider 93 | * 94 | * @param $length 95 | */ 96 | public function testSetLength($length): void 97 | { 98 | $this->_object->setLength($length); 99 | $this->assertSame($this->_object->getLength(), $length); 100 | } 101 | 102 | public function lengthProvider() 103 | { 104 | return array( 105 | array(1), 106 | array(4), 107 | array(8), 108 | array(16), 109 | ); 110 | } 111 | 112 | /** 113 | * @dataProvider segmentProvider 114 | * 115 | * @param $separator 116 | */ 117 | public function testSetSegmentSeparator($separator): void 118 | { 119 | $this->_object->setSegmentSeparator($separator); 120 | $this->assertSame($this->_object->getSegmentSeparator(), $separator); 121 | } 122 | 123 | public function segmentProvider() 124 | { 125 | return array( 126 | array(''), 127 | array('-'), 128 | ); 129 | } 130 | 131 | public function testSetSegmentSeparatorException(): void 132 | { 133 | $this->expectException(\InvalidArgumentException::class); 134 | $this->_object->setSegmentSeparator(-1); 135 | } 136 | 137 | /** 138 | * @dataProvider optionsProvider 139 | * 140 | * @param $option 141 | * @param $parameter 142 | */ 143 | public function testSetGet($option, $parameter): void 144 | { 145 | $this->_object 146 | ->setOptionValue($option, true) 147 | ->setParameter($parameter, 'A') 148 | ->setParameter(HybridPasswordGenerator::PARAMETER_SEPARATOR, '-') 149 | ->setSegmentLength(1) 150 | ->setSegmentCount(4) 151 | ; 152 | 153 | $this->assertSame('A-A-A-A', $this->_object->generatePassword()); 154 | } 155 | 156 | /** 157 | * @return array 158 | */ 159 | public function optionsProvider() 160 | { 161 | return array( 162 | array(HybridPasswordGenerator::OPTION_UPPER_CASE, HybridPasswordGenerator::PARAMETER_UPPER_CASE), 163 | array(HybridPasswordGenerator::OPTION_LOWER_CASE, HybridPasswordGenerator::PARAMETER_LOWER_CASE), 164 | array(HybridPasswordGenerator::OPTION_NUMBERS, HybridPasswordGenerator::PARAMETER_NUMBERS), 165 | array(HybridPasswordGenerator::OPTION_SYMBOLS, HybridPasswordGenerator::PARAMETER_SYMBOLS), 166 | ); 167 | } 168 | 169 | public function testCharacterListException(): void 170 | { 171 | $this->expectException('\Hackzilla\PasswordGenerator\Exception\CharactersNotFoundException'); 172 | $this->_object->getCharacterList(); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Tests/Generator/PronounceablePasswordGeneratorTest.php: -------------------------------------------------------------------------------- 1 | _object = new PronounceablePasswordGenerator(); 20 | } 21 | 22 | /** 23 | * @dataProvider passwordsProvider 24 | * 25 | * @param string $vowels 26 | * @param bool $upperCase 27 | * @param bool $symbols 28 | * @param bool $numbers 29 | */ 30 | public function testGeneratePassword($vowels, $upperCase, $symbols, $numbers): void 31 | { 32 | $this->_object->setParameter(PronounceablePasswordGenerator::PARAMETER_VOWELS, $vowels); 33 | $this->_object->setOptionValue(ComputerPasswordGenerator::OPTION_LENGTH, 16); 34 | $this->_object->setOptionValue(ComputerPasswordGenerator::OPTION_LOWER_CASE, true); 35 | $this->_object->setOptionValue(ComputerPasswordGenerator::OPTION_UPPER_CASE, $upperCase); 36 | $this->_object->setOptionValue(ComputerPasswordGenerator::OPTION_SYMBOLS, $symbols); 37 | $this->_object->setOptionValue(ComputerPasswordGenerator::OPTION_NUMBERS, $numbers); 38 | 39 | $passwords = $this->_object->generatePasswords(10); 40 | foreach ($passwords as $password) { 41 | $passwordLen = mb_strlen($password); 42 | $isLastCharVowel = null; 43 | 44 | for ($i = 0; $i < $passwordLen; $i++) { 45 | $char = $password[$i]; 46 | $isVowel = false !== mb_stripos($vowels, $char); 47 | if (null !== $isLastCharVowel) { 48 | $this->assertNotEquals($isVowel, $isLastCharVowel); 49 | } 50 | $isLastCharVowel = $isVowel; 51 | } 52 | } 53 | } 54 | 55 | public function passwordsProvider() 56 | { 57 | return array( 58 | array('aeiou', true, true, true), 59 | array('aeiou', true, true, false), 60 | array('aeiou', true, false, true), 61 | array('aeiou', false, true, true), 62 | array('aeiou', false, true, false), 63 | array('aeiou', false, false, true), 64 | array('aeiou', false, false, false), 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Tests/Generator/RequirementPasswordGeneratorTest.php: -------------------------------------------------------------------------------- 1 | _object = new RequirementPasswordGenerator(); 19 | 20 | $this->_object 21 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, false) 22 | ->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, false) 23 | ->setOptionValue(RequirementPasswordGenerator::OPTION_NUMBERS, false) 24 | ->setOptionValue(RequirementPasswordGenerator::OPTION_SYMBOLS, false) 25 | ->setOptionValue(RequirementPasswordGenerator::OPTION_AVOID_SIMILAR, false); 26 | } 27 | 28 | /** 29 | * @dataProvider lengthProvider 30 | * 31 | * @param $length 32 | */ 33 | public function testLength($length): void 34 | { 35 | $this->_object->setLength($length); 36 | $this->assertSame($this->_object->getLength(), $length); 37 | } 38 | 39 | /** 40 | * @dataProvider lengthProvider 41 | * 42 | * @param $length 43 | */ 44 | public function testGeneratePassword($length): void 45 | { 46 | $this->_object 47 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 48 | ->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, true) 49 | ->setOptionValue(RequirementPasswordGenerator::OPTION_NUMBERS, true) 50 | ->setOptionValue(RequirementPasswordGenerator::OPTION_SYMBOLS, true) 51 | ->setOptionValue(RequirementPasswordGenerator::OPTION_AVOID_SIMILAR, true); 52 | 53 | $this->_object->setLength($length); 54 | $this->assertSame(\mb_strlen($this->_object->generatePassword()), $length); 55 | } 56 | 57 | /** 58 | * @return array 59 | */ 60 | public function lengthProvider() 61 | { 62 | return array( 63 | array(1), 64 | array(4), 65 | array(8), 66 | array(16), 67 | ); 68 | } 69 | 70 | 71 | /** 72 | * @dataProvider lengthProvider 73 | * 74 | * @param $length 75 | */ 76 | public function testGenerateMultiBytePassword($length): void 77 | { 78 | $this->_object 79 | ->setOptionValue(ComputerPasswordGenerator::OPTION_UPPER_CASE, true) 80 | ->setOptionValue(ComputerPasswordGenerator::OPTION_LOWER_CASE, true) 81 | ->setParameter(ComputerPasswordGenerator::PARAMETER_UPPER_CASE, 'ßAÄÖÜÉÑÇŠŽДЖЮИЫ') 82 | ->setParameter(ComputerPasswordGenerator::PARAMETER_LOWER_CASE, 'ßaäöüéñçšžджюиы') 83 | ; 84 | 85 | $this->_object->setLength($length); 86 | $generatedPassword = $this->_object->generatePassword(); 87 | 88 | $this->assertSame(\mb_strlen($generatedPassword), $length); 89 | 90 | $uppercase = $this->_object->getParameter(ComputerPasswordGenerator::PARAMETER_UPPER_CASE); 91 | $lowercase = $this->_object->getParameter(ComputerPasswordGenerator::PARAMETER_LOWER_CASE); 92 | 93 | // Check each character in the generated password 94 | foreach (mb_str_split($generatedPassword) as $char) { 95 | $this->assertTrue(mb_strpos($uppercase, $char) !== false || mb_strpos($lowercase, $char) !== false, "Character '{$char}' is not in the allowed character sets"); 96 | } 97 | 98 | $this->_object->validatePassword($generatedPassword); 99 | } 100 | 101 | public function testGeneratePasswordNonIntException(): void 102 | { 103 | $this->expectException('\InvalidArgumentException'); 104 | $this->_object->generatePasswords('A'); 105 | } 106 | 107 | public function testGeneratePasswordNegativeIntException(): void 108 | { 109 | $this->expectException('\InvalidArgumentException'); 110 | $this->_object->generatePasswords(-1); 111 | } 112 | 113 | /** 114 | * @dataProvider getterSetterProvider 115 | * 116 | * @param $method 117 | */ 118 | public function testGetterSetters($method): void 119 | { 120 | $this->_object->{'set'.$method}(true); 121 | $this->assertTrue($this->_object->{'get'.$method}()); 122 | 123 | 124 | $this->_object->{'set'.$method}(false); 125 | $this->assertTrue(!$this->_object->{'get'.$method}()); 126 | } 127 | 128 | /** 129 | * @dataProvider lengthExceptionProvider 130 | * 131 | * @param $param 132 | */ 133 | public function testLengthException($param): void 134 | { 135 | $this->expectException(\InvalidArgumentException::class); 136 | $this->_object->setLength($param); 137 | } 138 | 139 | public function lengthExceptionProvider() 140 | { 141 | return array( 142 | array('a'), 143 | array(false), 144 | array(null), 145 | array(-1), 146 | ); 147 | } 148 | 149 | /** 150 | * @dataProvider getterSetterProvider 151 | * 152 | * @param $method 153 | */ 154 | public function testGetterSettersException($method): void 155 | { 156 | $this->expectException(\InvalidArgumentException::class); 157 | $this->_object->{'set'.$method}(1); 158 | } 159 | 160 | public function getterSetterProvider() 161 | { 162 | return array( 163 | array('Uppercase'), 164 | array('Lowercase'), 165 | array('Numbers'), 166 | array('Symbols'), 167 | array('AvoidSimilar'), 168 | ); 169 | } 170 | 171 | /** 172 | * @dataProvider optionProvider 173 | * 174 | * @param $option 175 | * @param $exists 176 | * @param $dontExist 177 | */ 178 | public function testSetOption($option, $exists, $dontExist): void 179 | { 180 | $this->_object->setOptionValue($option, true); 181 | $availableCharacters = $this->_object->getCharacterList()->getCharacters(); 182 | 183 | foreach ($exists as $check) { 184 | $this->assertStringContainsString($check, $availableCharacters); 185 | } 186 | foreach ($dontExist as $check) { 187 | $this->assertStringNotContainsString($check, $availableCharacters); 188 | } 189 | } 190 | 191 | /** 192 | * @return array 193 | */ 194 | public function optionProvider() 195 | { 196 | return array( 197 | array(RequirementPasswordGenerator::OPTION_UPPER_CASE, array('A', 'B', 'C'), array('a', 'b', 'c')), 198 | array(RequirementPasswordGenerator::OPTION_LOWER_CASE, array('a', 'b', 'c'), array('A', 'B', 'C')), 199 | array(RequirementPasswordGenerator::OPTION_NUMBERS, array('1', '2', '3'), array('a', 'b', 'c', 'A', 'B', 'C')), 200 | array(RequirementPasswordGenerator::OPTION_SYMBOLS, array('+', '=', '?'), array('a', 'b', 'c', 'A', 'B', 'C')), 201 | ); 202 | } 203 | 204 | public function testSetOptionSimilar(): void 205 | { 206 | $exists = array('a', 'b', 'c', 'A', 'B', 'C'); 207 | $dontExist = array('o', 'l', 'O'); 208 | 209 | $this->_object 210 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 211 | ->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, true) 212 | ->setOptionValue(RequirementPasswordGenerator::OPTION_AVOID_SIMILAR, true); 213 | 214 | $availableCharacters = $this->_object->getCharacterList()->getCharacters(); 215 | 216 | foreach ($exists as $check) { 217 | $this->assertStringContainsString($check, $availableCharacters); 218 | } 219 | foreach ($dontExist as $check) { 220 | $this->assertStringNotContainsString($check, $availableCharacters); 221 | } 222 | } 223 | 224 | /** 225 | * @dataProvider optionsProvider 226 | * 227 | * @param $option 228 | * @param $parameter 229 | */ 230 | public function testSetGet($option, $parameter): void 231 | { 232 | $this->_object 233 | ->setOptionValue($option, true) 234 | ->setParameter($parameter, 'A') 235 | ->setLength(4); 236 | 237 | $this->assertSame('AAAA', $this->_object->generatePassword()); 238 | } 239 | 240 | /** 241 | * @return array 242 | */ 243 | public function optionsProvider() 244 | { 245 | return array( 246 | array(RequirementPasswordGenerator::OPTION_UPPER_CASE, RequirementPasswordGenerator::PARAMETER_UPPER_CASE), 247 | array(RequirementPasswordGenerator::OPTION_LOWER_CASE, RequirementPasswordGenerator::PARAMETER_LOWER_CASE), 248 | array(RequirementPasswordGenerator::OPTION_NUMBERS, RequirementPasswordGenerator::PARAMETER_NUMBERS), 249 | array(RequirementPasswordGenerator::OPTION_SYMBOLS, RequirementPasswordGenerator::PARAMETER_SYMBOLS), 250 | ); 251 | } 252 | 253 | /** 254 | * 255 | */ 256 | public function testAvoidSimilar(): void 257 | { 258 | $this->_object 259 | ->setParameter(RequirementPasswordGenerator::PARAMETER_UPPER_CASE, 'AB') 260 | ->setParameter(RequirementPasswordGenerator::PARAMETER_SIMILAR, 'B') 261 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 262 | ->setOptionValue(RequirementPasswordGenerator::OPTION_AVOID_SIMILAR, true); 263 | 264 | $this->_object->setLength(4); 265 | 266 | $this->assertSame('AAAA', $this->_object->generatePassword()); 267 | } 268 | 269 | /** 270 | * 271 | */ 272 | public function testCharacterListException(): void 273 | { 274 | $this->expectException('\Hackzilla\PasswordGenerator\Exception\CharactersNotFoundException'); 275 | $this->_object->getCharacterList(); 276 | } 277 | 278 | /** 279 | * @dataProvider validOptionProvider 280 | */ 281 | public function testValidOption($option, $valid): void 282 | { 283 | $this->assertSame($valid, $this->_object->validOption($option)); 284 | } 285 | 286 | public function validOptionProvider() 287 | { 288 | return array( 289 | array(RequirementPasswordGenerator::OPTION_UPPER_CASE, true), 290 | array(RequirementPasswordGenerator::OPTION_LOWER_CASE, true), 291 | array(RequirementPasswordGenerator::OPTION_NUMBERS, true), 292 | array(RequirementPasswordGenerator::OPTION_SYMBOLS, true), 293 | array(null, false), 294 | array('', false), 295 | array(RequirementPasswordGenerator::OPTION_AVOID_SIMILAR, false), 296 | array(RequirementPasswordGenerator::OPTION_LENGTH, false), 297 | ); 298 | } 299 | 300 | /** 301 | * @dataProvider minMaxProvider 302 | * 303 | * @param $option 304 | * @param $count 305 | */ 306 | public function testMinMax($option, $count): void 307 | { 308 | $this->_object->setMinimumCount($option, $count); 309 | $this->assertSame($count, $this->_object->getMinimumCount($option)); 310 | 311 | $this->_object->setMaximumCount($option, $count); 312 | $this->assertSame($count, $this->_object->getMaximumCount($option)); 313 | } 314 | 315 | public function minMaxProvider() 316 | { 317 | return array( 318 | array(RequirementPasswordGenerator::OPTION_UPPER_CASE, null), 319 | array(RequirementPasswordGenerator::OPTION_UPPER_CASE, 1), 320 | array(RequirementPasswordGenerator::OPTION_UPPER_CASE, 2), 321 | array(RequirementPasswordGenerator::OPTION_LOWER_CASE, null), 322 | array(RequirementPasswordGenerator::OPTION_LOWER_CASE, 1), 323 | array(RequirementPasswordGenerator::OPTION_LOWER_CASE, 2), 324 | array(RequirementPasswordGenerator::OPTION_NUMBERS, null), 325 | array(RequirementPasswordGenerator::OPTION_NUMBERS, 1), 326 | array(RequirementPasswordGenerator::OPTION_NUMBERS, 2), 327 | array(RequirementPasswordGenerator::OPTION_SYMBOLS, null), 328 | array(RequirementPasswordGenerator::OPTION_SYMBOLS, 1), 329 | array(RequirementPasswordGenerator::OPTION_SYMBOLS, 2), 330 | ); 331 | } 332 | 333 | public function testSetMinimumCountException(): void 334 | { 335 | $this->expectException('\InvalidArgumentException'); 336 | $this->_object->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 'A'); 337 | } 338 | 339 | public function testSetMaximumCountException(): void 340 | { 341 | $this->expectException('\InvalidArgumentException'); 342 | $this->_object->setMaximumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 'A'); 343 | } 344 | 345 | public function testValidLimits(): void 346 | { 347 | $this->_object 348 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 349 | ->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, true) 350 | ->setOptionValue(RequirementPasswordGenerator::OPTION_NUMBERS, true) 351 | ->setOptionValue(RequirementPasswordGenerator::OPTION_SYMBOLS, true) 352 | ->setLength(4) 353 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 1) 354 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_LOWER_CASE, 1) 355 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_NUMBERS, 1) 356 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_SYMBOLS, 1) 357 | ; 358 | 359 | $this->assertTrue($this->_object->validLimits()); 360 | } 361 | 362 | public function testValidLimitsFalse(): void 363 | { 364 | $this->_object 365 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 366 | ->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, true) 367 | ->setOptionValue(RequirementPasswordGenerator::OPTION_NUMBERS, true) 368 | ->setOptionValue(RequirementPasswordGenerator::OPTION_SYMBOLS, true) 369 | ->setLength(3) 370 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 1) 371 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_LOWER_CASE, 1) 372 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_NUMBERS, 1) 373 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_SYMBOLS, 1) 374 | ; 375 | 376 | $this->assertFalse($this->_object->validLimits()); 377 | } 378 | 379 | public function testvalidLimitsFalse2(): void 380 | { 381 | $this->_object 382 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 383 | ->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, true) 384 | ->setOptionValue(RequirementPasswordGenerator::OPTION_NUMBERS, true) 385 | ->setOptionValue(RequirementPasswordGenerator::OPTION_SYMBOLS, true) 386 | ->setLength(3) 387 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 4) 388 | ; 389 | 390 | $this->assertFalse($this->_object->validLimits()); 391 | } 392 | 393 | public function testValidLimitsMaxFalse(): void 394 | { 395 | $this->_object 396 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 397 | ->setOptionValue(RequirementPasswordGenerator::OPTION_LOWER_CASE, true) 398 | ->setOptionValue(RequirementPasswordGenerator::OPTION_NUMBERS, true) 399 | ->setOptionValue(RequirementPasswordGenerator::OPTION_SYMBOLS, true) 400 | ->setLength(5) 401 | ->setMaximumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 1) 402 | ->setMaximumCount(RequirementPasswordGenerator::OPTION_LOWER_CASE, 1) 403 | ->setMaximumCount(RequirementPasswordGenerator::OPTION_NUMBERS, 1) 404 | ->setMaximumCount(RequirementPasswordGenerator::OPTION_SYMBOLS, 1) 405 | ; 406 | 407 | $this->assertFalse($this->_object->validLimits()); 408 | } 409 | 410 | public function testGeneratePasswordException(): void 411 | { 412 | $this->_object 413 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 414 | ->setLength(4) 415 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 5) 416 | ; 417 | 418 | $this->expectException('\Hackzilla\PasswordGenerator\Exception\ImpossibleMinMaxLimitsException'); 419 | $this->_object->generatePassword(); 420 | } 421 | 422 | public function testMaxPartialValidLimits(): void 423 | { 424 | $this->_object 425 | ->setLength(4) 426 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 427 | ->setMaximumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 1) 428 | ; 429 | 430 | $this->assertFalse($this->_object->validLimits()); 431 | } 432 | 433 | public function testMinPartialValidLimits(): void 434 | { 435 | $this->_object 436 | ->setLength(4) 437 | ->setOptionValue(RequirementPasswordGenerator::OPTION_UPPER_CASE, true) 438 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 5) 439 | ; 440 | 441 | $this->assertFalse($this->_object->validLimits()); 442 | } 443 | 444 | /** 445 | * @dataProvider validatePasswordProvider 446 | * 447 | * @param $password 448 | * @param $option 449 | * @param $min 450 | * @param $max 451 | * @param $valid 452 | */ 453 | public function testValidatePassword($password, $option, $min, $max, $valid): void 454 | { 455 | $this->_object->setMinimumCount($option, $min); 456 | $this->_object->setMaximumCount($option, $max); 457 | 458 | $this->assertSame($valid, $this->_object->validatePassword($password)); 459 | } 460 | 461 | public function validatePasswordProvider() 462 | { 463 | return array( 464 | array('ABCDef', RequirementPasswordGenerator::OPTION_UPPER_CASE, 2, 3, false), 465 | array('ABCdef', RequirementPasswordGenerator::OPTION_UPPER_CASE, 2, 3, true), 466 | array('ABcdef', RequirementPasswordGenerator::OPTION_UPPER_CASE, 2, 3, true), 467 | array('Abcdef', RequirementPasswordGenerator::OPTION_UPPER_CASE, 2, 3, false), 468 | array('ABCdef^\'%', RequirementPasswordGenerator::OPTION_SYMBOLS, 2, 3, true), 469 | array('ABcdef!@', RequirementPasswordGenerator::OPTION_SYMBOLS, 2, 3, true), 470 | array('Abcdef!', RequirementPasswordGenerator::OPTION_SYMBOLS, 2, 3, false), 471 | ); 472 | } 473 | 474 | /** 475 | * @dataProvider minLengthProvider 476 | * 477 | * @param $length 478 | */ 479 | public function testMinGeneratePassword($length): void 480 | { 481 | $this->_object 482 | ->setOptionValue(ComputerPasswordGenerator::OPTION_UPPER_CASE, true) 483 | ->setOptionValue(ComputerPasswordGenerator::OPTION_LOWER_CASE, true) 484 | ->setOptionValue(ComputerPasswordGenerator::OPTION_NUMBERS, true) 485 | ->setOptionValue(ComputerPasswordGenerator::OPTION_SYMBOLS, true) 486 | ->setOptionValue(ComputerPasswordGenerator::OPTION_AVOID_SIMILAR, true) 487 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_UPPER_CASE, 2) 488 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_LOWER_CASE, 2) 489 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_NUMBERS, 2) 490 | ->setMinimumCount(RequirementPasswordGenerator::OPTION_SYMBOLS, 2) 491 | ; 492 | 493 | $this->_object->setLength($length); 494 | 495 | $this->assertTrue($this->_object->validLimits()); 496 | 497 | $passwords = $this->_object->generatePasswords(5); 498 | $this->assertCount(5, $passwords); 499 | 500 | foreach ($passwords as $password) { 501 | $this->assertSame($length, \mb_strlen($password)); 502 | } 503 | } 504 | 505 | /** 506 | * @return array 507 | */ 508 | public function minLengthProvider() 509 | { 510 | return array( 511 | array(8), 512 | array(16), 513 | ); 514 | } 515 | 516 | /** 517 | * @dataProvider countOptionExceptionProvider 518 | * @expectException \Hackzilla\PasswordGenerator\Exception\InvalidOptionException 519 | * 520 | * @param $method 521 | * @param $option 522 | */ 523 | public function testCountOptionException($method, $option): void 524 | { 525 | $this->expectException(InvalidOptionException::class); 526 | $this->_object->{$method}($option, 1); 527 | } 528 | 529 | public function countOptionExceptionProvider() 530 | { 531 | return array( 532 | array('setMinimumCount', 'INVALID_OPTION'), 533 | array('setMaximumCount', 'INVALID_OPTION'), 534 | ); 535 | } 536 | 537 | /** 538 | * @dataProvider countValueExceptionProvider 539 | * 540 | * @param $method 541 | * @param $option 542 | */ 543 | public function testCountValueException($method, $option): void 544 | { 545 | $this->expectException(\InvalidArgumentException::class); 546 | $this->_object->{$method}($option, 0); 547 | } 548 | 549 | public function countValueExceptionProvider() 550 | { 551 | return array( 552 | array('setMinimumCount', 'INVALID_OPTION'), 553 | array('setMaximumCount', 'INVALID_OPTION'), 554 | ); 555 | } 556 | } 557 | -------------------------------------------------------------------------------- /Tests/Model/CharacterSetTest.php: -------------------------------------------------------------------------------- 1 | assertSame($result, $characterSet->getCharacters()); 20 | } 21 | 22 | public function characterProvider() 23 | { 24 | return array( 25 | array('ABC', 'ABC'), 26 | array('', ''), 27 | ); 28 | } 29 | 30 | /** 31 | * @dataProvider castCharacterProvider 32 | * 33 | * @param $characters 34 | * @param $result 35 | */ 36 | public function testConstructCast($characters, $result): void 37 | { 38 | $characterSet = new CharacterSet($characters); 39 | 40 | $this->assertSame($result, $characterSet->__toString()); 41 | $this->assertSame($result, (string) $characterSet); 42 | } 43 | 44 | public function castCharacterProvider() 45 | { 46 | return array( 47 | array('ABC', 'ABC'), 48 | array('', ''), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Tests/Model/Option/BooleanOptionTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Hackzilla\PasswordGenerator\Model\Option\OptionInterface', $option); 14 | $this->assertInstanceOf('Hackzilla\PasswordGenerator\Model\Option\BooleanOption', $option); 15 | } 16 | 17 | public function testType() 18 | { 19 | $option = new BooleanOption(); 20 | 21 | $this->assertSame(BooleanOption::TYPE_BOOLEAN, $option->getType()); 22 | } 23 | 24 | /** 25 | * @dataProvider validValueProvider 26 | * 27 | * @param mixed $value 28 | */ 29 | public function testValidValue($value) 30 | { 31 | $option = new BooleanOption(); 32 | $option->setValue($value); 33 | 34 | $this->assertSame($option->getValue(), $value); 35 | } 36 | 37 | public function validValueProvider() 38 | { 39 | return array( 40 | array(true), 41 | array(false), 42 | ); 43 | } 44 | 45 | /** 46 | * @dataProvider invalidValueProvider 47 | * 48 | * @param mixed $value 49 | */ 50 | public function testInvalidValue($value) 51 | { 52 | $this->expectException(\InvalidArgumentException::class); 53 | 54 | $option = new BooleanOption(); 55 | $option->setValue($value); 56 | } 57 | 58 | public function invalidValueProvider() 59 | { 60 | return array( 61 | array(1), 62 | array(null), 63 | array(1.1), 64 | array('a'), 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Tests/Model/Option/IntegerOptionTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Hackzilla\PasswordGenerator\Model\Option\OptionInterface', $option); 14 | $this->assertInstanceOf('Hackzilla\PasswordGenerator\Model\Option\IntegerOption', $option); 15 | } 16 | 17 | public function testType() 18 | { 19 | $option = new IntegerOption(); 20 | 21 | $this->assertSame(IntegerOption::TYPE_INTEGER, $option->getType()); 22 | } 23 | 24 | /** 25 | * @dataProvider validValueProvider 26 | * 27 | * @param mixed $value 28 | */ 29 | public function testValidValue($value) 30 | { 31 | $option = new IntegerOption(); 32 | $option->setValue($value); 33 | 34 | $this->assertSame($option->getValue(), $value); 35 | } 36 | 37 | public function validValueProvider() 38 | { 39 | return array( 40 | array(1), 41 | ); 42 | } 43 | 44 | /** 45 | * @dataProvider invalidValueProvider 46 | * 47 | * @param mixed $value 48 | */ 49 | public function testInvalidValue($value) 50 | { 51 | $this->expectException(\InvalidArgumentException::class); 52 | 53 | $option = new IntegerOption(); 54 | $option->setValue($value); 55 | } 56 | 57 | public function invalidValueProvider() 58 | { 59 | return array( 60 | array(true), 61 | array(false), 62 | array(null), 63 | array(1.1), 64 | array('a'), 65 | ); 66 | } 67 | 68 | /** 69 | * @dataProvider minMaxProvider 70 | * 71 | * @param $min 72 | * @param $max 73 | * @param $value 74 | */ 75 | public function testMinMax($min, $max, $value) 76 | { 77 | $option = new IntegerOption(array('min' => $min, 'max' => $max)); 78 | $option->setValue($value); 79 | 80 | $this->assertSame($option->getValue(), $value); 81 | } 82 | 83 | public function minMaxProvider() 84 | { 85 | return array( 86 | array(0, 255, 10), 87 | array(10, 10, 10), 88 | array(10, 15, 10), 89 | array(-100, 0, -10), 90 | array(-100, -10, -50), 91 | ); 92 | } 93 | 94 | /** 95 | * @dataProvider minMaxExceptionProvider 96 | * 97 | * @param $min 98 | * @param $max 99 | * @param $value 100 | */ 101 | public function testMinMaxException($min, $max, $value) 102 | { 103 | $this->expectException(\InvalidArgumentException::class); 104 | 105 | $option = new IntegerOption(array('min' => $min, 'max' => $max)); 106 | $option->setValue($value); 107 | } 108 | 109 | public function minMaxExceptionProvider() 110 | { 111 | return array( 112 | array(0, 255, 256), 113 | array(10, 10, 11), 114 | array(10, 15, -12), 115 | array(-100, 0, -101), 116 | array(-100, -10, -9), 117 | ); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Tests/Model/Option/OptionClass.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Hackzilla\PasswordGenerator\Model\Option\OptionInterface', $option); 20 | $this->assertInstanceOf($namespace, $option); 21 | $this->assertSame($type, $option->getType()); 22 | } 23 | 24 | public function testCreateFromTypeNull() 25 | { 26 | $this->assertNull(Option::createFromType('fail')); 27 | } 28 | 29 | public function typeProvider() 30 | { 31 | return array( 32 | array(Option::TYPE_BOOLEAN, 'Hackzilla\PasswordGenerator\Model\Option\BooleanOption'), 33 | array(Option::TYPE_INTEGER, 'Hackzilla\PasswordGenerator\Model\Option\IntegerOption'), 34 | array(Option::TYPE_STRING, 'Hackzilla\PasswordGenerator\Model\Option\StringOption'), 35 | ); 36 | } 37 | 38 | public function testDefault() 39 | { 40 | $option = new OptionClass(); 41 | $this->assertSame($option->getValue(), null); 42 | 43 | $option = new OptionClass(array('default' => 1)); 44 | $this->assertSame($option->getValue(), 1); 45 | 46 | $option = new OptionClass(array('default' => 'a')); 47 | $this->assertSame($option->getValue(), 'a'); 48 | } 49 | 50 | /** 51 | * @dataProvider valueProvider 52 | * 53 | * @param mixed $value 54 | */ 55 | public function testValue($value) 56 | { 57 | $option = new OptionClass(); 58 | $option->setValue($value); 59 | 60 | $this->assertSame($option->getValue(), $value); 61 | } 62 | 63 | public function valueProvider() 64 | { 65 | return array( 66 | array(true), 67 | array(1), 68 | array(null), 69 | array(1.1), 70 | array('a'), 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Tests/Model/Option/StringOptionTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('Hackzilla\PasswordGenerator\Model\Option\OptionInterface', $option); 14 | $this->assertInstanceOf('Hackzilla\PasswordGenerator\Model\Option\StringOption', $option); 15 | } 16 | 17 | public function testType() 18 | { 19 | $option = new StringOption(); 20 | 21 | $this->assertSame(StringOption::TYPE_STRING, $option->getType()); 22 | } 23 | 24 | /** 25 | * @dataProvider validValueProvider 26 | * 27 | * @param mixed $value 28 | */ 29 | public function testValidValue($value) 30 | { 31 | $option = new StringOption(); 32 | $option->setValue($value); 33 | 34 | $this->assertSame($option->getValue(), $value); 35 | } 36 | 37 | public function validValueProvider() 38 | { 39 | return array( 40 | array('a'), 41 | ); 42 | } 43 | 44 | /** 45 | * @dataProvider invalidValueProvider 46 | * 47 | * @param mixed $value 48 | */ 49 | public function testInvalidValue($value) 50 | { 51 | $this->expectException(\InvalidArgumentException::class); 52 | 53 | $option = new StringOption(); 54 | $option->setValue($value); 55 | } 56 | 57 | public function invalidValueProvider() 58 | { 59 | return array( 60 | array(true), 61 | array(false), 62 | array(1), 63 | array(null), 64 | array(1.1), 65 | ); 66 | } 67 | 68 | /** 69 | * @dataProvider minMaxExceptionProvider 70 | * 71 | * @param $min 72 | * @param $max 73 | * @param $value 74 | */ 75 | public function testMinMaxException($min, $max, $value) 76 | { 77 | $this->expectException(\InvalidArgumentException::class); 78 | 79 | $option = new StringOption(array('min' => $min, 'max' => $max)); 80 | $option->setValue($value); 81 | } 82 | 83 | public function minMaxExceptionProvider() 84 | { 85 | return array( 86 | array(0, 5, 'abcdef'), 87 | array(0, 1, 'ab'), 88 | array(1, 15, ''), 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Tests/RandomGenerator/NoRandomGeneratorTest.php: -------------------------------------------------------------------------------- 1 | _object = new NoRandomGenerator(); 14 | } 15 | 16 | public function rangeProvider() 17 | { 18 | return array( 19 | array(1, 100), 20 | array(10, 20), 21 | array(0, 5), 22 | array(1, 2), 23 | ); 24 | } 25 | 26 | /** 27 | * @dataProvider rangeProvider 28 | * 29 | * @param int $min 30 | * @param int $max 31 | */ 32 | public function testRandomGenerator($min, $max) 33 | { 34 | $randomInteger = $this->_object->randomInteger($min, $max); 35 | 36 | $this->assertSame($min, $randomInteger); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Tests/RandomGenerator/Php5RandomGeneratorTest.php: -------------------------------------------------------------------------------- 1 | _object = new Php5RandomGenerator(); 14 | } 15 | 16 | public function rangeProvider() 17 | { 18 | return array( 19 | array(1, 100), 20 | array(10, 20), 21 | array(0, 5), 22 | array(1, 2), 23 | ); 24 | } 25 | 26 | /** 27 | * @dataProvider rangeProvider 28 | * 29 | * @param int $min 30 | * @param int $max 31 | */ 32 | public function testRandomGenerator($min, $max): void 33 | { 34 | $randomInteger = $this->_object->randomInteger($min, $max); 35 | 36 | $this->assertGreaterThanOrEqual($min, $randomInteger); 37 | $this->assertLessThanOrEqual($max, $randomInteger); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tests/RandomGenerator/Php7RandomGeneratorTest.php: -------------------------------------------------------------------------------- 1 | _object = new Php7RandomGenerator(); 19 | } 20 | 21 | public function rangeProvider() 22 | { 23 | return array( 24 | array(1, 100), 25 | array(10, 20), 26 | array(0, 5), 27 | array(1, 2), 28 | ); 29 | } 30 | 31 | /** 32 | * @dataProvider rangeProvider 33 | * 34 | * @param int $min 35 | * @param int $max 36 | */ 37 | public function testRandomGenerator($min, $max): void 38 | { 39 | $randomInteger = $this->_object->randomInteger($min, $max); 40 | 41 | $this->assertGreaterThanOrEqual($min, $randomInteger); 42 | $this->assertLessThanOrEqual($max, $randomInteger); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/ReadMeTest.php: -------------------------------------------------------------------------------- 1 | setOptionValue(ComputerPasswordGenerator::OPTION_UPPER_CASE, true) 19 | ->setOptionValue(ComputerPasswordGenerator::OPTION_LOWER_CASE, true) 20 | ->setOptionValue(ComputerPasswordGenerator::OPTION_NUMBERS, true) 21 | ->setOptionValue(ComputerPasswordGenerator::OPTION_SYMBOLS, false) 22 | ; 23 | 24 | $this->assertIsString($generator->generatePassword()); 25 | } 26 | 27 | public function testMorePasswordsUsage() 28 | { 29 | $generator = new ComputerPasswordGenerator(); 30 | 31 | $generator 32 | ->setUppercase() 33 | ->setLowercase() 34 | ->setNumbers() 35 | ->setSymbols(false) 36 | ->setLength(12); 37 | 38 | $passwords = $generator->generatePasswords(10); 39 | 40 | $this->assertCount(10, $passwords); 41 | $this->assertIsString($passwords[0]); 42 | } 43 | 44 | public function testHybridPasswordGeneratorUsage() 45 | { 46 | $generator = new HybridPasswordGenerator(); 47 | 48 | $generator 49 | ->setUppercase() 50 | ->setLowercase() 51 | ->setNumbers() 52 | ->setSymbols(false) 53 | ->setSegmentLength(3) 54 | ->setSegmentCount(4) 55 | ->setSegmentSeparator('-'); 56 | 57 | $passwords = $generator->generatePasswords(10); 58 | 59 | $this->assertCount(10, $passwords); 60 | $this->assertIsString($passwords[0]); 61 | } 62 | 63 | public function testHumanPasswordGeneratorUsage() 64 | { 65 | $generator = new HumanPasswordGenerator(); 66 | 67 | $generator 68 | ->setWordList('/usr/share/dict/words') 69 | ->setMinWordLength(5) 70 | ->setMaxWordLength(8) 71 | ->setWordCount(3) 72 | ->setWordSeparator('-'); 73 | 74 | $passwords = $generator->generatePasswords(10); 75 | 76 | $this->assertCount(10, $passwords); 77 | $this->assertIsString($passwords[0]); 78 | } 79 | 80 | /** 81 | * @throws \Hackzilla\PasswordGenerator\Exception\FileNotFoundException 82 | */ 83 | public function testPhp5RandomGeneratorUsage() 84 | { 85 | $generator = new HumanPasswordGenerator(); 86 | 87 | $generator 88 | ->setRandomGenerator(new Php5RandomGenerator()) 89 | ->setWordList('/usr/share/dict/words') 90 | ->setMinWordLength(5) 91 | ->setMaxWordLength(8) 92 | ->setWordCount(3) 93 | ->setWordSeparator('-'); 94 | 95 | $passwords = $generator->generatePasswords(10); 96 | 97 | $this->assertCount(10, $passwords); 98 | $this->assertIsString($passwords[0]); 99 | } 100 | 101 | /** 102 | * @requires PHP 7 103 | * 104 | * @throws \Hackzilla\PasswordGenerator\Exception\FileNotFoundException 105 | */ 106 | public function testPhp7RandomGeneratorUsage() 107 | { 108 | $generator = new HumanPasswordGenerator(); 109 | 110 | $generator 111 | ->setRandomGenerator(new Php7RandomGenerator()) 112 | ->setWordList('/usr/share/dict/words') 113 | ->setMinWordLength(5) 114 | ->setMaxWordLength(8) 115 | ->setWordCount(3) 116 | ->setWordSeparator('-'); 117 | 118 | $passwords = $generator->generatePasswords(10); 119 | 120 | $this->assertCount(10, $passwords); 121 | $this->assertIsString($passwords[0]); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | =8.0", 15 | "ext-mbstring": "*" 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "^9.0" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Hackzilla\\PasswordGenerator\\": "" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./ 6 | 7 | 8 | 9 | ./Resources 10 | ./Tests 11 | ./vendor 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ./Tests 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /travis.phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 17 | ./Tests 18 | 19 | 20 | 21 | 22 | 23 | ./ 24 | 25 | ./Resources 26 | ./Tests 27 | ./vendor 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | --------------------------------------------------------------------------------