Session 4.3 – Call-back Functions

Module 4: PHP Filters and Handlers | Duration: 1 hr

Learning Objectives

By the end of this session, students will be able to:

  • Understand the concept of callback functions and their importance
  • Implement different types of callback functions in PHP
  • Create and use anonymous functions (lambda functions)
  • Work with closures and understand variable scope
  • Utilize arrow functions (PHP 7.4+) for concise syntax
  • Apply built-in PHP functions that accept callbacks
  • Build custom callback-based systems for flexible code architecture

Introduction to Callback Functions

A callback function is a function that is passed as an argument to another function and is executed at a later time. Callbacks are powerful tools for creating flexible, reusable, and modular code.

Key Insight

Callback functions enable you to write more dynamic and flexible code by allowing functions to execute custom behavior that is defined at runtime rather than at the time the function is written.

What Are Callback Functions?

In PHP, a callback is any callable code that can be executed. This includes:

Named Functions

Regular functions defined with the function keyword

Anonymous Functions

Functions without a name (lambda functions)

Closures

Anonymous functions that capture variables from the parent scope

Arrow Functions

Short syntax for simple closures (PHP 7.4+)

Simple Callback Example:
<?php
// Define a function that accepts a callback
function processArray($array, $callback) {
    $result = [];
    foreach ($array as $item) {
        $result[] = $callback($item);
    }
    return $result;
}

// Define a callback function
function double($number) {
    return $number * 2;
}

// Use the callback
$numbers = [1, 2, 3, 4, 5];
$doubled = processArray($numbers, 'double');

print_r($doubled); // Output: [2, 4, 6, 8, 10]
?>

Types of Callback Functions

1. String Callback (Function Name):
<?php
function greet($name) {
    return "Hello, " . $name . "!";
}

// Pass function name as string
$result = call_user_func('greet', 'John');
echo $result; // Output: Hello, John!
?>
2. Array Callback (Object Method):
<?php
class Calculator {
    public function add($a, $b) {
        return $a + $b;
    }

    public static function multiply($a, $b) {
        return $a * $b;
    }
}

$calc = new Calculator();

// Instance method callback
$result1 = call_user_func([$calc, 'add'], 5, 3);
echo $result1; // Output: 8

// Static method callback
$result2 = call_user_func(['Calculator', 'multiply'], 5, 3);
echo $result2; // Output: 15

// Alternative syntax for static methods
$result3 = call_user_func('Calculator::multiply', 5, 3);
echo $result3; // Output: 15
?>
3. Variable Function:
<?php
function sayHello() {
    return "Hello!";
}

// Store function name in variable
$functionName = 'sayHello';

// Call function using variable
echo $functionName(); // Output: Hello!
?>

Anonymous Functions (Lambda Functions)

Anonymous functions, also known as lambda functions, are functions without a name. They can be stored in variables and passed as arguments to other functions.

Basic Anonymous Function:
<?php
// Assign anonymous function to variable
$greeting = function($name) {
    return "Hello, " . $name . "!";
};

// Call the function
echo $greeting("Alice"); // Output: Hello, Alice!

// Pass anonymous function as callback
$numbers = [1, 2, 3, 4, 5];
$squared = array_map(function($n) {
    return $n * $n;
}, $numbers);

print_r($squared); // Output: [1, 4, 9, 16, 25]
?>
Anonymous Function with Use Cases:
<?php
// Filtering with anonymous function
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

$evenNumbers = array_filter($numbers, function($n) {
    return $n % 2 == 0;
});

print_r($evenNumbers); // Output: [2, 4, 6, 8, 10]

// Sorting with custom comparison
$fruits = ['banana', 'apple', 'cherry', 'date'];

usort($fruits, function($a, $b) {
    return strlen($a) - strlen($b);
});

print_r($fruits); // Sorted by length
?>
Returning Anonymous Functions:
<?php
function createMultiplier($factor) {
    return function($number) use ($factor) {
        return $number * $factor;
    };
}

$double = createMultiplier(2);
$triple = createMultiplier(3);

echo $double(5);  // Output: 10
echo $triple(5);  // Output: 15
?>

Closures and Variable Scope

A closure is an anonymous function that can access variables from the parent scope using the use keyword.

Basic Closure Example:
<?php
$message = "Hello";

// Closure accessing parent scope variable
$greet = function($name) use ($message) {
    return $message . ", " . $name . "!";
};

echo $greet("Bob"); // Output: Hello, Bob!

// Note: $message is captured by value (not reference)
$message = "Hi";
echo $greet("Bob"); // Still outputs: Hello, Bob!
?>
Closure with Reference:
<?php
$counter = 0;

// Capture by reference using &
$increment = function() use (&$counter) {
    $counter++;
    return $counter;
};

echo $increment(); // Output: 1
echo $increment(); // Output: 2
echo $increment(); // Output: 3
echo $counter;     // Output: 3
?>
Closure Factory Pattern:
<?php
class CounterFactory {
    public static function createCounter($initial = 0) {
        $count = $initial;

        return [
            'increment' => function() use (&$count) {
                return ++$count;
            },
            'decrement' => function() use (&$count) {
                return --$count;
            },
            'get' => function() use (&$count) {
                return $count;
            },
            'reset' => function() use (&$count, $initial) {
                $count = $initial;
                return $count;
            }
        ];
    }
}

$counter = CounterFactory::createCounter(10);

echo $counter['increment'](); // Output: 11
echo $counter['increment'](); // Output: 12
echo $counter['decrement'](); // Output: 11
echo $counter['get']();       // Output: 11
echo $counter['reset']();     // Output: 10
?>
Practical Closure Example - Event System:
<?php
class EventManager {
    private $listeners = [];

    public function on($event, $callback) {
        if (!isset($this->listeners[$event])) {
            $this->listeners[$event] = [];
        }
        $this->listeners[$event][] = $callback;
    }

    public function trigger($event, $data = null) {
        if (isset($this->listeners[$event])) {
            foreach ($this->listeners[$event] as $callback) {
                $callback($data);
            }
        }
    }
}

$events = new EventManager();

// Register event listeners using closures
$events->on('user.login', function($user) {
    echo "User {$user} logged in\n";
});

$events->on('user.login', function($user) {
    echo "Sending welcome email to {$user}\n";
});

$events->on('user.logout', function($user) {
    echo "User {$user} logged out\n";
});

// Trigger events
$events->trigger('user.login', 'john@example.com');
$events->trigger('user.logout', 'john@example.com');
?>

Arrow Functions (PHP 7.4+)

Arrow functions provide a shorter syntax for simple anonymous functions and automatically capture variables from the parent scope.

Arrow Function Syntax:
<?php
// Traditional anonymous function
$traditional = function($n) {
    return $n * 2;
};

// Arrow function (shorter syntax)
$arrow = fn($n) => $n * 2;

echo $traditional(5); // Output: 10
echo $arrow(5);       // Output: 10
?>
Arrow Functions with Array Operations:
<?php
$numbers = [1, 2, 3, 4, 5];

// Traditional way
$doubled1 = array_map(function($n) {
    return $n * 2;
}, $numbers);

// Arrow function way (cleaner)
$doubled2 = array_map(fn($n) => $n * 2, $numbers);

// Multiple operations
$result = array_filter(
    array_map(fn($n) => $n * 2, $numbers),
    fn($n) => $n > 5
);

print_r($result); // Output: [6, 8, 10]
?>
Automatic Variable Capture:
<?php
$multiplier = 3;

// Arrow functions automatically capture variables
$multiply = fn($n) => $n * $multiplier;

echo $multiply(5); // Output: 15

// No need for 'use' keyword like in closures
// Traditional closure equivalent:
$multiplyClosure = function($n) use ($multiplier) {
    return $n * $multiplier;
};
?>
Arrow Function Limitations
  • Can only contain a single expression
  • Automatically returns the result (no explicit return statement)
  • Cannot modify variables from parent scope (captured by value only)
  • No multi-line body allowed

Built-in PHP Functions with Callbacks

PHP provides many built-in functions that accept callbacks for various operations.

1. array_map() - Transform Array Elements:
<?php
$numbers = [1, 2, 3, 4, 5];

// Square each number
$squared = array_map(fn($n) => $n ** 2, $numbers);
print_r($squared); // [1, 4, 9, 16, 25]

// Convert to uppercase
$names = ['alice', 'bob', 'charlie'];
$upper = array_map('strtoupper', $names);
print_r($upper); // ['ALICE', 'BOB', 'CHARLIE']

// Multiple arrays
$a = [1, 2, 3];
$b = [10, 20, 30];
$sum = array_map(fn($x, $y) => $x + $y, $a, $b);
print_r($sum); // [11, 22, 33]
?>
2. array_filter() - Filter Array Elements:
<?php
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// Get even numbers
$even = array_filter($numbers, fn($n) => $n % 2 == 0);
print_r($even); // [2, 4, 6, 8, 10]

// Filter with keys
$data = ['a' => 1, 'b' => 2, 'c' => 3];
$filtered = array_filter($data, fn($key) => $key !== 'b', ARRAY_FILTER_USE_KEY);
print_r($filtered); // ['a' => 1, 'c' => 3]

// Filter with both key and value
$products = [
    'apple' => 2.50,
    'banana' => 1.20,
    'cherry' => 5.00
];
$expensive = array_filter(
    $products,
    fn($price, $name) => $price > 2 && strlen($name) > 5,
    ARRAY_FILTER_USE_BOTH
);
print_r($expensive);
?>
3. array_reduce() - Reduce Array to Single Value:
<?php
$numbers = [1, 2, 3, 4, 5];

// Sum all numbers
$sum = array_reduce($numbers, fn($carry, $item) => $carry + $item, 0);
echo $sum; // Output: 15

// Product of all numbers
$product = array_reduce($numbers, fn($carry, $item) => $carry * $item, 1);
echo $product; // Output: 120

// Build associative array
$items = ['apple', 'banana', 'cherry'];
$indexed = array_reduce($items, function($carry, $item) {
    $carry[$item] = strlen($item);
    return $carry;
}, []);
print_r($indexed); // ['apple' => 5, 'banana' => 6, 'cherry' => 6]
?>
4. usort() / uasort() / uksort() - Custom Sorting:
<?php
// Sort by length
$words = ['elephant', 'cat', 'dog', 'butterfly'];
usort($words, fn($a, $b) => strlen($a) - strlen($b));
print_r($words);

// Sort objects
$users = [
    ['name' => 'Alice', 'age' => 25],
    ['name' => 'Bob', 'age' => 20],
    ['name' => 'Charlie', 'age' => 30]
];

usort($users, fn($a, $b) => $a['age'] - $b['age']);
print_r($users); // Sorted by age

// Sort descending
$numbers = [3, 1, 4, 1, 5, 9, 2, 6];
usort($numbers, fn($a, $b) => $b - $a);
print_r($numbers);
?>
5. array_walk() - Apply Function to Each Element:
<?php
$fruits = ['apple', 'banana', 'cherry'];

// Modify array in place
array_walk($fruits, function(&$item, $key) {
    $item = strtoupper($item) . " ({$key})";
});
print_r($fruits); // ['APPLE (0)', 'BANANA (1)', 'CHERRY (2)']

// With additional parameter
$prices = [10, 20, 30];
array_walk($prices, function(&$price, $key, $tax) {
    $price = $price * (1 + $tax);
}, 0.1); // 10% tax

print_r($prices); // [11, 22, 33]
?>
6. call_user_func() / call_user_func_array():
<?php
// call_user_func
function add($a, $b) {
    return $a + $b;
}

$result = call_user_func('add', 5, 3);
echo $result; // Output: 8

// call_user_func_array (with array of arguments)
$args = [5, 3];
$result = call_user_func_array('add', $args);
echo $result; // Output: 8

// With anonymous function
$multiply = fn($a, $b) => $a * $b;
$result = call_user_func($multiply, 4, 5);
echo $result; // Output: 20
?>

Practical Examples

Example 1: Custom Validation System
<?php
class Validator {
    private $rules = [];

    public function addRule($field, $callback, $message) {
        $this->rules[$field][] = [
            'callback' => $callback,
            'message' => $message
        ];
    }

    public function validate($data) {
        $errors = [];

        foreach ($this->rules as $field => $rules) {
            if (!isset($data[$field])) {
                continue;
            }

            foreach ($rules as $rule) {
                if (!$rule['callback']($data[$field])) {
                    $errors[$field][] = $rule['message'];
                }
            }
        }

        return $errors;
    }
}

// Usage
$validator = new Validator();

// Add validation rules using callbacks
$validator->addRule('email', fn($value) => filter_var($value, FILTER_VALIDATE_EMAIL),
    'Invalid email format');

$validator->addRule('age', fn($value) => is_numeric($value) && $value >= 18,
    'Age must be 18 or older');

$validator->addRule('username', fn($value) => strlen($value) >= 3,
    'Username must be at least 3 characters');

// Validate data
$formData = [
    'email' => 'invalid-email',
    'age' => 15,
    'username' => 'ab'
];

$errors = $validator->validate($formData);
print_r($errors);
?>
Example 2: Pipeline Pattern
<?php
class Pipeline {
    private $pipes = [];

    public function pipe($callback) {
        $this->pipes[] = $callback;
        return $this;
    }

    public function process($value) {
        return array_reduce($this->pipes, function($carry, $pipe) {
            return $pipe($carry);
        }, $value);
    }
}

// Usage
$pipeline = new Pipeline();

$result = $pipeline
    ->pipe(fn($x) => $x * 2)
    ->pipe(fn($x) => $x + 10)
    ->pipe(fn($x) => $x / 2)
    ->process(5);

echo $result; // Output: 10
// Process: 5 * 2 = 10, 10 + 10 = 20, 20 / 2 = 10

// Text processing pipeline
$textPipeline = new Pipeline();

$text = "  hello WORLD  ";
$processed = $textPipeline
    ->pipe('trim')
    ->pipe('strtolower')
    ->pipe(fn($s) => ucfirst($s))
    ->pipe(fn($s) => $s . '!')
    ->process($text);

echo $processed; // Output: Hello world!
?>
Example 3: Middleware System
<?php
class MiddlewareManager {
    private $middlewares = [];

    public function add($middleware) {
        $this->middlewares[] = $middleware;
    }

    public function handle($request, $finalHandler) {
        $next = $finalHandler;

        // Build middleware chain in reverse
        foreach (array_reverse($this->middlewares) as $middleware) {
            $next = function($request) use ($middleware, $next) {
                return $middleware($request, $next);
            };
        }

        return $next($request);
    }
}

// Usage
$manager = new MiddlewareManager();

// Authentication middleware
$manager->add(function($request, $next) {
    echo "Checking authentication...\n";
    if (!isset($request['user'])) {
        return "Authentication failed";
    }
    return $next($request);
});

// Logging middleware
$manager->add(function($request, $next) {
    echo "Logging request: " . $request['path'] . "\n";
    return $next($request);
});

// Validation middleware
$manager->add(function($request, $next) {
    echo "Validating request...\n";
    return $next($request);
});

// Final handler
$finalHandler = function($request) {
    return "Request processed: " . $request['path'];
};

// Process request
$request = ['user' => 'john', 'path' => '/api/data'];
$result = $manager->handle($request, $finalHandler);
echo $result;
?>
Example 4: Retry Mechanism with Callbacks
<?php
function retry($callback, $maxAttempts = 3, $delay = 1000) {
    $attempts = 0;

    while ($attempts < $maxAttempts) {
        try {
            return $callback();
        } catch (Exception $e) {
            $attempts++;

            if ($attempts >= $maxAttempts) {
                throw $e;
            }

            echo "Attempt {$attempts} failed. Retrying...\n";
            usleep($delay * 1000); // Convert to microseconds
        }
    }
}

// Usage
$apiCall = function() {
    static $callCount = 0;
    $callCount++;

    echo "API call attempt #{$callCount}\n";

    // Simulate failure on first 2 attempts
    if ($callCount < 3) {
        throw new Exception("API unavailable");
    }

    return "Success!";
};

try {
    $result = retry($apiCall, 5, 500);
    echo $result;
} catch (Exception $e) {
    echo "All attempts failed: " . $e->getMessage();
}
?>

Session Summary

Key Points
  • Callback functions are functions passed as arguments to other functions
  • PHP supports multiple callback types: named functions, anonymous functions, closures, and arrow functions
  • Anonymous functions provide flexibility for inline function definitions
  • Closures can access variables from parent scope using the use keyword
  • Arrow functions (PHP 7.4+) provide concise syntax for simple operations
  • PHP has many built-in functions that work with callbacks: array_map, array_filter, array_reduce, etc.
  • Callbacks enable flexible architectures like pipelines, middleware, and event systems
  • Proper use of callbacks improves code reusability and maintainability
Next Session Preview

In the next session, we will explore Working with JSON in PHP, learning how to encode and decode JSON data, handle API responses, and build RESTful web services.