PHP: Dynamic method calling — Part 2

Erland Muchasaj
3 min readApr 22, 2024

--

This article delves deeper into PHP’s dynamic method invocation, presenting a more robust approach to method overloading.

Dynamic Method Calling — Part 2
Dynamic Method Calling — Part 2

You can dynamically create PHP methods using the method “overloading” in PHP. For more information about property and method overloading you can read here. By leveraging method overloading, developers can enhance the flexibility and adaptability of their PHP applications.

As explained in PHP Magic there are 4 main functions that we can use to call dynamic functions:

  1. call_user_func and call_user_func_array for non-static methods
  2. forward_static_call and forward_static_call_array for static methods.

Also, we can use __call() or __callStatic() magic methods, to intercept calls to inaccessible methods within a class.

You can use call_user_func or call_user_func_array within __call() and forward_static_call or forward_static_call_array within __callStatic(), to handle such dynamic method invocations.

  1. __call() is triggered when invoking inaccessible methods in an object context.
  2. __callStatic() is triggered when invoking inaccessible methods in a static context.

Choosing between call_user_func and call_user_func_array depends on factors such as the number of arguments the function expects and whether the arguments are readily available as separate parameters. Similar considerations apply to forward_static_call and forward_static_call_array, offering developers flexibility in handling dynamic method calls based on specific requirements.

To illustrate dynamic method calling in practice, consider the example of a Importer class responsible for importing data from various sources. The import() method dynamically calls specific import handlers based on the source URL. By constructing method names dynamically and invoking them using __callStatic(), the Importer class demonstrates a robust approach to the dynamic method invocation.

<?php 

class Importer
{
/**
* Import data from a specific CSV source

* @param string $path path of file
* @param string $delimiter of CSV file
*
* @return void
* @throws UnableToProcessCsv
*/
public static function import(string $path, string $delimiter = ","): void
{
try {
// Open the CSV file for reading
$handle = fopen($path, 'r');

// if we do not have access to read the file we return
if ($handle === false) {
throw new UnableToProcessCsv("Unable to open CSV file: $path");
}

// Read and store the header row
$header = array_map('trim', fgetcsv($handle, null, $delimiter));

// Initialize an empty array to store current data
$data = [];

// Process the CSV file in chunks
// Read row from the CSV file
while (($row = fgetcsv($handle, null, $delimiter)) !== FALSE) {
// we save data as key => value pare
$data[] = array_combine($header, $row);
}

// Close the CSV file
fclose($handle);

if (! empty($data)) {
// Determine wwhere the data is comming from, based on URL:
$parse = parse_url($data[0]['path']);

$url = preg_replace('/^www\./i', '', $parse['host']);

$url = str_replace('.', '_', $url);

$value = ucwords(str_replace(['-', '_'], ' ', $url));

// we create a method like: handleDomainName and we then call it dynamically
$methodName = 'handle' . str_replace(' ', '', $value);

// call the method.
static::{$methodName}($data); # <= here we just call the method which will trigger __callStatic() method.
}

// Free memory by clearing the chunk data
$data = [];
} catch (Exception $e) {
// Handle any exceptions thrown during CSV processing
// Log the error or throw a custom exception
echo $e->getMessage();
}
}

/**
* Handle boat-specs.com Import.
*
* @param array $records The records to process.
*
* @return void
*/
protected static function importFromBoatSpecsCom($records): void
{
foreach ($records as $key => $record) {
// process each record and save into database
}
}

/**
* Handle boats.com Import.
*
* @param array $records The records to process.
*
* @return void
*/
protected static function importFromBoatsCom($records): void
{
foreach ($records as $key => $record) {
// process each record and save into database
}
}


public static function __callStatic(string $name, array $arguments) {
// Check if the method starts with "handle"
if (strpos($name, 'handle') === 0) {

// Extract the actual method name
$method = substr($name, 6);

// Construct internal method call
$methodName = 'importFrom'.$method; // ex: importFromBoatsCom()

// Check if the method exists
if (method_exists(__CLASS__, $methodName) && is_callable([__CLASS__, $methodName], true, $callable_name)) {

// Call the method with arguments using forward_static_call_array
return forward_static_call_array($callable_name, $arguments);

} else {
throw new \BadMethodCallException("Method '$methodName' is not implemented.");
}
} else {
throw new \InvalidArgumentException("Method '$name' does not exist.");
}
}
}

Importer::import('boats.csv');

Feel free to Subscribe for more content like this 🔔, clap 👏🏻 , comment 💬, and share the article with anyone you’d like

And as it always has been, I appreciate your support, and thanks for reading.

--

--