### Install OTPHP via Composer
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/index.md
Use this command to add the library to your PHP project.
```sh
composer require spomky-labs/otphp
```
--------------------------------
### Migration Path Examples
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Comparison of mutable v11.3 code, transitional v11.4 code, and required v12.0 immutable code.
```php
use OTPHP\TOTP;
use OTPHP\InternalClock;
$totp = TOTP::generate(new InternalClock());
$totp->setLabel('alice@example.com');
$totp->setIssuer('My Service');
$totp->setDigits(8);
// $totp is modified in place
```
```php
use OTPHP\TOTP;
use OTPHP\InternalClock;
// Old way still works but triggers deprecation warnings
$totp = TOTP::generate(new InternalClock());
$totp->setLabel('alice@example.com'); // Deprecated warning
// New immutable way
$totp = TOTP::generate(new InternalClock())
->withLabel('alice@example.com')
->withIssuer('My Service')
->withDigits(8);
// Each with*() method returns a new instance
```
```php
use OTPHP\TOTP;
use OTPHP\InternalClock;
// Only immutable methods available
$totp = TOTP::generate(new InternalClock())
->withLabel('alice@example.com')
->withIssuer('My Service')
->withDigits(8);
```
--------------------------------
### Production Clock Implementation
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Installing and using a PSR-20 compliant clock for production environments.
```php
composer require symfony/clock
```
```php
use Symfony\Component\Clock\NativeClock;
use OTPHP\TOTP;
$clock = new NativeClock();
$totp = TOTP::generate($clock);
```
--------------------------------
### Generate QR Code with BaconQrCode
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md
Install the BaconQrCode library and render the provisioning URI as a base64-encoded PNG image.
```shell
composer require bacon/bacon-qr-code:^3.0
```
```php
withLabel('alice@google.com'); // The label (string)
$renderer = new GDLibRenderer(250);
$writer = new Writer($renderer);
$qr = $writer->writeString($totp->getProvisioningUri());
echo '
';
```
--------------------------------
### Generate QR Code with endroid/qr-code
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md
Install the endroid/qr-code library and use the builder API to generate a data URI for the QR code.
```shell
composer require endroid/qr-code
```
```php
withLabel('alice@google.com'); // The label (string)
$builder = new Builder(
writer: new PngWriter(),
data: $totp->getProvisioningUri(),
size: 250,
);
$result = $builder->build();
echo '
';
```
--------------------------------
### Configure TOTP Epoch
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Set a custom epoch to define the starting timestamp for time-based calculations. Use dynamic secrets when sharing the epoch to avoid generating identical passwords.
```php
withPeriod(5) // The period (5 seconds)
->withDigest('sha1') // The digest algorithm
->withDigits(6); // The output will generate 6 digits
$password = $otp->at(1519401289); // Current period is: 1519401285 - 1519401289
$otp->verify($password, 1519401289); // Second 1: true
$otp->verify($password, 1519401290); // Second 2: false
// With epoch
$otp = TOTP::generate();
$otp = $otp->withPeriod(5) // The period (30 seconds)
->withDigest('sha1') // The digest algorithm
->withDigits(6) // The output will generate 8 digits
->withEpoch(1519401289); // The epoch is now 02/23/2018 @ 3:54:49pm (UTC)
$password = $otp->at(1519401289); // Current period is: 1519401289 - 1519401293
$otp->verify($password, 1519401289); // Second 1: true
$otp->verify($password, 1519401290); // Second 2: true
$otp->verify($password, 1519401291); // Second 3: true
$otp->verify($password, 1519401292); // Second 4: true
$otp->verify($password, 1519401293); // Second 5: true
$otp->verify($password, 1519401294); // Second 6: false
```
--------------------------------
### Digest Algorithm Enforcement Exceptions
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Examples of operations that throw exceptions when using non-compliant digest algorithms like md5.
```php
// Direct configuration
TOTP::generate(new InternalClock())->withDigest('md5'); // InvalidParameterException
// Provisioning URI
Factory::loadFromProvisioningUri(
'otpauth://totp/Foo:alice@foo.bar?algorithm=md5&secret=JDDK4U6G3BJLEZ7Y',
new InternalClock()
); // InvalidProvisioningUriException
```
--------------------------------
### Deprecation warning message
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Example of the deprecation warning message that indicates a missing PSR Clock implementation.
```text
The parameter "$clock" will become mandatory in 12.0.0.
Please set a valid PSR Clock implementation instead of "null".
```
--------------------------------
### Verify HOTP with window
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Window.md
Demonstrates how the window parameter extends the range of counters tested during HOTP verification.
```php
verify('123456', 999); // Will return false
$hotp->verify('123456', 999, 10); // Will return true (1000 is tested)
```
--------------------------------
### Create and Manage OTP Objects
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/index.md
Demonstrates generating secrets, creating OTP instances with a PSR-20 clock, and retrieving current passwords.
```php
getSecret()}\n";
// You can also specify a custom secret size (in bytes)
// The default is 64 bytes (which encodes to 103 base32 characters)
$otp = TOTP::generate($clock, 16); // 16 bytes = 26 base32 characters
echo "The OTP secret is: {$otp->getSecret()}\n";
// Note: use your own way to load the user secret.
// The function "load_user_secret" is simply a placeholder.
$secret = load_user_secret();
$otp = TOTP::createFromSecret($secret, $clock);
echo "The current OTP is: {$otp->now()}\n";
```
--------------------------------
### Generate Provisioning URI in PHP
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md
Create a TOTP instance and generate a provisioning URI string.
```php
withLabel('alice@google.com'); // The label (string)
$totp->getProvisioningUri(); // Will return otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP
```
--------------------------------
### Object Instantiation Migration
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v8-v9.md
Replaces direct constructor calls with the static create() method.
```php
getProvisioningUri(),
);
$otp->verify($_POST['otp']);
```
--------------------------------
### Preparing for v12.0
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Tools for identifying deprecated method usage and verifying immutability in tests.
```bash
grep -r "->set[A-Z]" --include="*.php" | grep -v vendor
```
```php
$original = TOTP::generate(new InternalClock());
$modified = $original->withLabel('test');
// These should be different instances
assert($original !== $modified);
assert($original->getLabel() === null);
assert($modified->getLabel() === 'test');
```
--------------------------------
### Verify OTP using a provisioning URI
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/QA.md
Load an OTP object from a stored provisioning URI to ensure all configuration parameters are preserved.
```php
getProvisioningUri()
);
$otp->verify($_POST['otp']);
```
--------------------------------
### Factory Provisioning URI Migration
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Comparison of Factory provisioning URI loading between v11.4 and v12.0.
```php
use OTPHP\Factory;
$otp = Factory::loadFromProvisioningUri($uri);
```
```php
use OTPHP\Factory;
use OTPHP\InternalClock;
$otp = Factory::loadFromProvisioningUri($uri, new InternalClock());
```
--------------------------------
### Configure OTP with Immutable Methods
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Use with* methods to return a new OTP object with updated parameters, leaving the original instance unchanged.
```php
withPeriod(60)
->withDigest('sha256')
->withDigits(8)
->withLabel('alice@foo.bar')
->withIssuer('My Service');
// $baseOtp remains unchanged with default settings
// $customOtp is a new object with the custom settings
```
--------------------------------
### Load OTP object from provisioning URI
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Factory.md
Instantiate a TOTP or HOTP object using the Factory class. A PSR-20 clock implementation is recommended for TOTP objects.
```php
verify('654321'); // returns false
// With a window 5 seconds, this will fail
// because the input is tested with 147682209-5 and 147682209+5 seconds.
// and this does not allow the previous OTP to be used
$totp->verify('654321', null, 5); // returns false
// With a window 10 seconds, this will succeed
// because the input is tested with 147682209-10 and 147682209+10 seconds.
// and the previous OTP is tested
$totp->verify('654321', null, 10); // returns true
```
--------------------------------
### Migrate to specific exception handling in PHP
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md
Demonstrates the transition from catching generic InvalidArgumentException to specific OTPHP exception classes.
```php
// Before (still works)
try {
$totp = TOTP::createFromSecret($secret);
} catch (\InvalidArgumentException $e) {
// ...
}
// After (more specific)
try {
$totp = TOTP::createFromSecret($secret);
} catch (\OTPHP\Exception\InvalidParameterException $e) {
// Handle parameter errors with access to parameter name/value
} catch (\OTPHP\Exception\SecretDecodingException $e) {
// Handle Base32 decoding errors
}
```
--------------------------------
### Verify TOTP using individual parameters
Source: https://github.com/spomky-labs/otphp/wiki/What-should-I-store-in-my-server-database
Initializes a TOTP object using specific configuration parameters retrieved from the user model.
```php
getEmail(),
$user->getOtpSecret(),
$period,
$digest,
$digits
);
$totp->verify($_POST['otp']);
```
--------------------------------
### Testing with Mock Clock
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Using a fixed or mock clock for testing purposes.
```php
use Symfony\Component\Clock\MockClock;
use OTPHP\TOTP;
$clock = new MockClock('2024-01-01 12:00:00');
$totp = TOTP::generate($clock);
```
--------------------------------
### Run PHPUnit tests
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Execute the test suite to verify that no deprecation warnings are triggered.
```bash
vendor/bin/phpunit
```
--------------------------------
### Configure OTP with Mutable Methods
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Use set* methods to modify an OTP object in place, which is suitable for step-by-step configuration.
```php
setPeriod(60); // Modifies the object
$otp->setDigest('sha256'); // Modifies the object
$otp->setDigits(8); // Modifies the object
$otp->setLabel('alice@foo.bar'); // Modifies the object
$otp->setIssuer('My Service'); // Modifies the object
// $otp is now configured with all the above settings
```
--------------------------------
### Handling InvalidProvisioningUriException
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md
Caught when a provided provisioning URI string is malformed or missing required components.
```php
try {
$otp = Factory::loadFromProvisioningUri('invalid-uri');
} catch (\OTPHP\Exception\InvalidProvisioningUriException $e) {
echo $e->getMessage(); // "Not a valid OTP provisioning URI"
}
```
--------------------------------
### Generate QR Code URI with Online Service
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md
Use an external QR code service by passing the service URI and a custom placeholder to the getQrCodeUri method.
```php
withLabel('alice@google.com'); // The label (string)
$qr_code_url = $totp->getQrCodeUri(
'https://api.qrserver.com/v1/create-qr-code/?color=5330FF&bgcolor=70FF7E&data=[DATA]&qzone=2&margin=0&size=300x300&ecc=M',
'[DATA]'
);
echo "
";
```
--------------------------------
### Generate QR Code URI
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/index.md
Configures the OTP label and generates a URI for QR code rendering.
```php
withLabel('Label of your web');
$grCodeUri = $otp->getQrCodeUri(
'https://api.qrserver.com/v1/create-qr-code/?data=[DATA]&size=300x300&ecc=M',
'[DATA]'
);
echo "
";
```
--------------------------------
### Demonstrate TOTP lifetime behavior
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/QA.md
Observe how TOTP values change over time based on the defined period.
```php
now();
sleep(1);
}
```
--------------------------------
### Add custom parameters to TOTP
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Use the withParameter method to include arbitrary key-value pairs in the provisioning URI.
```php
withLabel('alice@google.com') // The label
->withParameter('foo', 'bar');
$totp->getProvisioningUri(); // Will return otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&foo=bar
```
--------------------------------
### Common Migration Patterns
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Standard refactoring patterns for updating property values, conditional logic, and object initialization.
```php
// Before (v11.3)
$otp->setSecret('NEWSECRET');
$otp->setDigits(8);
// After (v11.4+)
$otp = $otp->withSecret('NEWSECRET')
->withDigits(8);
```
```php
// Before (v11.3)
if ($useCustomLabel) {
$otp->setLabel($customLabel);
}
// After (v11.4+)
if ($useCustomLabel) {
$otp = $otp->withLabel($customLabel);
}
```
```php
// Before (v11.3)
$hotp->setCounter($hotp->getCounter() + 1);
// After (v11.4+)
$hotp = $hotp->withCounter($hotp->getCounter() + 1);
```
```php
// Before (v11.3) - Required multiple statements
$totp = TOTP::createFromSecret('secret', new InternalClock());
$totp->setLabel('user@example.com');
$totp->setIssuer('MyApp');
$totp->setParameter('image', 'https://example.com/logo.png');
// After (v11.4+) - Clean method chaining
$totp = TOTP::createFromSecret('secret', new InternalClock())
->withLabel('user@example.com')
->withIssuer('MyApp')
->withParameter('image', 'https://example.com/logo.png');
```
--------------------------------
### Verify TOTP with Google Authenticator
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md
Generate a TOTP instance and display the current token for verification against the Google Authenticator app.
```php
withLabel('alice@google.com'); // The label (string)
echo 'Current OTP: ' . $totp->now();
```
--------------------------------
### Configure a Custom Secret
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Use a manually generated secret string instead of the default 512-bit secret.
```php
getOtpSecret());
$totp = $totp->withPeriod($period)
->withDigest($digest)
->withDigits($digits)
->withLabel($user->getEmail());
$totp->verify($_POST['otp']);
```
--------------------------------
### OTP Label Assignment Migration
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v8-v9.md
Updates label handling by removing it from the constructor and using the setLabel() method instead.
```php
setLabel($label);
```
--------------------------------
### Catching library-specific exceptions
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md
Use the OTPExceptionInterface to catch any exception thrown by the OTPHP library.
```php
try {
$totp = TOTP::createFromSecret('invalid secret!@#');
} catch (\OTPHP\Exception\OTPExceptionInterface $e) {
// Catches any OTPHP exception
}
```
--------------------------------
### Configure TOTP with custom parameters in PHP
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/AppConfig.md
Creates a TOTP instance with a custom secret, period, digest algorithm, and digit count. Note that these specific settings may not be compatible with all authenticator apps.
```php
withPeriod(10) // The period (int)
->withDigest('sha512') // The digest algorithm (string)
->withDigits(8) // The number of digits (int)
->withLabel('alice@google.com'); // The label (string)
echo 'Current OTP: ' . $totp->now();
```
--------------------------------
### Configure issuer for TOTP
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Set an issuer to distinguish between multiple OTPs with the same label. The issuer is automatically added as a label prefix and a query parameter.
```php
withLabel('alice@google.com') // The label (string)
->withIssuer('My Service');
```
```php
getProvisioningUri(); // Will return otpauth://totp/My%20Service%3Aalice%40google.com?issuer=My%20Service&secret=JBSWY3DPEHPK3PXP
```
--------------------------------
### Search for TOTP usage
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Use grep to identify instances of TOTP usage that require a clock parameter update.
```bash
# Search for TOTP usage without clock parameter
grep -r "TOTP::generate()" --include="*.php"
grep -r "TOTP::create(" --include="*.php"
grep -r "new TOTP(" --include="*.php"
grep -r "Factory::loadFromProvisioningUri(" --include="*.php"
```
--------------------------------
### Handling ParameterNotFoundException
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md
Caught when attempting to retrieve a parameter that does not exist on the OTP object.
```php
try {
$totp = TOTP::createFromSecret('SECRET');
$value = $totp->getParameter('nonexistent');
} catch (\OTPHP\Exception\ParameterNotFoundException $e) {
echo $e->getMessage(); // "Parameter \"nonexistent\" does not exist"
echo $e->parameterName; // "nonexistent"
}
```
--------------------------------
### Internal Clock Usage
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Using the built-in InternalClock for simple use cases.
```php
use OTPHP\InternalClock;
use OTPHP\TOTP;
$totp = TOTP::generate(new InternalClock());
```
--------------------------------
### Access Exception Properties
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md
Retrieve detailed error information using public readonly properties provided by specific OTPHP exceptions.
```php
try {
$totp = TOTP::create('SECRET');
$totp = $totp->withDigits($_POST['digits']);
} catch (\OTPHP\Exception\InvalidParameterException $e) {
echo "Error: {$e->getMessage()}\n";
echo "Parameter: {$e->parameterName}\n";
echo "Invalid value: " . json_encode($e->parameterValue) . "\n";
// Output:
// Error: Digits must be at least 1.
// Parameter: digits
// Invalid value: 0
}
```
--------------------------------
### Catch All OTPHP Exceptions via Marker Interface
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md
Use the OTPExceptionInterface to catch any library-specific error, separating them from general application exceptions.
```php
try {
$totp = TOTP::createFromSecret($_POST['secret']);
$totp = $totp->withLabel($_POST['label'])
->withIssuer($_POST['issuer']);
} catch (\OTPHP\Exception\OTPExceptionInterface $e) {
// Handle any OTPHP-specific error
return response()->json(['error' => $e->getMessage()], 400);
} catch (\Exception $e) {
// Handle unexpected errors
log()->error('Unexpected error: ' . $e->getMessage());
return response()->json(['error' => 'Internal server error'], 500);
}
```
--------------------------------
### Inject Custom Secret
Source: https://github.com/spomky-labs/otphp/wiki/OTP-Secret
Manually generate a secret, encode it using Base32, and provide it during TOTP instantiation.
```php
getSecret(); // Return the value of $encoded_secret
```
--------------------------------
### TOTP Generation Migration
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Comparison of TOTP generation between v11.4 and v12.0.
```php
use OTPHP\TOTP;
// This triggers a deprecation warning in v11.4
$totp = TOTP::generate();
```
```php
use OTPHP\TOTP;
use OTPHP\InternalClock;
// You must provide a Clock implementation
$totp = TOTP::generate(new InternalClock());
```
--------------------------------
### Generate Secret with Built-in Method
Source: https://github.com/spomky-labs/otphp/wiki/OTP-Secret
Uses the library's internal generator to create a 256-bit Base32 encoded secret.
```php
getSecret(); // Return the Base32 encoded secret generated by this library
```
--------------------------------
### Handle Specific OTPHP Exceptions
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md
Catch targeted exceptions to handle specific error scenarios like URI parsing or parameter validation.
```php
try {
$otp = Factory::loadFromProvisioningUri($uri);
$isValid = $otp->verify($code);
} catch (\OTPHP\Exception\InvalidProvisioningUriException $e) {
// Handle URI parsing errors
log('Invalid provisioning URI: ' . $e->getMessage());
} catch (\OTPHP\Exception\InvalidParameterException $e) {
// Handle parameter validation errors
log("Invalid {$e->parameterName}: {$e->getMessage()}");
}
```
--------------------------------
### Handling SecretDecodingException
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md
Caught when the provided secret string fails Base32 decoding.
```php
try {
$totp = TOTP::createFromSecret('INVALID!@#$%');
$totp->now(); // Triggers secret decoding
} catch (\OTPHP\Exception\SecretDecodingException $e) {
echo $e->getMessage(); // "Unable to decode the secret. Is it correctly base32 encoded?"
}
```
--------------------------------
### Migrate Digest Algorithm
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/UPGRADE_v11-v12.md
Update code to use a spec-compliant digest algorithm like sha256 instead of insecure alternatives.
```php
// Before
$totp = $totp->withDigest('md5');
// After
$totp = $totp->withDigest('sha256');
```
--------------------------------
### Add image parameter to TOTP
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Include an image URL via the 'image' parameter for applications that support custom branding.
```php
withLabel('alice@google.com') // The label (string)
->withParameter('image', 'https://foo.bar/otp.png');
$totp->getProvisioningUri(); // Will return otpauth://totp/alice%40google.com?secret=JBSWY3DPEHPK3PXP&image=https%3A%2F%2Ffoo.bar%2Fotp.png
```
--------------------------------
### Configure Digest Algorithm
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Set the HMAC digest algorithm. Only algorithms producing at least 19 bytes are supported; sha256 and sha512 are recommended.
```php
withPeriod(30) // The period (30 seconds)
->withDigest('sha256'); // The digest algorithm
```
--------------------------------
### Adjust Period and Counter
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Modify the default TOTP period or HOTP counter values.
```php
withPeriod(10); // The period is now 10 seconds
$otp = HOTP::generate();
$otp = $otp->withCounter(1000); // The counter is now 1000. We recommend you start at `0`, but you can set any value (at least 0)
```
--------------------------------
### Generate OTP with Custom Secret Size
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Specify the secret size in bytes during OTP generation. The library automatically removes trailing '=' characters.
```php
getSecret(); // Will be 26 characters long
```
--------------------------------
### Verify OTP Input
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/index.md
Validates a user-provided OTP against the stored secret.
```php
$otp = TOTP::createFromSecret($secret); // create TOTP object from the secret.
$otp->verify($input); // Returns true if the input is verified, otherwise false.
```
--------------------------------
### Configure TOTP Digits
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Adjust the number of digits generated by the TOTP instance. Values greater than 10 are generally discouraged for usability.
```php
withPeriod(30) // The period (30 seconds)
->withDigest('sha1') // The digest algorithm
->withDigits(8); // The output will generate 8 digits
```
--------------------------------
### Disable issuer as query parameter
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Customize.md
Use withIssuerIncludedAsParameter(false) to remove the issuer from the query string while keeping it in the label prefix.
```php
withIssuerIncludedAsParameter(false);
echo $totp->getProvisioningUri(); // Will return otpauth://totp/My%20Service%3Aalice%40google.com?secret=JBSWY3DPEHPK3PXP
```
--------------------------------
### Handling InvalidLabelException
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md
Caught when a label or issuer format violates the Google Authenticator specification.
```php
try {
$totp = TOTP::createFromSecret('SECRET');
$totp = $totp->withLabel('user:invalid:format'); // Invalid: multiple colons
} catch (\OTPHP\Exception\InvalidLabelException $e) {
echo $e->getMessage(); // "Neither issuer nor account name in label may contain a colon."
echo $e->labelName; // "label"
echo $e->labelValue; // "user:invalid:format"
}
```
--------------------------------
### Maintain Backward Compatibility with Standard Exceptions
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md
Custom exceptions extend standard PHP exceptions, allowing existing try-catch blocks to function as expected.
```php
// This still works - catches InvalidParameterException and InvalidLabelException
try {
$totp = TOTP::create('SECRET');
$totp = $totp->withDigits(-1);
} catch (\InvalidArgumentException $e) {
// Catches all exceptions extending InvalidArgumentException
}
// This also works - catches SecretDecodingException
try {
$totp = TOTP::createFromSecret('INVALID!@#');
$totp->now();
} catch (\RuntimeException $e) {
// Catches all exceptions extending RuntimeException
}
```
--------------------------------
### Handling InvalidParameterException
Source: https://github.com/spomky-labs/otphp/blob/11.6.x/doc/Exceptions.md
Caught when an OTP parameter is provided with an invalid value, exposing the parameter name and value.
```php
try {
$totp = TOTP::create('SECRET');
$totp = $totp->withDigits(0); // Invalid: must be at least 1
} catch (\OTPHP\Exception\InvalidParameterException $e) {
echo $e->getMessage(); // "Digits must be at least 1."
echo $e->parameterName; // "digits"
echo $e->parameterValue; // 0
}
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.