How to migrate a java command pattern using runnable to PHP 7.4?

In PHP you can achieve the same using call_user_func that is similar to method reference in Java.

<?php

namespace StockCommandNS;

//Command interface
interface Order {
    public function execute();
}

//Receiver class
class StockTrade {

    public function buy() {
        print("You want to buy stocks\n");
    }

    public function sell() {
        print("You want to sell stocks\n");
    }
}

//Invoker class
class Agent {

    public function placeOrder(Order $order) {
        $order->execute($order);
    }
}

//ConcreteCommand Class
class GenericOrder implements Order {

    private $action;

    public function __construct($action) {
        $this->action = $action;
    }

    public function execute() {
        call_user_func($this->action);
    }
}

$stock = new StockTrade();
$bsc = new GenericOrder([$stock, 'buy']);
$ssc = new GenericOrder([$stock, 'sell']);
$agent = new Agent();
$agent->placeOrder($bsc); // Buy Shares
$agent->placeOrder($ssc); // Sell Shares

Output for 7.2.0 - 7.4.3

You want to buy stocks
You want to sell stocks

Run PHP code: https://3v4l.org/fWo20

Another less clean option is to use variable function.

class GenericOrder implements Order {

    private $stock;
    private $action;

    public function __construct($stock, $action) {
        $this->stock = $stock;
        $this->action = $action;
    }

    public function execute() {
        $method = $this->action;
        $this->stock->$method();
    }
}

$bsc = new GenericOrder($stock, 'buy');
$ssc = new GenericOrder($stock, 'sell');

I'm not sure that the approach with method references is better in all cases. As a rule of thumb, you should always consider on per-use-case basis when to use method references or anonymous function is the Command pattern.


As Evgeniy already mentioned you can use call_user_func().

Since there is many ways how to solve this I've added my solutions to your question. You can also make an object callable by adding the __invoke method inside a class. It's also possible to return a callable function. I've added in total 3 examples for it.

This is my version of your MyCommand class in java which is used for all 3 examples.

class MyCommand implements Order
{
    private $action;

    public function __construct(callable $action)
    {
        $this->action = $action;
    }

    public function execute()
    {
        // Option 1) use call_user_function
        call_user_func($this->action);

        // Option 2) define it as a variable and call it by adding `()`
        //$action = $this->action;
        //$action();
    }
}

Example 1) A callable function (https://3v4l.org/FVTEK)

class Stock
{
    public function buy(): callable
    {
        return function () {
            echo "You want to buy stocks via callable function" . PHP_EOL;
        };
    }

    public function sell(): callable
    {
        return function () {
            echo "You want to sell stocks via callable function" . PHP_EOL;
        };
    }
}

$stock = new Stock();
$bsc = new MyCommand($stock->buy());
$ssc = new MyCommand($stock->sell());

$bsc->execute();
$ssc->execute();

Example 2) A callable class (https://3v4l.org/BrKjv)

class StockBuy
{
    public function __invoke()
    {
        echo "You want to buy stocks via __invoke()" . PHP_EOL;
    }
}

class StockSell
{
    public function __invoke()
    {
        echo "You want to sell stocks __invoke()" . PHP_EOL;
    }
}

$bsc = new MyCommand(new StockBuy());
$ssc = new MyCommand(new StockSell());

$bsc->execute();
$ssc->execute();

Example 3) Static member functions which return callable. Just an example to be more close to java (https://3v4l.org/PKk4B)

class Stock
{
    static public function buy(): callable
    {
        return function () {
            echo "You want to buy stocks via callable function" . PHP_EOL;
        };

        // or as callable object
        // return new StockBuy();
    }

    static public function sell(): callable
    {
        return function () {
            echo "You want to sell stocks via callable function" . PHP_EOL;
        };

        // or as callable object
        // return new StockSell();
    }
}

$bsc = new MyCommand(Stock::buy());
$ssc = new MyCommand(Stock::sell());
$bsc->execute();
$ssc->execute();

Please let me know if you have any further questions.