1

The object is to make the name of the function shorter.

I have a function called somedescriptivefunctionName.

function somedescriptivefunctionName($a) {
   echo $a;
}

I want to assign it to $this->f so that I do not litter the code with long names.

$this->f('Yahoo!!'); // will echo 'Yahoo!!'

e.g. I imagine something like this:

$this->f = $this->somedescriptivefunctionName; // wrong way

Is this possible?

Of course I could make a new function f, and return somedescriptivefunctionName(). But this is not the point.

conanDrum
  • 215
  • 2
  • 7

2 Answers2

1

You can bind a property to a closure and invoke the property as a method:

class T{
    private $f;
    function abcdefghijklmnopqrstuvwxyz($param){
        echo $param;
    }
    function __construct(){
        $this->f = fn() => $this->abcdefghijklmnopqrstuvwxyz(...func_get_args());
    }
    function doWork() {
        ($this->f)('hello');
        $g = $this->f;
        $g('world');
    }
}

(new T())->doWork();

In this code, the class f property is bound to a closure via an arrow function, which became available as of PHP 7.4, and we can dynamically get the arguments passed via func_get_args. If you are using a version less than 7.4 you can also just use a regular anonymous function.

Unfortunately, do to name resolution issues, properties cannot be invoked as methods without using an extra set of parentheses which means you have to do ($this->f)('hello'). If you want to skip that, you can further assign the class property to a local variable which can then be invoked normally as the $g is done.

edit

Here's a version of the constructor that doesn't use arrow functions, it is invoked in the same way.

    function __construct()
    {
        $this->f = function () {
            return $this->abcdefghijklmnopqrstuvwxyz(...func_get_args());
        };
    }
Chris Haas
  • 53,986
  • 12
  • 141
  • 274
  • This is very good. Almost perfect if not for the fact that it is php7.4+ and the following: $this->f = fn() => $this->plcfg->errorTitle(...func_get_args()); $this->errorTitle=$this->f; echo ($this->errorTitle)('fatal','hello'); Is there away to get to this stage where we do not need the brackets e.g.: echo $this->errorTitle('fatal','hello'); If we can do that, then the call will be exactly the same in both classes (the current class, and the remote class which lives in $this->plcfg) Thanks!!! – conanDrum Jul 06 '20 at 06:08
1

You will store somewhere the string

$f = "somedescriptivefunctionName";

and then call the function by specifying its name in curly brackets:

$this->{$f}('Yahoo!!');

EDIT

According to the comment section, the aim is to call a method of an instance of another class by the same name. You can extend the config class to achieve a somewhat similar behavior, but I think that's not acceptable as a solution in this case. You can convert your class into a decorator, like

class SomeClassDecorator
{
    protected $_instance;

    public function myMethod() {
        return strtoupper( $this->_instance->someMethod() );
    }

    public function __construct(SomeClass $instance) {
        $this->_instance = $instance;
    }

    public function __call($method, $args) {
        return call_user_func_array(array($this->_instance, $method), $args);
    }

    public function __get($key) {
        return $this->_instance->$key;
    }

    public function __set($key, $val) {
        return $this->_instance->$key = $val;
    }

    // can implement additional (magic) methods here ...
}

The code above was copied from Gordon's amazing answer here: How to add a method to an existing class in PHP?

An evolution of this idea could be to create a decorator for your config class as a base class and extend that base class for your classes that are configured by this, so you will implement a single decorator and (re)use it.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • I can confirm that this is somewhat useful, but I need something better. Using your method I do this and it works: $ref = new ReflectionClass($this->config_class); $this->plcfg=$ref->newInstanceWithoutConstructor(); $errorTitle='errorTitle'; echo $this->plcfg->${errorTitle}('fatal','asdf'); My target is to actually be able to do this: echo $this->errorTitle('fatal','asdf'); If we can do that, then the call will be exactly the same in both classes (the current class, and the remote class which lives in $this->plcfg) Thanks!!! – conanDrum Jul 06 '20 at 06:19