Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
Entry
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
6 / 6
8
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fromInstance
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fromClass
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fromFactory
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 command
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 signature
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Celema\Console;
6
7use Closure;
8use ValueError;
9
10/**
11 * A registered command: its metadata plus the factory producing the
12 * runnable instance on first use.
13 *
14 * @internal
15 */
16final class Entry
17{
18    private ?object $command = null;
19
20    /** @param class-string $class */
21    private function __construct(
22        public readonly Command $meta,
23        public readonly string $class,
24        private readonly Closure $factory,
25    ) {}
26
27    public static function fromInstance(object $command): self
28    {
29        return new self(Command::of($command), $command::class, static fn(): object => $command);
30    }
31
32    /**
33     * @param class-string $class
34     *
35     * @psalm-suppress MixedMethodCall The command class is known only at runtime
36     */
37    public static function fromClass(string $class): self
38    {
39        return new self(Command::of($class), $class, static fn(): object => new $class());
40    }
41
42    /** @param class-string $class */
43    public static function fromFactory(string $class, Closure $factory): self
44    {
45        return new self(Command::of($class), $class, $factory);
46    }
47
48    /**
49     * @psalm-suppress MixedAssignment, MixedReturnStatement Psalm does not narrow the
50     *     factory's result by instanceof against a class-string; PHPStan and Mago do
51     */
52    public function command(): object
53    {
54        if ($this->command === null) {
55            $command = ($this->factory)();
56
57            // The metadata was read from the keyed class; an unrelated
58            // object would be described by one class and run as another.
59            if (!$command instanceof $this->class) {
60                throw new ValueError(
61                    "Factory for command '{$this->meta->full()}' must return a {$this->class}",
62                );
63            }
64
65            $this->command = $command;
66        }
67
68        return $this->command;
69    }
70
71    /**
72     * Reads the command's signature off the keyed class.
73     */
74    public function signature(): Signature
75    {
76        return Signature::of($this->class, $this->meta->full());
77    }
78}