PHP: Dynamic method calling — Part 2
This article delves deeper into PHP’s dynamic method invocation, presenting a more robust approach to method overloading.
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:
- call_user_func and call_user_func_array for non-static methods
- 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.
__call()
is triggered when invoking inaccessible methods in an object context.__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.