Understand more magic methods: __callStatic()
Tags: programming, php, magicmethods, programmingtips
Published: 2021-08-24 09:50:40

As you know, magic methods are special methods which override PHP's default's action when certain actions are performed on an object. The following method names are considered magical: `__construct()`, `__destruct()`, `__call()`, `__callStatic()`, `__get()`, `__set()`, `__isset()`, `__unset()`, `__sleep()`, `__wakeup()`, `__serialize()`, `__unserialize()`, `__toString()`, `__invoke()`, `__set_state()`, `__clone()`, and `__debugInfo()`. All magic methods, with the exception of `__construct()`, `__destruct()`, and `__clone()`, must be declared as public, otherwise an E_WARNING is emitted.

__callStatic() is method overloading, triggered when invoking inaccessible methods in a static context.

```php
public static __callStatic(string $name, array $arguments): mixed
```

The $name argument is the name of the method being called. The $arguments argument is an enumerated array containing the parameters passed to the $name'ed method.

With regard to `__callStatic()`, you can call a method via double colon, and rename functions to short/pretty name

```php
class Str
{
    /**
     * Shortcut methods
     */
    private static $methods = [
        'chunk' => 'chunk_split',
        'split' => 'str_split',
        'length' => 'strlen',
        'reverse' => 'strrev',
        'countWord' => 'str_word_count',
    ];
    
    /**
     * Trigger when invoking inaccessible methods in a static context
     */
    public static function __callStatic(string $method, array $parameters)
    {
        if (!array_key_exists($method, self::$methods)) {
            throw new Exception('The ' . $method . ' is not supported.');
        }

        return call_user_func_array(self::$methods[$method], $parameters);
    }
    
    /**
     * Parse query string to varaibles
     */
    public static function parse(string $str)
    {
        parse_str($str, $output);
        return $output;
    }
}

$data = "Happy New Year!";
$countWord = Str::countWord($data, 1);
var_dump($countWord);

$queryStr = "first=value&arr[]=foo+bar&arr[]=baz";
$output = Str::parse($queryStr);
var_dump($output);
```

```php
class Arr
{
    /**
     * Shortcut methods
     */
    private static $methods = [
        'search' => 'array_search',
        'chunk' => 'array_chunk',
        'combine' => 'array_combine',
        'count' => 'array_count_values'
    ];

    /**
     * Trigger when invoking inaccessible methods in a static context
     */
    public static function __callStatic(string $method, array $parameters)
    {
        if (!array_key_exists($method, self::$methods)) {
            throw new Exception('The ' . $method . ' is not supported.');
        }

        return call_user_func_array(self::$methods[$method], $parameters);
    }

    /**
     * Count all elements in an array
     */
    public static function length(array $arr)
    {
        return count($arr);
    }
}

$arr = [1,2,3,4,5];
$arr2 = [9,8,7,6,5];

$rs = Arr::combine($arr, $arr2);
var_dump($rs);

$length = Arr::length($arr);
var_dump($length);
```

Another way uses this overloading method that you can define your own function without `static` keyword, access using double colon. However, this way restricts you must define methods as `protected`

```php
class Helper
{
    /**
     * __callStatic
     * @param  string $method
     * @param  mixed $args
     * @return void
     */
    public static function __callStatic($method, $args) {
        $called = get_called_class();
        $class = new $called();
        return $class->$method(...$args);
    }
    
    protected function generateToken($length)
    {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $charactersLength = strlen($characters);
        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[rand(0, $charactersLength - 1)];
        }
        return $randomString;
    }
}

$result = Helper::generateToken(10);
var_dump($result);
```