# PHP Extension Dependencies Guide

**Last Updated:** 2026-01-15

## Overview

PHP extensions like `mbstring`, `iconv`, `gd`, `curl`, etc. may be available in local development but missing in production, causing silent failures or fatal errors. This guide provides comprehensive information about PHP extension dependencies, best practices, and prevention measures.

## Problem Statement

**Common Issue**: Code that works perfectly in local development fails silently or with fatal errors in production because PHP extensions are not available.

**Root Cause**: PHP extensions are not always enabled by default and may differ between development and production environments.

**Impact**:

- Silent failures (code executes but produces incorrect results)
- Fatal errors (page crashes completely)
- Poor user experience
- Difficult to debug (works locally but fails in production)

## Extension Categories

### Required Extensions

These extensions are **required** for core functionality and must be available:

#### json

- **Purpose**: JSON encoding/decoding
- **Status**: Included by default in PHP 8.0+
- **Functions**: `json_encode()`, `json_decode()`
- **Check**: `extension_loaded('json')`
- **Fallback**: None (critical - will fail if missing)

#### mbstring

- **Purpose**: Multibyte string handling for UTF-8 text processing
- **Status**: Optional but required for proper UTF-8 handling
- **Functions**: `mb_strlen()`, `mb_substr()`, `mb_strtolower()`, `mb_check_encoding()`, `mb_convert_encoding()`
- **Check**: `function_exists('mb_strlen')` or `extension_loaded('mbstring')`
- **Fallback**: Use `iconv` or standard PHP string functions (see fallback patterns)

#### iconv

- **Purpose**: Character encoding conversion
- **Status**: Optional but recommended as fallback for mbstring
- **Functions**: `iconv()`
- **Check**: `function_exists('iconv')`
- **Fallback**: Use `mb_convert_encoding()` if available, or skip encoding conversion

#### curl

- **Purpose**: HTTP client for API requests
- **Status**: Required for API integrations
- **Functions**: `curl_init()`, `curl_exec()`, `curl_setopt()`, `curl_close()`
- **Check**: `function_exists('curl_init')`
- **Fallback**: Use `file_get_contents()` if `allow_url_fopen` enabled (limited features)

### Optional Extensions

These extensions enhance functionality but have fallbacks:

#### gd

- **Purpose**: Image processing and manipulation
- **Status**: Optional - enhances image handling
- **Functions**: `imagecreatefromjpeg()`, `imagecreatefrompng()`, `imagejpeg()`, `imagepng()`
- **Check**: `extension_loaded('gd') && function_exists('imagecreatefromjpeg')`
- **Fallback**: Skip image processing, use original images

#### zip

- **Purpose**: ZIP archive handling
- **Status**: Optional - required for Excel generation
- **Functions**: `zip_open()`, `zip_read()`, `zip_entry_open()`
- **Check**: `extension_loaded('zip')`
- **Fallback**: Skip ZIP operations, use alternative methods

#### xml

- **Purpose**: XML parsing and processing
- **Status**: Optional - enhances XML handling
- **Functions**: `simplexml_load_string()`, `xml_parse()`
- **Check**: `extension_loaded('xml')`
- **Fallback**: Use alternative XML parsers or skip XML processing

## Best Practices

### 1. Always Check Before Use

**❌ WRONG:**

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

**✅ CORRECT:**

```php
// Check function exists before use
if (function_exists('mb_strtolower')) {
    $text = mb_strtolower($text, 'UTF-8');
} else {
    $text = strtolower($text);
}
```

### 2. Use Extension Loaded Check

**For extension-level checks:**

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

**For function-level checks:**

```php
if (function_exists('mb_strlen')) {
    // Function is available
} else {
    // Use fallback
}
```

### 3. Provide Fallbacks

**Always provide fallback logic for optional extensions:**

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

### 4. Log Fallback Usage

**Log when fallbacks are used for monitoring:**

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

### 5. Use Helper Functions

**Use existing helper functions when available:**

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

## Common Patterns

### mbstring Pattern

**Standard pattern for UTF-8 encoding:**

```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
}
```

### curl Pattern

**Standard pattern for HTTP requests:**

```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
    if (ini_get('allow_url_fopen')) {
        $response = file_get_contents($url);
    } else {
        throw new RuntimeException('cURL and allow_url_fopen both unavailable');
    }
}
```

### gd Pattern

**Standard pattern for image processing:**

```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
}
```

## Installation Instructions

### Ubuntu/Debian

```bash
# Install mbstring
sudo apt-get install php-mbstring

# Install iconv (usually included)
sudo apt-get install php-iconv

# Install curl
sudo apt-get install php-curl

# Install gd
sudo apt-get install php-gd

# Install zip
sudo apt-get install php-zip

# Install xml
sudo apt-get install php-xml

# Restart web server
sudo service php-fpm restart
# OR
sudo service apache2 restart
```

### CentOS/RHEL

```bash
# Install mbstring
sudo yum install php-mbstring

# Install iconv
sudo yum install php-iconv

# Install curl
sudo yum install php-curl

# Install gd
sudo yum install php-gd

# Install zip
sudo yum install php-zip

# Install xml
sudo yum install php-xml

# Restart web server
sudo systemctl restart php-fpm
# OR
sudo systemctl restart httpd
```

### macOS (Homebrew)

```bash
# Install mbstring
brew install php-mbstring

# Install iconv (usually included)
# Install curl (usually included)
# Install gd
brew install php-gd

# Restart PHP-FPM
brew services restart php
```

## Verification

### Check Extension Availability

**Command line:**

```bash
# List all loaded extensions
php -m

# Check specific extension
php -r "echo extension_loaded('mbstring') ? 'Loaded' : 'Not loaded';"

# Check PHP version
php -v
```

**PHP code:**

```php
// Check extension loaded
if (extension_loaded('mbstring')) {
    echo "mbstring is loaded\n";
} else {
    echo "mbstring is NOT loaded\n";
}

// Check function exists
if (function_exists('mb_strlen')) {
    echo "mb_strlen() is available\n";
} else {
    echo "mb_strlen() is NOT available\n";
}
```

**Web-accessible diagnostic:**

Access: `/v2/api/php-extensions-diagnostics.php`

## Troubleshooting

### Extension Not Loading

**Symptoms:**

- Function calls fail with "Call to undefined function"
- Extension shows as not loaded in `php -m`

**Solutions:**

1. **Check php.ini:**

   ```bash
   php --ini
   # Edit the loaded php.ini file
   # Uncomment: extension=mbstring
   ```

2. **Check extension directory:**

   ```bash
   php -i | grep extension_dir
   # Verify extension files exist in that directory
   ```

3. **Restart web server:**

   ```bash
   sudo service php-fpm restart
   # OR
   sudo service apache2 restart
   ```

4. **Check PHP version compatibility:**
   ```bash
   php -v
   # Some extensions require specific PHP versions
   ```

### Extension Works Locally But Not in Production

**Symptoms:**

- Code works in development
- Fails in production with extension errors

**Solutions:**

1. **Verify extension installed in production:**

   ```bash
   # On production server
   php -m | grep mbstring
   ```

2. **Check different php.ini files:**

   - CLI may use different php.ini than web server
   - PHP-FPM may use different php.ini than mod_php

3. **Check web server configuration:**

   - Apache: Check `.htaccess` or `httpd.conf`
   - Nginx: Check PHP-FPM configuration

4. **Use diagnostic endpoint:**
   - Access `/v2/api/php-extensions-diagnostics.php`
   - Compare with local environment

### Fallback Not Working

**Symptoms:**

- Extension missing but fallback not executing
- Code still fails even with fallback

**Solutions:**

1. **Verify fallback logic:**

   ```php
   // Add logging
   if (!extension_loaded('mbstring')) {
       error_log('mbstring not available, using fallback');
       // Fallback code
   }
   ```

2. **Test fallback locally:**

   - Temporarily disable extension in php.ini
   - Test fallback logic

3. **Check error logs:**
   - Review PHP error logs
   - Check for fallback usage messages

## Pre-Deployment Checklist

Before deploying 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
- [ ] Run extension validator: `php v2/scripts/dev-helpers/check-php-extensions.php`
- [ ] Run pre-deployment check: `php v2/scripts/dev-helpers/pre-deployment-check.php`
- [ ] Tested with extension disabled locally (if possible)
- [ ] Verified extension availability in production environment
- [ ] Checked error logs for fallback usage

## Related Documentation

- `.cursor/rules/php-extensions.mdc` - Cursor rules for PHP extensions
- `docs/development/PHP_EXTENSION_FALLBACK_PATTERNS.md` - Fallback patterns guide
- `docs/development/PRODUCTION_DEPLOYMENT_CHECKLIST.md` - Deployment checklist
- `docs/content/blog/MBSTRING_FIX_SUMMARY.md` - mbstring fix documentation
- `v2/helpers/php-extensions.php` - Extension helper functions

## Code Examples

### Example 1: Blog Schema Generator

**File**: `v2/config/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
}
```

### Example 2: Blog Template Helpers

**File**: `v2/config/blog-template-helpers.php`

```php
// Use helper function if available
$title_length = _has_mbstring() ? mb_strlen($title, 'UTF-8') : _mb_strlen_fallback($title, 'UTF-8');
```

### Example 3: Excel Generation

**File**: `v2/api/generate_excel.php`

```php
// Check required extensions
$required_extensions = ['zip', 'xml', 'gd', 'mbstring'];
$missing_extensions = [];
foreach ($required_extensions as $ext) {
    if (!extension_loaded($ext)) {
        $missing_extensions[] = $ext;
    }
}

if (!empty($missing_extensions)) {
    throw new Exception('Missing required PHP extensions: ' . implode(', ', $missing_extensions));
}
```

## Summary

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

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