# Native PHP 8.5 URL System

**Zero-dependency URL parsing and manipulation** using native PHP 8.5 `Uri\Rfc3986\Uri` and `Uri\WhatWg\Url`.

## Features

✅ **Native PHP 8.5 API** - No external dependencies
✅ **Dual Spec Support** - RFC 3986 and WHATWG URL Standard
✅ **Smart Factory** - Automatic spec selection based on use case
✅ **Immutable Withers** - Framework-compliant readonly pattern
✅ **Type Safe** - Full type safety with enums and value objects
✅ **IDNA/Punycode** - Native international domain name support

## Installation

**Requirement**: PHP 8.5+

No composer packages needed - uses native PHP URL API!

## Quick Start

### Basic Usage

```php
use App\Framework\Http\Url\UrlFactory;

// Automatic spec selection
$url = UrlFactory::parse('https://example.com/path');

// API Client (RFC 3986)
$apiUrl = UrlFactory::forApiClient('https://api.example.com/users');

// Browser Redirect (WHATWG)
$redirect = UrlFactory::forBrowserRedirect('https://app.example.com/dashboard');
```

### URL Manipulation (Immutable)

```php
$url = UrlFactory::forApiClient('https://api.example.com/users');

// Immutable withers return new instances
$withAuth = $url->withUserInfo('api_key', 'secret');
$withQuery = $url->withQuery('filter=active&sort=name');
$withPath = $url->withPath('/v2/users');

// Component access
echo $url->getScheme();   // 'https'
echo $url->getHost();     // 'api.example.com'
echo $url->getPath();     // '/users'
```

### Use Case Factory Methods

```php
// API & Server-Side (RFC 3986)
UrlFactory::forApiClient($url);        // REST, GraphQL, SOAP
UrlFactory::forCurlRequest($url);      // cURL operations
UrlFactory::forSignature($url);        // OAuth, AWS signing
UrlFactory::forCanonical($url);        // SEO, duplicate detection

// Browser & Client-Side (WHATWG)
UrlFactory::forBrowserRedirect($url);  // HTTP redirects
UrlFactory::forDeepLink($url);         // Universal links
UrlFactory::forFormAction($url);       // HTML form actions
UrlFactory::forClientSide($url);       // JavaScript fetch()
```

## Architecture

### URL Specifications

**RFC 3986** - Server-side URL handling:
- Strict parsing rules
- No automatic encoding
- Preserves exact structure
- Best for: API clients, signatures, cURL

**WHATWG** - Browser-compatible URL handling:
- Living standard (matches browsers)
- Automatic percent-encoding
- URL normalization
- Best for: Redirects, deep links, forms

### Components

```
src/Framework/Http/Url/
├── Url.php                # Unified URL interface
├── UrlSpec.php           # RFC3986 vs WHATWG enum
├── UrlUseCase.php        # Use case categories
├── Rfc3986Url.php        # RFC 3986 implementation
├── WhatwgUrl.php         # WHATWG implementation
└── UrlFactory.php        # Smart factory
```

## Examples

### API Client with Authentication

```php
$apiUrl = UrlFactory::forApiClient('https://api.example.com/resource');
$withAuth = $apiUrl->withUserInfo('api_key', 'secret_token');

echo $withAuth->toString();
// https://api_key:secret_token@api.example.com/resource
```

### URL Signature Generation

```php
$url = UrlFactory::forSignature('https://api.example.com/resource');
$withParams = $url->withQuery('timestamp=1234567890&user_id=42');

// Generate signature from canonical URL string
$canonical = $withParams->toString();
$signature = hash_hmac('sha256', $canonical, $secretKey);

$signed = $withParams->withQuery(
    $withParams->getQuery() . "&signature={$signature}"
);
```

### Browser Redirect with Parameters

```php
$redirect = UrlFactory::forBrowserRedirect('https://app.example.com/login');
$withReturn = $redirect->withQuery('return_url=/dashboard&status=success');

// Browser-compatible encoding
header('Location: ' . $withReturn->toString());
```

### Deep Link with Fallback

```php
$deepLink = UrlFactory::forDeepLink('myapp://open/article/123');
$fallback = UrlFactory::forBrowserRedirect('https://example.com/article/123');

// Try deep link first, fallback to web
$targetUrl = $isNativeApp ? $deepLink : $fallback;
```

### IDNA/Punycode Support

```php
$unicode = UrlFactory::parse('https://例え.jp/path');
$ascii = $unicode->toAsciiString();

echo $ascii; // https://xn--r8jz45g.jp/path
```

### URL Comparison

```php
$url1 = UrlFactory::parse('https://example.com/path#frag1');
$url2 = UrlFactory::parse('https://example.com/path#frag2');

// Ignore fragment by default
$url1->equals($url2);                    // true

// Include fragment in comparison
$url1->equals($url2, includeFragment: true);  // false
```

### Relative URL Resolution

```php
$base = UrlFactory::parse('https://example.com/base/path');
$resolved = $base->resolve('../other/resource');

echo $resolved->toString();
// https://example.com/other/resource
```

### Spec Conversion

```php
$rfc = UrlFactory::forApiClient('https://example.com/path');
$whatwg = UrlFactory::convert($rfc, UrlSpec::WHATWG);

// Now browser-compatible with normalization
```

## Use Case Guide

### When to use RFC 3986

✅ REST API requests
✅ URL signature generation (OAuth, AWS)
✅ cURL operations
✅ Canonical URL generation (SEO)
✅ Webhook URLs
✅ FTP/SFTP URLs

### When to use WHATWG

✅ HTTP redirects
✅ Deep links / universal links
✅ HTML form actions
✅ JavaScript fetch() compatibility
✅ Browser-side URL generation
✅ Mobile app URLs

## Testing

Comprehensive Pest tests included:

```bash
./vendor/bin/pest tests/Unit/Framework/Http/Url/
```

Test coverage:
- RFC 3986 parsing and manipulation
- WHATWG parsing and normalization
- Factory method selection
- URL comparison and resolution
- IDNA/Punycode handling
- Immutability guarantees
- Edge cases and error handling

## Framework Integration

### Readonly Pattern

All URL classes are `final readonly` with immutable withers:

```php
final readonly class Rfc3986Url implements Url
{
    private function __construct(
        private NativeRfc3986Uri $uri
    ) {}

    // Withers return new instances
    public function withPath(string $path): self
    {
        return new self($this->uri->withPath($path));
    }
}
```

### Value Object Pattern

URLs are value objects with value semantics:

```php
$url1 = UrlFactory::parse('https://example.com/path');
$url2 = UrlFactory::parse('https://example.com/path');

$url1->equals($url2); // true
```

### DI Container Integration

Register in container initializer:

```php
final readonly class UrlServiceInitializer implements Initializer
{
    #[Initializer]
    public function __invoke(): UrlService
    {
        return new UrlService(UrlFactory::class);
    }
}
```

## Performance

Native PHP 8.5 implementation = **C-level performance**:

- ✅ Zero external dependencies
- ✅ No reflection overhead
- ✅ Optimized memory usage
- ✅ Fast parsing and manipulation
- ✅ Native IDNA conversion

## Migration from Legacy Code

### Before (primitive strings)

```php
function generateApiUrl(string $baseUrl, array $params): string
{
    $query = http_build_query($params);
    return $baseUrl . '?' . $query;
}
```

### After (type-safe URLs)

```php
function generateApiUrl(Url $baseUrl, array $params): Url
{
    $query = http_build_query($params);
    return $baseUrl->withQuery($query);
}

// Usage
$apiUrl = UrlFactory::forApiClient('https://api.example.com/resource');
$withParams = generateApiUrl($apiUrl, ['filter' => 'active']);
```

## Best Practices

1. **Use factory methods** for automatic spec selection
2. **Prefer specific use case methods** over generic parse()
3. **Type hint with Url** for flexibility
4. **Use equals() for comparison** instead of string comparison
5. **Leverage immutability** - withers are safe for concurrent use
6. **Choose correct spec** - RFC 3986 for server, WHATWG for browser

## Advanced Features

### Native URL Access

Access underlying native PHP URL objects when needed:

```php
$url = UrlFactory::forApiClient('https://example.com');
$nativeUri = $url->getNativeUrl(); // \Uri\Rfc3986\Uri
```

### Custom URL Schemes

Both specs support custom schemes:

```php
$custom = UrlFactory::parse('myscheme://resource/path');
echo $custom->getScheme(); // 'myscheme'
```

## Troubleshooting

### Invalid URL Errors

```php
try {
    $url = UrlFactory::parse('invalid://url');
} catch (\InvalidArgumentException $e) {
    // Handle parse error
}
```

### Spec Mismatch

Convert between specs when needed:

```php
$rfc = UrlFactory::forApiClient('https://example.com');
$whatwg = UrlFactory::convert($rfc, UrlSpec::WHATWG);
```

### IDNA Issues

Native PHP 8.5 handles IDNA automatically:

```php
$url = UrlFactory::parse('https://例え.jp');
$ascii = $url->toAsciiString(); // Automatic Punycode
```

## Resources

- [RFC 3986 Specification](https://www.rfc-editor.org/rfc/rfc3986)
- [WHATWG URL Standard](https://url.spec.whatwg.org/)
- [PHP 8.5 URL API Documentation](https://www.php.net/manual/en/book.uri.php)
- [IDNA/Punycode Reference](https://www.rfc-editor.org/rfc/rfc5891)

## License

Part of Custom PHP Framework - Internal Use
