Skip to content

Commit 541c87a

Browse files
authored
Merge pull request #30 from nicobleiler/dev
Release: Promote dev to alpha
2 parents c0dc6bd + 7d38463 commit 541c87a

7 files changed

Lines changed: 117 additions & 5 deletions

File tree

.coderabbit.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
2+
language: "en-US"
3+
early_access: true
4+
5+
reviews:
6+
profile: "assertive"
7+
high_level_summary: true
8+
collapse_walkthrough: false
9+
review_status: false
10+
11+
auto_review:
12+
enabled: true
13+
drafts: false
14+
ignore_title_keywords:
15+
- "WIP"
16+
base_branches:
17+
- dev
18+
- alpha

.gitattributes

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
* text=auto
2+
3+
*.md diff=markdown
4+
*.php diff=php
5+
6+
/.github export-ignore
7+
/tests export-ignore
8+
/benchmarks export-ignore
9+
/scripts export-ignore
10+
.gitattributes export-ignore
11+
.gitignore export-ignore
12+
.coderabbit.yaml export-ignore
13+
AGENTS.md export-ignore
14+
CONTRIBUTING.md export-ignore
15+
phpbench.json export-ignore
16+
phpstan.neon export-ignore
17+
phpunit.xml export-ignore
18+
pint.json export-ignore
19+
rector.php export-ignore
20+
renovate.json export-ignore
21+
.releaserc export-ignore

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
[![PHP Version](https://img.shields.io/packagist/php-v/nicobleiler/php-passphrase.svg)](https://packagist.org/packages/nicobleiler/php-passphrase)
66
[![Code Size](https://img.shields.io/github/languages/code-size/nicobleiler/php-passphrase)](https://github.com/nicobleiler/php-passphrase)
77
[![Wordlist Size](https://img.shields.io/github/size/nicobleiler/php-passphrase/resources/wordlists/eff_large_wordlist.php?label=wordlist)](https://github.com/nicobleiler/php-passphrase/blob/master/resources/wordlists/eff_large_wordlist.php)
8-
[![CI](https://github.com/nicobleiler/php-passphrase/actions/workflows/test.yml/badge.svg)](https://github.com/nicobleiler/php-passphrase/actions/workflows/test.yml)[![License](https://img.shields.io/packagist/l/nicobleiler/php-passphrase.svg)](LICENSE)
8+
[![CI](https://github.com/nicobleiler/php-passphrase/actions/workflows/test.yml/badge.svg)](https://github.com/nicobleiler/php-passphrase/actions/workflows/test.yml)
9+
[![License](https://img.shields.io/packagist/l/nicobleiler/php-passphrase.svg)](LICENSE)
910

1011
A Bitwarden-inspired passphrase generator for PHP with first-class Laravel integration.
1112

@@ -111,7 +112,7 @@ echo $generator->generate(); // deterministic output
111112

112113
| Parameter | Type | Default | Description |
113114
|---|---|---|---|
114-
| `numWords` | `?int` | `3` | Number of words (3–20). `null` uses instance/config default. |
115+
| `numWords` | `?int` | `3` | Number of words (minimum of 3). `null` uses instance/config default. |
115116
| `wordSeparator` | `?string` | `'-'` | Character(s) between words. `null` uses instance/config default. |
116117
| `capitalize` | `?bool` | `false` | Capitalize the first letter of each word. `null` uses instance/config default. |
117118
| `includeNumber` | `?bool` | `false` | Append a random digit (0–9) to one random word. `null` uses instance/config default. |

config/passphrase.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
|--------------------------------------------------------------------------
99
|
1010
| The default number of words to include in a generated passphrase.
11-
| Must be between 3 and 20.
11+
| Must be a minimum of 3.
1212
|
1313
*/
1414
'num_words' => 3,

src/PassphraseGenerator.php

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,48 @@
1111

1212
class PassphraseGenerator
1313
{
14+
/**
15+
* Minimum allowed number of words in a generated passphrase.
16+
*
17+
* @internal
18+
*/
1419
public const MINIMUM_NUM_WORDS = 3;
1520

21+
/**
22+
* Maximum allowed number of words in a generated passphrase.
23+
*
24+
* @internal
25+
*
26+
* @deprecated This constant is planned for removal in the next major version. Do not rely on a fixed maximum word count.
27+
*/
1628
public const MAXIMUM_NUM_WORDS = 20;
1729

30+
/**
31+
* Default number of words used when no explicit value is provided.
32+
*
33+
* @internal
34+
*/
1835
public const DEFAULT_NUM_WORDS = 3;
1936

37+
/**
38+
* Default separator used between words in generated passphrases.
39+
*
40+
* @internal
41+
*/
2042
public const DEFAULT_WORD_SEPARATOR = '-';
2143

44+
/**
45+
* Default capitalization setting for generated passphrases.
46+
*
47+
* @internal
48+
*/
2249
public const DEFAULT_CAPITALIZE = false;
2350

51+
/**
52+
* Default setting for appending a random digit to a generated passphrase.
53+
*
54+
* @internal
55+
*/
2456
public const DEFAULT_INCLUDE_NUMBER = false;
2557

2658
private WordList $wordList;
@@ -71,10 +103,10 @@ public function setDefaults(
71103
* Parameters default to the instance defaults set via setDefaults().
72104
* In Laravel, these come from config/passphrase.php.
73105
*
74-
* @param ?int $numWords Number of words (MINIMUM_NUM_WORDS-MAXIMUM_NUM_WORDS), null to use instance default
106+
* @param ?int $numWords Number of words (minimum 3), null to use instance default
75107
* @param ?string $wordSeparator Character(s) to separate words, null to use instance default
76108
* @param ?bool $capitalize Capitalize first letter of each word, null to use instance default
77-
* @param ?bool $includeNumber Append a random digit to a random word, null to use instance default
109+
* @param ?bool $includeNumber Append a random digit (0-9) to a random word, null to use instance default
78110
* @param ?int $targetEntropyBits Optional. If set, adjusts numWords to meet or exceed this target entropy.
79111
* Entropy is calculated conservatively only based on the number of words in the word list, ignoring the additional entropy from numbers.
80112
*/

tests/LaravelIntegrationTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,24 @@ public function test_config_include_number_is_applied(): void
211211
$result = Passphrase::generate();
212212
$this->assertMatchesRegularExpression('/\d/', $result, 'Expected passphrase to contain a digit');
213213
}
214+
215+
public function test_target_entropy_bits_with_excluded_words_config(): void
216+
{
217+
$words = ['alpha', 'bravo', 'charlie', 'delta', 'echo', 'foxtrot', 'golf', 'hotel'];
218+
$excluded = ['echo', 'foxtrot', 'golf', 'hotel'];
219+
$diff = array_diff($words, $excluded);
220+
$targetEntropyBits = 10;
221+
$entropyPerWord = log(count($diff), 2);
222+
$expectedNumWords = (int) ceil($targetEntropyBits / $entropyPerWord);
223+
224+
config([
225+
'passphrase.word_list' => $words,
226+
'passphrase.excluded_words' => $excluded,
227+
]);
228+
229+
$this->refreshServiceProvider();
230+
231+
$result = Passphrase::generate(wordSeparator: '-', targetEntropyBits: $targetEntropyBits);
232+
$this->assertCount($expectedNumWords, explode('-', $result));
233+
}
214234
}

tests/PassphraseGeneratorTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,26 @@ public function test_target_entropy_bits_overrides_num_words_param(): void
452452
$this->assertCount($expectedNumWords, explode('-', $result));
453453
}
454454

455+
public function test_target_entropy_bits_with_excluded_words_increases_word_count(): void
456+
{
457+
$words = ['alpha', 'bravo', 'charlie', 'delta', 'echo', 'foxtrot', 'golf', 'hotel'];
458+
$wordList = WordList::fromArray($words);
459+
$targetEntropyBits = 10;
460+
461+
$generator = new PassphraseGenerator($wordList);
462+
463+
$expectedNumWords = (int) ceil($targetEntropyBits / $wordList->entropyPerWord());
464+
$result = $generator->generate(wordSeparator: '|', targetEntropyBits: $targetEntropyBits);
465+
$this->assertCount($expectedNumWords, explode('|', $result));
466+
467+
$filteredWordList = $wordList->excludeWords(['echo', 'foxtrot', 'golf', 'hotel']);
468+
$generatorWithExcluded = new PassphraseGenerator($filteredWordList);
469+
470+
$expectedNumWordsWithExcluded = (int) ceil($targetEntropyBits / $filteredWordList->entropyPerWord());
471+
$resultWithExcluded = $generatorWithExcluded->generate(wordSeparator: '|', targetEntropyBits: $targetEntropyBits);
472+
$this->assertCount($expectedNumWordsWithExcluded, explode('|', $resultWithExcluded));
473+
}
474+
455475
// -------------------------------------------------------
456476
// EFF word list tests
457477
// -------------------------------------------------------

0 commit comments

Comments
 (0)