# php-extensions Full Instructions

## Core Principle

**NEVER call PHP extension functions without checking if the extension is available first.**

Extensions that work locally but fail in production:

- `mbstring` - Multibyte string functions (`mb_*`)
- `iconv` - Character encoding conversion (`iconv_*`)
- `gd` - Image processing (`image*`, `gd_*`)
- `curl` - HTTP client (`curl_*`)
- `zip` - ZIP archive handling (`zip_*`)
- `xml` - XML parsing (`xml_*`, `simplexml_*`)

## Required Extension Checks

### Before Using Extension Functions

**ALWAYS check availability:**

```php
// ✅ CORRECT: Check function exists before use
if (function_exists('mb_check_encoding') && function_exists('mb_convert_encoding')) {
    if (!mb_check_encoding($text, 'UTF-8')) {
        $text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');
    }
} else {
    // Fallback: Use iconv if available, otherwise skip encoding check
    if (function_exists('iconv')) {
        $text = iconv('UTF-8', 'UTF-8//IGNORE', $text);
    }
    // If neither available, continue with string as-is
    // Additional cleaning can be done with preg_replace
}
```

**❌ WRONG: Direct function call without check**

```php
// This will cause fatal error if mbstring not available
$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');
```

### Extension Availability Checks

**Use `function_exists()` for functions:**

```php
if (function_exists('mb_strlen')) {
    $length = mb_strlen($text, 'UTF-8');
} else {
    // Fallback
    $length = strlen($text);
}
```

**Use `extension_loaded()` for extensions:**

```php
if (extension_loaded('mbstring')) {
    // Safe to use mbstring functions
} else {
    // Use fallback
}
```

**Use helper function `_has_mbstring()` (if available):**

```php
// Check if helper function exists (from blog-template-helpers.php)
if (function_exists('_has_mbstring') && _has_mbstring()) {
    $length = mb_strlen($text, 'UTF-8');
} else {
    $length = strlen($text);
}
```

## Extension Categories

### Required Extensions

These extensions are **required** for core functionality:

- **`json`** - JSON encoding/decoding (PHP 8.0+ includes by default)
- **`mbstring`** - Multibyte string handling (required for UTF-8 text processing)
- **`iconv`** - Character encoding conversion (fallback for mbstring)
- **`curl`** - HTTP requests (required for API integrations)

**Pattern for required extensions:**

```php
// Check required extension
if (!extension_loaded('json')) {
    throw new RuntimeException('JSON extension is required but not available');
}

// Or with fallback if acceptable
if (!extension_loaded('mbstring')) {
    error_log('WARNING: mbstring extension not available, using fallback');
    // Use fallback functions
}
```

### Optional Extensions

These extensions enhance functionality but have fallbacks:

- **`gd`** - Image processing (fallback: skip image operations)
- **`zip`** - ZIP archive handling (fallback: skip ZIP operations)
- **`xml`** - XML parsing (fallback: use alternative parser)

**Pattern for optional extensions:**

```php
if (extension_loaded('gd') && function_exists('imagecreatefromjpeg')) {
    // Use GD for image processing
    $image = imagecreatefromjpeg($file);
} else {
    // Fallback: Skip image processing or use alternative
    error_log('GD extension not available, skipping image processing');
    return false;
}
```

## Fallback Patterns

### mbstring Fallback Pattern

**Standard pattern (from `blog-template-helpers.php`):**

```php
// Check if mbstring available
if (function_exists('_has_mbstring') && _has_mbstring()) {
    $length = mb_strlen($text, 'UTF-8');
    $substring = mb_substr($text, 0, 100, 'UTF-8');
} else {
    // Use fallback functions
    $length = strlen($text);
    $substring = substr($text, 0, 100);
}
```

**Encoding check pattern:**

```php
// Step 6: Ensure UTF-8 encoding
if (function_exists('mb_check_encoding') && function_exists('mb_convert_encoding')) {
    if (!mb_check_encoding($text, 'UTF-8')) {
        $text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');
    }
} else {
    // Fallback: Use iconv if available
    if (function_exists('iconv')) {
        $text = iconv('UTF-8', 'UTF-8//IGNORE', $text);
    }
    // If neither available, continue as-is
    // preg_replace can clean invalid characters
}
```

### iconv Fallback Pattern

```php
if (function_exists('iconv')) {
    $converted = iconv('UTF-8', 'ISO-8859-1//IGNORE', $text);
} else {
    // Fallback: Use mb_convert_encoding if available
    if (function_exists('mb_convert_encoding')) {
        $converted = mb_convert_encoding($text, 'ISO-8859-1', 'UTF-8');
    } else {
        // Last resort: Use utf8_decode (limited support)
        $converted = utf8_decode($text);
    }
}
```

### curl Fallback Pattern

```php
if (function_exists('curl_init')) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    curl_close($ch);
} else {
    // Fallback: Use file_get_contents (less features)
    if (ini_get('allow_url_fopen')) {
        $response = file_get_contents($url);
    } else {
        throw new RuntimeException('cURL and allow_url_fopen both unavailable');
    }
}
```

### gd Fallback Pattern

```php
if (extension_loaded('gd') && function_exists('imagecreatefromjpeg')) {
    $image = imagecreatefromjpeg($file);
    // Process image
    imagejpeg($image, $output);
} else {
    // Fallback: Skip image processing
    error_log('GD extension not available, skipping image optimization');
    copy($file, $output); // Just copy original
}
```

## Common Pitfalls

### ❌ Pitfall 1: Assuming Extension Available

```php
// WRONG: Assumes mbstring is always available
$text = mb_strtolower($text, 'UTF-8');
```

**Fix:**

```php
// CORRECT: Check first
if (function_exists('mb_strtolower')) {
    $text = mb_strtolower($text, 'UTF-8');
} else {
    $text = strtolower($text);
}
```

### ❌ Pitfall 2: Not Checking Both Function and Extension

```php
// WRONG: Only checks function, not extension
if (function_exists('mb_strlen')) {
    // But mbstring might not be fully loaded
}
```

**Fix:**

```php
// CORRECT: Check extension or use helper
if (extension_loaded('mbstring') && function_exists('mb_strlen')) {
    // Safe to use
}
```

### ❌ Pitfall 3: No Fallback Logic

```php
// WRONG: Fails silently if extension missing
if (extension_loaded('gd')) {
    processImage($file);
}
// No fallback - function silently fails
```

**Fix:**

```php
// CORRECT: Always provide fallback
if (extension_loaded('gd') && function_exists('imagecreatefromjpeg')) {
    processImage($file);
} else {
    error_log('GD not available, using fallback');
    processImageFallback($file);
}
```

### ❌ Pitfall 4: Checking Wrong Function

```php
// WRONG: Checks wrong function name
if (function_exists('mbstring_strlen')) {
    // Function doesn't exist
}
```

**Fix:**

```php
// CORRECT: Check actual function name
if (function_exists('mb_strlen')) {
    // Correct function name
}
```

## Code Review Checklist

Before committing code that uses PHP extensions:

- [ ] All extension function calls have `function_exists()` or `extension_loaded()` checks
- [ ] Fallback logic is implemented for optional extensions
- [ ] Error handling for missing required extensions
- [ ] Logging when fallbacks are used
- [ ] Tested with extension disabled locally
- [ ] Documentation updated if new extension dependency added

## Helper Functions

**Use existing helper functions when available:**

- `_has_mbstring()` - Check mbstring availability (from `blog-template-helpers.php`)
- `_mb_strlen_fallback()` - Fallback for `mb_strlen()`
- `_mb_substr_fallback()` - Fallback for `mb_substr()`
- `_mb_strtolower_fallback()` - Fallback for `mb_strtolower()`

**Pattern:**

```php
// Use helper if available, otherwise check directly
if (function_exists('_has_mbstring') && _has_mbstring()) {
    $length = mb_strlen($text, 'UTF-8');
} elseif (function_exists('_mb_strlen_fallback')) {
    $length = _mb_strlen_fallback($text, 'UTF-8');
} else {
    $length = strlen($text);
}
```

## Pre-Deployment Validation

**Before deploying:**

1. Run extension validator: `php v2/scripts/dev-helpers/check-php-extensions.php`
2. Run pre-deployment check: `php v2/scripts/dev-helpers/pre-deployment-check.php`
3. Verify Composer requirements: `composer install --no-dev`
4. Test with extensions disabled (if possible)

## Related Files

- `v2/config/blog-template-helpers.php` - mbstring fallback functions
- `v2/helpers/php-extensions.php` - Extension helper functions
- `v2/scripts/dev-helpers/check-php-extensions.php` - Extension validator
- `docs/development/PHP_EXTENSION_DEPENDENCIES.md` - Comprehensive guide

## Examples from Codebase

**Good example (blog-schema-generator.php):**

```php
// Step 6: Ensure UTF-8 encoding
if (function_exists('mb_check_encoding') && function_exists('mb_convert_encoding')) {
    if (!mb_check_encoding($answer_text, 'UTF-8')) {
        $answer_text = mb_convert_encoding($answer_text, 'UTF-8', 'UTF-8');
    }
} else {
    // Fallback: Use iconv if available
    if (function_exists('iconv')) {
        $answer_text = iconv('UTF-8', 'UTF-8//IGNORE', $answer_text);
    }
    // If neither available, continue as-is
}
```

**Good example (blog-template-helpers.php):**

```php
$title_length = _has_mbstring() ? mb_strlen($title, 'UTF-8') : _mb_strlen_fallback($title, 'UTF-8');
```

## Testing

**Test fallback logic:**

1. Disable extension in php.ini (if possible)
2. Run test script: `php v2/scripts/dev-helpers/test-extension-fallbacks.php`
3. Verify graceful degradation
4. Check error logs for fallback usage

## Production Monitoring

**Monitor extension availability:**

- Check diagnostic endpoint: `/v2/api/php-extensions-diagnostics.php`
- Review error logs for fallback usage
- Set up alerts for missing critical extensions

## Summary

1. **Always check** before using extension functions
2. **Always provide fallbacks** for optional extensions
3. **Always log** when fallbacks are used
4. **Always test** with extensions disabled
5. **Always validate** before deployment

**Remember**: What works locally may not work in production. Always check extension availability.
