/ Gists / 23. Command
On gists

23. Command

PHP Patterns

pic.md Raw #

command1.php Raw #

<?php

interface Command
{
    public function handle() : void;
}



final class CreateListing implements Command
{
    private const MIN_TITLE_LENGTH = 10;
    private const MIN_CONTENT_LENGTH = 15;
    private const MIN_AUTHOR_LENGTH = 5;
    private $repository;
    private $title;
    private $content;
    private $author;

    public function __construct(ListingRepository $repository, string $title, string $content, string $author)
    {
        $this->repository = $repository;
        $this->title = $title;
        $this->content = $content;
        $this->author = $author;
    }

    private function validate(): void
    {
        if (strlen($this->title) < self::MIN_TITLE_LENGTH) {
            throw new LengthException(sprintf("Title is too short. Must be at least %d characters",
                self::MIN_TITLE_LENGTH));
        }
        if (strlen($this->content) < self::MIN_CONTENT_LENGTH) {
            throw new LengthException(sprintf("Content is too short. Must be at least %d characters",
                self::MIN_CONTENT_LENGTH));
        }
        if (strlen($this->author) < self::MIN_AUTHOR_LENGTH) {
            throw new LengthException(sprintf("Author name is too short. Must be at least %d characters",
                self::MIN_AUTHOR_LENGTH));
        }
    }

    public function handle(): void
    {
        $this->validate();
        $this->repository->create($this->title, $this->content, $this->author);
    }
}


final class DeleteListing implements Command
{
    private $repository;
    private $listingUid;

    public function __construct(ListingRepository $repository, string $listingUid)
    {
        $this->repository = $repository;
        $this->listingUid = $listingUid;
    }

    public function handle(): void
    {
        $this->repository->delete($this->listingUid);
    }
}

final class ListingRepository
{
    public function create(string $title, string $content, string $author): void
    {
        echo sprintf("Creating new listing by \"%s\" and title \"%s\"", $author, $title).PHP_EOL;
        echo sprintf("Content: \"%s\"", $content).PHP_EOL;
        echo sprintf("Generated uid: \"%s\"", uniqid()).PHP_EOL;
    }

    public function delete(string $uid): void
    {
        echo sprintf("Removing job listing with uid: \"%s\"", $uid).PHP_EOL;
    }
}


final class Client
{
    private $listingRepository;

    public function __construct(ListingRepository $repository)
    {
        $this->listingRepository = $repository;
    }

    public function createListing(string $title, string $content, string $author): void
    {
        $command = new CreateListing($this->listingRepository, $title, $content, $author);
        $command->handle();
    }

    public function deleteListing(string $listingUid) : void
    {
        $command = new DeleteListing($this->listingRepository, $listingUid);
        $command->handle();
    }

}


$client = new Client(new ListingRepository());
$client->createListing(
    "New job listing",
    "This is a content of a listing",
    "Company"
);
$client->deleteListing("Unique id");

command2.php Raw #

<?php

namespace RefactoringGuru\Command\Conceptual;

/**
 * The Command interface declares a method for executing a command.
 */
interface Command
{
    public function execute(): void;
}

/**
 * Some commands can implement simple operations on their own.
 */
class SimpleCommand implements Command
{
    private $payload;

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

    public function execute(): void
    {
        echo "SimpleCommand: See, I can do simple things like printing (" . $this->payload . ")\n";
    }
}

/**
 * However, some commands can delegate more complex operations to other objects,
 * called "receivers."
 */
class ComplexCommand implements Command
{
    /**
     * @var Receiver
     */
    private $receiver;

    /**
     * Context data, required for launching the receiver's methods.
     */
    private $a;

    private $b;

    /**
     * Complex commands can accept one or several receiver objects along with
     * any context data via the constructor.
     */
    public function __construct(Receiver $receiver, string $a, string $b)
    {
        $this->receiver = $receiver;
        $this->a = $a;
        $this->b = $b;
    }

    /**
     * Commands can delegate to any methods of a receiver.
     */
    public function execute(): void
    {
        echo "ComplexCommand: Complex stuff should be done by a receiver object.\n";
        $this->receiver->doSomething($this->a);
        $this->receiver->doSomethingElse($this->b);
    }
}

/**
 * The Receiver classes contain some important business logic. They know how to
 * perform all kinds of operations, associated with carrying out a request. In
 * fact, any class may serve as a Receiver.
 */
class Receiver
{
    public function doSomething(string $a): void
    {
        echo "Receiver: Working on (" . $a . ".)\n";
    }

    public function doSomethingElse(string $b): void
    {
        echo "Receiver: Also working on (" . $b . ".)\n";
    }
}

/**
 * The Invoker is associated with one or several commands. It sends a request to
 * the command.
 */
class Invoker
{
    /**
     * @var Command
     */
    private $onStart;

    /**
     * @var Command
     */
    private $onFinish;

    /**
     * Initialize commands.
     */
    public function setOnStart(Command $command): void
    {
        $this->onStart = $command;
    }

    public function setOnFinish(Command $command): void
    {
        $this->onFinish = $command;
    }

    /**
     * The Invoker does not depend on concrete command or receiver classes. The
     * Invoker passes a request to a receiver indirectly, by executing a
     * command.
     */
    public function doSomethingImportant(): void
    {
        echo "Invoker: Does anybody want something done before I begin?\n";
        if ($this->onStart instanceof Command) {
            $this->onStart->execute();
        }

        echo "Invoker: ...doing something really important...\n";

        echo "Invoker: Does anybody want something done after I finish?\n";
        if ($this->onFinish instanceof Command) {
            $this->onFinish->execute();
        }
    }
}

/**
 * The client code can parameterize an invoker with any commands.
 */
$invoker = new Invoker;
$invoker->setOnStart(new SimpleCommand("Say Hi!"));
$receiver = new Receiver;
$invoker->setOnFinish(new ComplexCommand($receiver, "Send email", "Save report"));

$invoker->doSomethingImportant();

command3.php Raw #

<?php

interface Command
{
    /**
     * this is the most important method in the Command pattern,
     * The Receiver goes in the constructor.
     */
    public function execute();
}


interface UndoableCommand extends Command
{
    /**
     * This method is used to undo change made by command execution
     */
    public function undo();
}


class HelloCommand implements Command
{
    private Receiver $output;

    /**
     * Each concrete command is built with different receivers.
     * There can be one, many or completely no receivers, but there can be other commands in the parameters
     */
    public function __construct(Receiver $console)
    {
        $this->output = $console;
    }

    /**
     * execute and output "Hello World".
     */
    public function execute()
    {
        // sometimes, there is no receiver and this is the command which does all the work
        $this->output->write('Hello World');
    }
}


class AddMessageDateCommand implements UndoableCommand
{
    private Receiver $output;

    /**
     * Each concrete command is built with different receivers.
     * There can be one, many or completely no receivers, but there can be other commands in the parameters.
     */
    public function __construct(Receiver $console)
    {
        $this->output = $console;
    }

    /**
     * Execute and make receiver to enable displaying messages date.
     */
    public function execute()
    {
        // sometimes, there is no receiver and this is the command which
        // does all the work
        $this->output->enableDate();
    }

    /**
     * Undo the command and make receiver to disable displaying messages date.
     */
    public function undo()
    {
        // sometimes, there is no receiver and this is the command which
        // does all the work
        $this->output->disableDate();
    }
}


/**
 * Receiver is a specific service with its own contract and can be only concrete.
 */
class Receiver
{
    private bool $enableDate = false;

    /**
     * @var string[]
     */
    private array $output = [];

    public function write(string $str)
    {
        if ($this->enableDate) {
            $str .= ' ['.date('Y-m-d').']';
        }

        $this->output[] = $str;
    }

    public function getOutput(): string
    {
        return join("\n", $this->output);
    }

    /**
     * Enable receiver to display message date
     */
    public function enableDate()
    {
        $this->enableDate = true;
    }

    /**
     * Disable receiver to display message date
     */
    public function disableDate()
    {
        $this->enableDate = false;
    }
}


/**
 * Invoker is using the command given to it.
 * Example : an Application in SF2.
 */
class Invoker
{
    private Command $command;

    /**
     * in the invoker we find this kind of method for subscribing the command
     * There can be also a stack, a list, a fixed set ...
     */
    public function setCommand(Command $cmd)
    {
        $this->command = $cmd;
    }

    /**
     * executes the command; the invoker is the same whatever is the command
     */
    public function run()
    {
        $this->command->execute();
    }
}


// USAGE
$invoker = new Invoker();
$receiver = new Receiver();

$invoker->setCommand(new HelloCommand($receiver));
$invoker->run();
$this->assertSame('Hello World', $receiver->getOutput());

// https://designpatternsphp.readthedocs.io/en/latest/Behavioral/Command/README.html