ChrisBAshton/smartresolution

View on GitHub
webapp/core/helpers/FunctionParser.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

/**
 * Parses a function name and calls the function. Copes with anonymous, global and public class functions.
 * Used in conjunction with the publish-subscribe module mechanisms.
 */
class FunctionParser {

    /**
     * FunctionParser constructor: parses the function and executes it immediately.
     * @param string|function $functionToCall Anonymous/global/named class function.
     * @param array           $parameters     (Optional) Parameters to pass to the function. These parameters are extracted from the array, e.g. array('foo', 'bar') => myFunction($foo, $bar) rather than myFunction(array($foo, $bar))
     */
    function __construct($functionToCall, $parameters = array()) {
        // if $functionToCall is an anonymous function or a string representing a global function
        if (is_callable($functionToCall)) {
            call_user_func_array($functionToCall, $parameters);
        }
        else {
            // $functionToCall is the name of a class function (e.g. 'Foo->bar') and needs to be parsed
            $this->tryToCallClassFunction($functionToCall, $parameters);
        }
    }

    /**
     * The passed function was not an anonymous function nor a global function, so is probably a named class function, e.g. MyClass->myFunction.
     * We need to parse the string to deduce which class to instantiate, then instantiate the class and call its function.
     * @param  string $functionToCall Named class function.
     * @param  array  $parameters     (Optional) Parameters to pass to the function. These parameters are extracted from the array, e.g. array('foo', 'bar') => myFunction($foo, $bar) rather than myFunction(array($foo, $bar))
     */
    private function tryToCallClassFunction($functionToCall, $parameters) {
        $this->checkIfFunctionToCallIsValid($functionToCall);
        $className     = $this->extractClassName($functionToCall);
        $methodName    = $this->extractMethodName($functionToCall);
        $classInstance = new $className();
        call_user_func_array(array($classInstance, $methodName), $parameters);
    }

    /**
     * Attempts to validate that the class method string at least looks like a class method string, even if the class method itself might not exist. If it doesn't look like a class function, an exception is raised.
     * @param  string $functionToCall Class method, e.g. 'MyClass->myFunction'
     */
    private function checkIfFunctionToCallIsValid($functionToCall) {
        $classFunction = strpos($functionToCall, '->') !== false;
        if (!$classFunction) {
            Utils::instance()->throwException('Invalid function: ' . $functionToCall);
        }
    }

    /**
     * Extracts the class name from the class/method string. 'MyClass->myFunction' => 'MyClass'
     * @param  string $functionToCall Class/method string.
     * @return string                 Class name.
     */
    private function extractClassName($functionToCall) {
        $parts = explode('->', $functionToCall);
        return $parts[0];
    }

    /**
     * Extracts the method name from the class/method string. 'MyClass->myFunction' => 'myFunction'
     * @param  string $functionToCall Class/method string.
     * @return string                 Method name.
     */
    private function extractMethodName($functionToCall) {
        $parts = explode('->', $functionToCall);
        return $parts[1];
    }
}