Skip to main content

Course Progress

Loading...

Understanding PHP Conditionals: Null Coalescing Operator

Duration: 30 minutes
Module 2: PHP Operators

Learning Objectives

  • Master PHP operators
  • Understand operator precedence
  • Apply operators in practical scenarios
  • Write efficient expressions

Introduction to the Null Coalescing Operator

Welcome to our continuing exploration of PHP conditional operators! In our previous sessions, we covered if, if-else, if-elseif-else, switch statements, and the ternary operator. Today, we're focusing on the null coalescing operator (??), a powerful addition to PHP introduced in version 7.0 that offers an elegant solution for providing default values when dealing with potentially null or undefined variables.

The Safety Net Analogy

Think of the null coalescing operator as a safety net in a circus. The tightrope walker (your variable) attempts to cross from one side to another. If the walker makes it across (the variable exists and isn't null), everything continues as planned. But if the walker falls (the variable is null or doesn't exist), the safety net (your fallback value) catches them, and the show goes on without interruption.

Diagram
Null or undefined Variable Check Use Original Value Use Fallback Value

Basic Null Coalescing Syntax

The null coalescing operator is represented by two question marks (??) and provides a way to check if a variable exists and is not null, returning an alternative value if it doesn't meet those conditions.

Basic Syntax


// Syntax:
$result = $potentially_null_or_undefined_var ?? $default_value;

// Example:
$username = $_GET['username'] ?? 'Guest';
                

In this example, if $_GET['username'] exists and is not null, its value will be assigned to $username. Otherwise, the string 'Guest' will be assigned instead.

Comparison with Traditional Methods

Before the null coalescing operator, you had to use more verbose approaches:


// Using isset() with ternary operator:
$username = isset($_GET['username']) ? $_GET['username'] : 'Guest';

// Using array_key_exists() for more specific checks:
$username = array_key_exists('username', $_GET) ? $_GET['username'] : 'Guest';

// Using null coalescing operator (PHP 7+):
$username = $_GET['username'] ?? 'Guest';
                

The null coalescing approach is much more concise and expressive, making your code cleaner and more readable.

Understanding the Behavior

Exactly What "Null Coalescing" Means

The term "coalesce" means to come together or form one mass or whole. In programming, it refers to merging or choosing from multiple possible values. The null coalescing operator specifically "coalesces" by checking if a value exists and is not null, then deciding which value to use.

The operator ?? returns:

  • The left operand ($a) when it exists and is not null
  • The right operand ($b) otherwise

$result = $a ?? $b;

// This is equivalent to:
$result = (isset($a) && $a !== null) ? $a : $b;
                

Key Differences from Ternary Operator

While the null coalescing operator might seem similar to the ternary operator, they serve different purposes:

Aspect Null Coalescing (??) Ternary (?:)
Primary Purpose Providing default values for potentially null/undefined variables General conditional expression for any boolean condition
What it checks If variable exists and is not null If condition evaluates to true
Handles undefined variables Yes, safely No, would cause an error
Treats empty string, 0, false as Valid values (returns them) Falsy values (could return alternative)

This distinction is crucial! Consider the following examples:


// Example with empty string
$name = "";
$display1 = $name ?: "No Name";   // Ternary: Returns "No Name" (empty string is falsy)
$display2 = $name ?? "No Name";   // Null coalescing: Returns "" (empty string exists and isn't null)

// Example with zero
$count = 0;
$display1 = $count ?: "N/A";      // Ternary: Returns "N/A" (0 is falsy)
$display2 = $count ?? "N/A";      // Null coalescing: Returns 0 (0 exists and isn't null)

// Example with false
$isActive = false;
$display1 = $isActive ?: "Unknown";   // Ternary: Returns "Unknown" (false is falsy)
$display2 = $isActive ?? "Unknown";   // Null coalescing: Returns false (false exists and isn't null)
                

Visual Comparison of Operators

Value Handling Comparison Value Type Ternary Operator (?:) Null Coalescing (??) null Returns alternative value Returns alternative value Undefined variable Causes error Returns alternative value Empty string ("") Returns alternative value Returns empty string Zero (0) Returns alternative value Returns zero Boolean false Returns alternative value Returns false Non-empty values Returns original value Returns original value Uses original value Uses alternative value/Error

Practical Usage of the Null Coalescing Operator

Use Case 1: Form Input Handling

The null coalescing operator shines when handling form submissions, ensuring you have valid default values:


// Processing form data safely
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
$age = $_POST['age'] ?? 0;
$subscribe = $_POST['subscribe'] ?? false;

// Without null coalescing, you'd need:
$name = isset($_POST['name']) ? $_POST['name'] : '';
$email = isset($_POST['email']) ? $_POST['email'] : '';
// and so on...
                

Use Case 2: Configuration Settings

When loading configuration values that might not be defined:


// Load configuration with defaults
$config = [
    'debug' => true,
    'cache_timeout' => 3600
    // 'theme' is not defined
];

// Get values with defaults for missing settings
$debug = $config['debug'] ?? false;
$cacheTimeout = $config['cache_timeout'] ?? 1800;
$theme = $config['theme'] ?? 'default';
$maxUploadSize = $config['max_upload_size'] ?? (1024 * 1024 * 10); // 10MB default
                

Use Case 3: API Response Handling

When working with API responses that might have missing fields:


// Imagine this is a JSON response decoded into an array
$apiResponse = [
    'status' => 'success',
    'data' => [
        'user' => [
            'name' => 'John Doe',
            'email' => 'john@example.com'
            // 'phone' is missing
        ]
    ]
    // 'meta' field is missing
];

// Safely access potentially missing nested values
$userName = $apiResponse['data']['user']['name'] ?? 'Unknown User';
$userPhone = $apiResponse['data']['user']['phone'] ?? 'No phone provided';
$metaData = $apiResponse['meta'] ?? [];
                

Use Case 4: WordPress Theme Customization

In WordPress theme development, the null coalescing operator is perfect for handling theme options:


// Get theme options with defaults
function get_theme_setting($key) {
    $options = get_option('my_theme_options');
    return $options[$key] ?? get_theme_default($key);
}

// Theme customization example
$headerColor = get_theme_mod('header_color') ?? '#333333';
$footerText = get_theme_mod('footer_text') ?? '© ' . date('Y') . ' ' . get_bloginfo('name');
$showSidebar = get_theme_mod('show_sidebar') ?? true;
                

Use Case 5: Language Localization

When handling translations that might be missing entries:


// Simplified translation function
function translate($key, $default = null) {
    global $translations;
    return $translations[$key] ?? ($default ?? $key);
}

// Usage
echo translate('welcome_message', 'Welcome to our website!');
echo translate('about_us');  // If missing, uses 'about_us' as the default
                

Chaining Null Coalescing Operators

One of the most powerful features of the null coalescing operator is the ability to chain multiple checks in sequence, creating a fallback cascade.

Basic Chaining Example


// Try multiple possible sources for a username
$username = $_GET['username'] ?? $_POST['username'] ?? $_SESSION['username'] ?? 'Guest';
                

This code tries to retrieve a username from GET parameters first, then POST parameters, then the session, and finally defaults to 'Guest' if none are available.

Diagram
null or undefined exists and not null null or undefined exists and not null null or undefined $_GET['username Use $_GET['username $_POST['username Use $_POST['username $_SESSION['username Use $_SESSION['username Guest

Complex Chaining Example


// Building a configuration system with multiple fallbacks
function getConfig($key) {
    global $userConfig, $groupConfig, $defaultConfig;
    
    return $userConfig[$key] ?? 
           $groupConfig[$key] ?? 
           $defaultConfig[$key] ?? 
           throw new Exception("No configuration found for key: $key");
}

// Usage
try {
    $debugMode = getConfig('debug_mode');
    $theme = getConfig('theme');
} catch (Exception $e) {
    echo "Configuration error: " . $e->getMessage();
}
                

Notice how in PHP 8.0+, you can even throw an exception as the final fallback in a null coalescing chain, which is extremely useful for required settings.

The Null Coalescing Assignment Operator (??=)

PHP 7.4 introduced an even more convenient variation: the null coalescing assignment operator (??=). This operator assigns a value to a variable only if the variable is null or doesn't exist.

Basic Syntax


// Syntax:
$variable ??= $value;

// This is equivalent to:
$variable = $variable ?? $value;
                

The null coalescing assignment operator combines the null coalescing operation with assignment, making your code even more concise.

Practical Examples


// Initialize variables only if they don't exist
function processRequest($options) {
    // Default options if not provided
    $options['timeout'] ??= 30;
    $options['retries'] ??= 3;
    $options['verify_ssl'] ??= true;
    
    // Now use the options...
    return makeApiRequest($options);
}

// Setting default session values
session_start();

// Initialize session variables only if they don't exist
$_SESSION['visits'] ??= 0;
$_SESSION['first_visit'] ??= time();

// Now we can safely increment visits
$_SESSION['visits']++;
                

Before vs. After Comparison


// Before PHP 7.4:
if (!isset($cache['user_profile'])) {
    $cache['user_profile'] = getUserProfile($userId);
}

// With PHP 7.4+ null coalescing assignment:
$cache['user_profile'] ??= getUserProfile($userId);
                

The new operator makes the code significantly cleaner and more readable.

Null Coalescing vs. isset() and empty()

Understanding how the null coalescing operator differs from the traditional isset() and empty() functions is crucial for using it effectively.

Comparison Table

Function/Operator What it checks Returns true for empty string Returns true for 0 Returns true for false Returns true for null Safe with undefined variables
isset($var) Variable exists and is not null Yes Yes Yes No Yes
empty($var) Variable is considered "empty" Yes Yes Yes Yes Yes
$var ?? $default Variable exists and is not null Keeps $var Keeps $var Keeps $var Uses $default Yes
!empty($var) Variable is not "empty" No No No No Yes

Practical Comparison


// Test values
$nullVar = null;
$emptyString = "";
$zero = 0;
$false = false;
$undefined; // Undefined variable

// Testing isset()
echo isset($nullVar) ? "Yes" : "No";        // No
echo isset($emptyString) ? "Yes" : "No";    // Yes
echo isset($zero) ? "Yes" : "No";           // Yes
echo isset($false) ? "Yes" : "No";          // Yes
echo isset($undefined) ? "Yes" : "No";      // No

// Testing empty()
echo empty($nullVar) ? "Yes" : "No";        // Yes
echo empty($emptyString) ? "Yes" : "No";    // Yes
echo empty($zero) ? "Yes" : "No";           // Yes
echo empty($false) ? "Yes" : "No";          // Yes
echo empty($undefined) ? "Yes" : "No";      // Yes

// Testing null coalescing
echo $nullVar ?? "Default";        // Default
echo $emptyString ?? "Default";    // "" (empty string)
echo $zero ?? "Default";           // 0
echo $false ?? "Default";          // false
echo $undefined ?? "Default";      // Default
                

This comparison demonstrates why the null coalescing operator is particularly useful when you want to accept "falsy" values like empty strings, 0, or false, but provide a default for null or undefined variables.

Choosing the Right Tool

  • Use isset() when you only need to check if a variable exists and is not null
  • Use empty() when you need to check if a variable is "empty" (null, 0, "", false, empty array, etc.)
  • Use ?? when you want to provide a default value for variables that don't exist or are null, while preserving other falsy values
  • Use ?: (ternary) when you want to provide a default for any falsy value

// Choose based on your specific needs:

// If you want to preserve 0, empty string, and false:
$count = $data['count'] ?? 10;  // 0 is a valid count

// If you consider 0, empty string, or false as invalid:
$count = $data['count'] ?: 10;  // 0 would be replaced with 10
                

Working with Nested Arrays

The null coalescing operator is particularly useful when dealing with deeply nested arrays, like configuration arrays or API responses.

Traditional Approach


// Safely getting a deeply nested value using isset()
$userCity = '';
if (isset($response['data']['user']['address']['city'])) {
    $userCity = $response['data']['user']['address']['city'];
} else {
    $userCity = 'Unknown';
}
                

Null Coalescing Approach


// Using null coalescing - but this still has an issue!
$userCity = $response['data']['user']['address']['city'] ?? 'Unknown';

// This will cause an error if any intermediate key doesn't exist
                

The problem with the above code is that if any of the intermediate keys (data, user, address) don't exist, PHP will throw an error.

Proper Nested Approach


// First approach: Check each level
$userCity = isset($response['data']) && 
           isset($response['data']['user']) && 
           isset($response['data']['user']['address']) && 
           isset($response['data']['user']['address']['city']) 
           ? $response['data']['user']['address']['city'] 
           : 'Unknown';

// Second approach: Use the null coalescing operator with intermediate default arrays
$userCity = ($response['data'] ?? [])['user'] ?? [])['address'] ?? [])['city'] ?? 'Unknown';

// Third approach (PHP 7.4+): Use null safe operator
$userCity = $response?->data?->user?->address?->city ?? 'Unknown';
                

The second and third approaches are much cleaner, with the third one being available in newer PHP versions.

Helper Function for Nested Arrays


/**
 * Safely get a value from a nested array
 * 
 * @param array $array The array to search in
 * @param string|array $path The path to the value (string with separator or array of keys)
 * @param mixed $default The default value if the path doesn't exist
 * @param string $separator The separator for string paths (default: .)
 * @return mixed The value or the default
 */
function array_get($array, $path, $default = null, $separator = '.') {
    // If the path is a string, convert it to an array
    if (is_string($path)) {
        $path = explode($separator, $path);
    }
    
    // Loop through the path
    foreach ($path as $key) {
        // If the current level is not an array or the key doesn't exist, return the default
        if (!is_array($array) || !array_key_exists($key, $array)) {
            return $default;
        }
        
        // Move down one level
        $array = $array[$key];
    }
    
    // Return the final value
    return $array;
}

// Usage:
$userCity = array_get($response, 'data.user.address.city', 'Unknown');
// Or with array path:
$userCity = array_get($response, ['data', 'user', 'address', 'city'], 'Unknown');
                

This helper function provides a clean way to safely access deeply nested array values with a concise syntax.

Compatibility Considerations

While the null coalescing operator is incredibly useful, it's important to be aware of compatibility issues when working with different PHP versions.

PHP Version Support

Feature PHP Version Required
Null coalescing operator (??) PHP 7.0+
Null coalescing assignment operator (??=) PHP 7.4+
Nullsafe operator (?->) PHP 8.0+

Fallback for Older PHP Versions

If you need to support PHP versions before 7.0, you'll need to use the traditional isset() approach:


// PHP 7.0+ version
$username = $_GET['username'] ?? 'Guest';

// Compatible version for PHP 5.x
$username = isset($_GET['username']) ? $_GET['username'] : 'Guest';

// Function to mimic ?? in older PHP versions
function nullCoalesce($var, $default) {
    return isset($var) ? $var : $default;
}

// Usage:
$username = nullCoalesce($_GET['username'], 'Guest');
                

Version-Dependent Code

If your code needs to run on multiple PHP versions, you can use version checks:


// Version-dependent code
if (PHP_VERSION_ID >= 70400) {
    // PHP 7.4+ - Can use null coalescing assignment
    $options['timeout'] ??= 30;
} else if (PHP_VERSION_ID >= 70000) {
    // PHP 7.0-7.3 - Can use null coalescing but not assignment
    $options['timeout'] = $options['timeout'] ?? 30;
} else {
    // PHP 5.x - Must use isset()
    $options['timeout'] = isset($options['timeout']) ? $options['timeout'] : 30;
}
                

Best Practices for Null Coalescing

Use for Default Values

The null coalescing operator is perfect for providing default values for missing or null variables:


// Good use of null coalescing
$perPage = $_GET['per_page'] ?? 20;
$sortBy = $_GET['sort_by'] ?? 'date';
$direction = $_GET['direction'] ?? 'desc';
                

Chain Responsibly

While chaining is powerful, don't overdo it. Keep it readable and consider line breaks for long chains:


// Too long for one line
$username = $_GET['username'] ?? $_POST['username'] ?? $_SESSION['username'] ?? $_COOKIE['remember_user'] ?? 'Guest';

// More readable with line breaks
$username = $_GET['username'] 
         ?? $_POST['username'] 
         ?? $_SESSION['username'] 
         ?? $_COOKIE['remember_user'] 
         ?? 'Guest';
                

Be Careful with Nested Arrays

Remember that null coalescing only checks one level deep. For nested arrays, use proper techniques as discussed in a previous section:


// Incorrect for nested arrays - might cause errors
$city = $user['address']['city'] ?? 'Unknown';

// Correct approach
$city = isset($user['address']) ? ($user['address']['city'] ?? 'Unknown') : 'Unknown';
// Or use a helper function:
$city = array_get($user, 'address.city', 'Unknown');
                

Understand When Not to Use It

The null coalescing operator isn't always the best choice. For example, if you consider empty strings or zeros as "missing" values, you might want to use other approaches:


// If 0 is a valid value:
$count = $data['count'] ?? 10;

// If you consider 0 as invalid and want to use the default:
$count = empty($data['count']) ? 10 : $data['count'];
// Or using the ternary operator:
$count = $data['count'] ?: 10;
                

Keep Default Values Sensible

Choose default values that make sense for your application context:


// Good defaults
$page = $_GET['page'] ?? 1; // First page makes sense as a default
$search = $_GET['q'] ?? ''; // Empty string is a sensible default for a search
$limit = $_GET['limit'] ?? 25; // Reasonable page size
                

WordPress-Specific Examples

Theme Customization


// functions.php - Getting theme options with defaults
function my_theme_get_option($key) {
    $options = get_option('my_theme_options', []);
    return $options[$key] ?? get_theme_default($key);
}

// Theme setup
function my_theme_setup() {
    // Set up theme defaults with null coalescing
    $logo_url = get_theme_mod('logo_url') ?? get_template_directory_uri() . '/images/default-logo.png';
    $sidebar_position = get_theme_mod('sidebar_position') ?? 'right';
    $footer_columns = get_theme_mod('footer_columns') ?? 4;
    
    // Register sidebars conditionally
    if (get_theme_mod('show_sidebar') ?? true) {
        register_sidebar([
            'name' => 'Main Sidebar',
            'id' => 'sidebar-main'
        ]);
    }
}
add_action('after_setup_theme', 'my_theme_setup');
                

Plugin Settings


// Plugin settings with defaults
class MyPlugin {
    private $settings;
    
    public function __construct() {
        $this->settings = get_option('my_plugin_settings', []);
    }
    
    public function get_setting($key) {
        return $this->settings[$key] ?? $this->get_default($key);
    }
    
    public function get_default($key) {
        $defaults = [
            'api_key' => '',
            'cache_time' => 3600,
            'enable_feature_x' => false,
            'max_items' => 10
        ];
        
        return $defaults[$key] ?? null;
    }
}

// Usage in a shortcode
function my_plugin_shortcode($atts) {
    $plugin = new MyPlugin();
    
    // Get attributes with defaults from settings
    $atts = shortcode_atts([
        'limit' => $plugin->get_setting('max_items'),
        'type' => 'recent',
        'category' => ''
    ], $atts);
    
    // Rest of the shortcode...
}
add_shortcode('my_plugin', 'my_plugin_shortcode');
                

Custom Post Type Display


// Template file for custom post type
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
    <header class="entry-header">
        <h1 class="entry-title"><?php the_title(); ?></h1>
    </header>
    
    <div class="entry-content">
        <?php
        // Get custom fields with default values
        $custom_fields = get_post_meta(get_the_ID());
        $location = $custom_fields['location'][0] ?? 'Online';
        $price = $custom_fields['price'][0] ?? 'Free';
        $event_date = $custom_fields['event_date'][0] ?? '';
        
        // Format event date if it exists
        $formatted_date = $event_date 
            ? date('F j, Y', strtotime($event_date)) 
            : 'Date TBA';
        ?>
        
        <div class="event-details">
            <p><strong>Date:</strong> <?php echo $formatted_date; ?></p>
            <p><strong>Location:</strong> <?php echo esc_html($location); ?></p>
            <p><strong>Price:</strong> <?php echo esc_html($price); ?></p>
        </div>
        
        <?php the_content(); ?>
    </div>
</article>
                

Practice Exercises

Exercise 1: Basic Null Coalescing

Create a function that retrieves user profile information from an array, using null coalescing for default values.

Reveal Solution

function getUserProfile($userData) {
    // Extract user information with defaults using null coalescing
    $name = $userData['name'] ?? 'Anonymous User';
    $email = $userData['email'] ?? 'No email provided';
    $age = $userData['age'] ?? 'Not specified';
    $city = $userData['city'] ?? 'Unknown location';
    
    return "
        <div class='user-profile'>
            <h2>{$name}</h2>
            <p><strong>Email:</strong> {$email}</p>
            <p><strong>Age:</strong> {$age}</p>
            <p><strong>Location:</strong> {$city}</p>
        </div>
    ";
}

// Test with partial data
$user1 = [
    'name' => 'John Doe',
    'email' => 'john@example.com'
    // age and city are missing
];

echo getUserProfile($user1);
                    

Exercise 2: Chaining and Assignment

Create a function to process an API request, using chained null coalescing for parameters and null coalescing assignment for defaults.

Reveal Solution

function processApiRequest($params) {
    // Default parameters
    $params['method'] ??= 'GET';
    $params['format'] ??= 'json';
    $params['version'] ??= 'v1';
    
    // Get the endpoint from various possible sources
    $endpoint = $params['endpoint'] ?? 
                $params['resource'] ?? 
                $params['target'] ?? 
                'default';
    
    // Create the API URL
    $baseUrl = "https://api.example.com/{$params['version']}/";
    $url = $baseUrl . $endpoint;
    
    return "Making {$params['method']} request to: $url (Format: {$params['format']})";
}

// Test with minimal parameters
$request1 = [
    'endpoint' => 'users'
];

// Test with alternative parameter names
$request2 = [
    'resource' => 'products',
    'method' => 'POST',
    'version' => 'v2'
];

echo processApiRequest($request1) . "
";
echo processApiRequest($request2);
                    

Exercise 3: Working with Nested Arrays

Create a function to safely extract information from a nested API response, handling potential missing values.

Reveal Solution

function extractProductInfo($apiResponse) {
    // Using intermediate default arrays
    $product = ($apiResponse['data'] ?? [])['product'] ?? [];
    
    // Extract product details with defaults
    $name = $product['name'] ?? 'Unknown Product';
    $price = $product['price'] ?? 0.00;
    $currency = ($product['pricing'] ?? [])['currency'] ?? 'USD';
    $inStock = ($product['inventory'] ?? [])['in_stock'] ?? false;
    $stockLevel = ($product['inventory'] ?? [])['level'] ?? 0;
    
    // Create stock message
    $stockMessage = $inStock 
        ? "In Stock ({$stockLevel} available)" 
        : "Out of Stock";
    
    return "
        <div class='product'>
            <h3>{$name}</h3>
            <p class='price'>{$currency} {$price}</p>
            <p class='stock {$inStock ? 'in-stock' : 'out-of-stock'}'>{$stockMessage}</p>
        </div>
    ";
}

// Test with a complete response
$response1 = [
    'data' => [
        'product' => [
            'name' => 'Smartphone X',
            'price' => 499.99,
            'pricing' => [
                'currency' => 'EUR',
                'discount' => 0
            ],
            'inventory' => [
                'in_stock' => true,
                'level' => 42
            ]
        ]
    ]
];

// Test with a partial response
$response2 = [
    'data' => [
        'product' => [
            'name' => 'Laptop Pro',
            'price' => 1299.99
            // Missing pricing and inventory
        ]
    ]
];

// Test with a minimal response
$response3 = [
    'data' => []
];

echo extractProductInfo($response1);
echo extractProductInfo($response2);
echo extractProductInfo($response3);
                    

Exercise 4: WordPress Theme Options

Create a set of functions to handle WordPress theme options with appropriate defaults using null coalescing.

Reveal Solution

/**
 * Get theme options with defaults
 * 
 * @return array Theme options
 */
function get_theme_options() {
    return get_option('my_theme_options', []);
}

/**
 * Get a single theme option with default
 * 
 * @param string $key Option key
 * @param mixed $default Default value if not set
 * @return mixed Option value
 */
function get_theme_option($key, $default = null) {
    $options = get_theme_options();
    return $options[$key] ?? $default;
}

/**
 * Get theme color scheme with defaults
 * 
 * @return array Color scheme
 */
function get_theme_colors() {
    // Get color scheme name first
    $scheme_name = get_theme_option('color_scheme', 'default');
    
    // Default colors
    $default_colors = [
        'primary' => '#3366cc',
        'secondary' => '#ff9900',
        'text' => '#333333',
        'background' => '#ffffff',
        'accent' => '#66cc99'
    ];
    
    // Get saved colors
    $saved_colors = get_theme_option('colors', []);
    
    // Merge with defaults, so we have all needed colors
    return [
        'scheme' => $scheme_name,
        'primary' => $saved_colors['primary'] ?? $default_colors['primary'],
        'secondary' => $saved_colors['secondary'] ?? $default_colors['secondary'],
        'text' => $saved_colors['text'] ?? $default_colors['text'],
        'background' => $saved_colors['background'] ?? $default_colors['background'],
        'accent' => $saved_colors['accent'] ?? $default_colors['accent']
    ];
}

/**
 * Output inline CSS for theme colors
 */
function output_theme_colors_css() {
    $colors = get_theme_colors();
    
    $css = "
        :root {
            --color-primary: {$colors['primary']};
            --color-secondary: {$colors['secondary']};
            --color-text: {$colors['text']};
            --color-background: {$colors['background']};
            --color-accent: {$colors['accent']};
        }
        
        body {
            background-color: var(--color-background);
            color: var(--color-text);
        }
        
        a {
            color: var(--color-primary);
        }
        
        .button, .btn {
            background-color: var(--color-primary);
            color: white;
        }
        
        .button.secondary, .btn.secondary {
            background-color: var(--color-secondary);
        }
    ";
    
    echo "<style id='theme-colors'>{$css}</style>";
}

// Example of usage in header.php
// add_action('wp_head', 'output_theme_colors_css');

// Test (simulating WordPress functions)
function get_option($option_name, $default = false) {
    // This would normally get from WordPress database
    $options = [
        'my_theme_options' => [
            'color_scheme' => 'custom',
            'colors' => [
                'primary' => '#007bff',
                'secondary' => '#6c757d'
                // text, background, and accent are missing
            ]
        ]
    ];
    
    return $options[$option_name] ?? $default;
}

// Test the functions
$colors = get_theme_colors();
print_r($colors);
                    

Additional Resources

Homework Assignment

Building on our student dashboard, create a more sophisticated system for handling student data with null coalescing and related operators:

  1. Create a PHP script that processes student data from multiple sources, using the null coalescing operator to determine the most reliable source for each piece of information.
  2. Your script should handle the following student data (some of which might be missing from each source):
    • Basic information: ID, name, contact info
    • Academic information: Scores, attendance, courses
    • Additional information: Activities, notes, special accommodations
  3. Implement a function that retrieves student data with appropriate defaults using null coalescing.
  4. Implement a function that handles nested student data (like contact information) safely.
  5. Create helper functions to generate HTML reports for:
    • Student profile
    • Academic performance report
    • Attendance summary
  6. Create a function to store and retrieve data with default settings using null coalescing assignment.
  7. Show appropriate visual indicators in your HTML output based on performance metrics.
  8. Bonus: Implement a simple caching system for student data with expiration times, using null coalescing to check cache validity.

Submit your code and a brief explanation of where and how you used the null coalescing operator and related techniques, and why these were the appropriate choices for those situations.