Skip to content

Conversation

@Spomky
Copy link
Member

@Spomky Spomky commented Jan 3, 2026

Summary

This PR introduces a comprehensive custom exception system to replace generic PHP exceptions with specific, meaningful exception types while maintaining full backward compatibility.

New Exception Types

  • OTPExceptionInterface: Marker interface for all OTPHP exceptions - allows catching all library exceptions with one catch block
  • InvalidParameterException: For parameter validation errors (secret, digits, algorithm, period, epoch, counter, timestamp, leeway, secretSize, etc.)
  • InvalidLabelException: For label/issuer format validation errors
  • InvalidProvisioningUriException: For URI parsing and validation errors
  • ParameterNotFoundException: For accessing non-existent parameters
  • SecretDecodingException: For Base32 decoding failures

Key Features

🎯 Better Error Handling

  • Catch specific exceptions instead of generic ones
  • Access parameter name and value via public readonly properties for debugging
  • OTPExceptionInterface allows catching all OTPHP exceptions

📖 Comprehensive Documentation

  • New doc/Exceptions.md with complete exception guide
  • Detailed examples for each exception type
  • Migration guide and best practices
  • Updated doc/index.md to reference exception handling

✅ Code Quality

  • Replaced all assert() calls in src/ with proper exception throws
  • All exceptions validated by PHPStan
  • 133 tests pass (100% coverage maintained)
  • Updated PHPStan baseline

🔄 Backward Compatibility

  • All custom exceptions extend PHP built-in exceptions
  • Existing catch (InvalidArgumentException $e) still works
  • Existing catch (RuntimeException $e) still works
  • Exception messages unchanged
  • No code changes required for existing users

Examples

Before (still works)

try {
    $totp = TOTP::createFromSecret($secret);
    $totp->setDigits(0);
} catch (\InvalidArgumentException $e) {
    echo $e->getMessage(); // "Digits must be at least 1."
}

After (enhanced debugging)

try {
    $totp = TOTP::createFromSecret($secret);
    $totp->setDigits(0);
} catch (\OTPHP\Exception\InvalidParameterException $e) {
    echo $e->getMessage();      // "Digits must be at least 1."
    echo $e->parameterName;     // "digits"
    echo $e->parameterValue;    // 0
}

Catch All OTPHP Exceptions

try {
    $otp = Factory::loadFromProvisioningUri($uri);
} catch (\OTPHP\Exception\OTPExceptionInterface $e) {
    // Catches all OTPHP-specific exceptions
    log("OTPHP Error: " . $e->getMessage());
}

Test Plan

  • All 133 PHPUnit tests pass
  • PHPStan analysis passes with baseline
  • Backward compatibility verified
  • Documentation complete and accurate
  • No assert() calls remaining in src/
  • All exception types have public readonly properties

Breaking Changes

None - this PR is 100% backward compatible.

🤖 Generated with Claude Code

This commit introduces a comprehensive custom exception system to
replace generic PHP exceptions (InvalidArgumentException, RuntimeException)
with specific, meaningful exception types while maintaining full
backward compatibility.

## New Exception Types

- **OTPExceptionInterface**: Marker interface for all OTPHP exceptions
- **InvalidParameterException**: Parameter validation errors (secret, digits, algorithm, period, epoch, counter, etc.)
- **InvalidLabelException**: Label/issuer format errors
- **InvalidProvisioningUriException**: URI parsing errors
- **ParameterNotFoundException**: Missing parameter errors
- **SecretDecodingException**: Base32 decoding failures

## Key Features

- All custom exceptions extend PHP built-in exceptions (BC maintained)
- Public readonly properties for parameter name/value debugging
- Replaced all assert() calls with proper exception throws
- Comprehensive documentation with examples and best practices

## Breaking Changes

None - fully backward compatible:
- Existing catch(InvalidArgumentException) still works
- Existing catch(RuntimeException) still works
- Exception messages unchanged
- No code changes required for existing users

## Documentation

- Added doc/Exceptions.md with complete exception guide
- Updated doc/index.md to reference exception handling
- Included migration guide and best practices

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@Spomky Spomky self-assigned this Jan 3, 2026
@Spomky Spomky added this to the 11.4.0 milestone Jan 3, 2026
@Spomky Spomky merged commit ec5ff75 into 11.4.x Jan 3, 2026
16 of 17 checks passed
@Spomky Spomky deleted the feature/custom-exceptions branch January 3, 2026 13:16
Spomky added a commit that referenced this pull request Jan 4, 2026
Merge up changes from 11.4.x including:
- Documentation for readonly classes migration (PR #244)
- Custom exception hierarchy (PR #251)
- Documentation improvements (PR #250)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@Spomky Spomky mentioned this pull request Jan 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants