Issues (24)

src/Console/ChatCommand.php (2 issues)

1
<?php
2
/*
3
 * This file is part of Rivescript-php
4
 *
5
 * (c) Shea Lewis <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace Axiom\Rivescript\Console;
12
13
use Axiom\Rivescript\Exceptions\ParseException;
14
use Axiom\Rivescript\Rivescript;
15
use Symfony\Component\Console\Command\Command;
16
use Symfony\Component\Console\Question\Question;
17
use Symfony\Component\Console\Input\InputArgument;
18
use Symfony\Component\Console\Input\InputInterface;
19
use Symfony\Component\Console\Output\OutputInterface;
20
21
/**
22
 * ChatCommand class
23
 *
24
 * This class handles input from the interactive console from
25
 * the command-line.
26
 *
27
 * PHP version 7.4 and higher.
28
 *
29
 * @category Core
30
 * @package  Contracts
31
 * @author   Shea Lewis <[email protected]>
32
 * @license  https://opensource.org/licenses/MIT MIT
33
 * @link     https://github.com/axiom-labs/rivescript-php
34
 * @since    0.3.0
35
 */
36
class ChatCommand extends Command
37
{
38
    /**
39
     * @var Rivescript
40
     */
41
    protected Rivescript $rivescript;
42
43
    /**
44
     * Create a new ChatCommand instance.
45
     *
46
     * @param Rivescript $rivescript The Rivescript client.
47
     */
48
    public function __construct(Rivescript $rivescript)
49
    {
50
        $this->rivescript = $rivescript;
51
52
        parent::__construct();
53
    }
54
55
    /**
56
     * Configure the console command.
57
     *
58
     * @return void
59
     */
60
    public function configure(): void
61
    {
62
        $this->setName('chat')
63
            ->setDescription('Chat with a Rivescript instance')
64
            ->addArgument('source', InputArgument::REQUIRED, 'Your Rivescript source file');
65
    }
66
67
    /**
68
     * Execute the console command.
69
     *
70
     * @param InputInterface  $input  The input interface the message came from.
71
     * @param OutputInterface $output The output interface to output the response to.
72
     *
73
     * @return int
74
     */
75
    public function execute(InputInterface $input, OutputInterface $output): int
76
    {
77
        try {
78
            $this->rivescript->onSay = function (string $msg, int $verbosity) use ($output) {
79
                $output->writeln("Say: {$msg}", $verbosity);
80
            };
81
82
83
            $source = $this->loadFiles($input->getArgument('source'));
84
85
            $this->rivescript->load($source);
86
87
            $loadedSource = explode('/', $input->getArgument('source'));
88
            $loadedSource = end($loadedSource);
89
90
            $output->writeln('RiveScript Interpreter (PHP) -- Interactive Console v2.0');
91
            $output->writeln('--------------------------------------------------------');
92
            $output->writeln('RiveScript Version:       2.0');
93
            $output->writeln('Currently Loaded Source:  ' . $loadedSource);
94
            $output->writeln('');
95
            $output->writeln('You are now chatting with a RiveScript bot. Type a message and press Return');
96
            $output->writeln('to send it. When finished, type "/quit" to exit the interactive console.');
97
            $output->writeln('');
98
99
            $this->waitForUserInput($input, $output);
100
        } catch (ParseException $e) {
101
            $error = "<error>{$e->getMessage()}</error>";
102
            $output->writeln($error);
103
104
            log_warning($e->getMessage());
105
        }
106
107
        return 0;
108
    }
109
110
    /**
111
     * Wait and listen for user input.
112
     *
113
     * @param InputInterface  $input  The input interface the message came from.
114
     * @param OutputInterface $output The output interface to output the response to.
115
     *
116
     * @return void
117
     */
118
    protected function waitForUserInput(InputInterface $input, OutputInterface $output): void
119
    {
120
        $helper = $this->getHelper('question');
121
        $question = new Question('<info>You > </info>');
122
123
        $message = $helper->ask($input, $output, $question);
124
125
        $this->listenForConsoleCommands($input, $output, $message);
126
127
        $this->getBotResponse($input, $output, $message);
128
    }
129
130
    /**
131
     * Listen for console commands before passing message to interpreter.
132
     *
133
     * @param InputInterface  $input   The input interface the message came from.
134
     * @param OutputInterface $output  The output interface to output the response to.
135
     * @param string          $message The message typed in the console.
136
     *
137
     * @return int
138
     */
139
    protected function listenForConsoleCommands(InputInterface $input, OutputInterface $output, string $message): int
140
    {
141
        if ($message === '/quit') {
142
            $output->writeln('Exiting...');
143
            die();
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
144
        }
145
146
        if ($message === '/reload') {
147
            return $this->execute($input, $output);
148
        }
149
150
        if ($message === '/help') {
151
            $output->writeln('');
152
            $output->writeln('<comment>Usage:</comment>');
153
            $output->writeln('  Type a message and press Return to send.');
154
            $output->writeln('');
155
            $output->writeln('<comment>Commands:</comment>');
156
            $output->writeln('  <info>/help</info>        Show this text');
157
            $output->writeln('  <info>/reload</info>      Reload the interactive console');
158
            $output->writeln('  <info>/quit</info>        Quit the interative console');
159
            $output->writeln('');
160
161
            $this->waitForUserInput($input, $output);
162
        }
163
164
        return 0;
165
    }
166
167
    /**
168
     * Pass along user message to interpreter and fetch a reply.
169
     *
170
     * @param InputInterface  $input   The input interface the message came from.
171
     * @param OutputInterface $output  The output interface to output the response to.
172
     * @param string          $message The message typed in the console.
173
     *
174
     * @return void
175
     */
176
    protected function getBotResponse(InputInterface $input, OutputInterface $output, string $message): void
177
    {
178
        $bot = 'Bot > ';
179
        $reply = $this->rivescript->reply($message);
180
        $response = "<info>{$reply}</info>";
181
182
        $output->writeln($bot . $response);
183
184
        $this->waitForUserInput($input, $output);
185
    }
186
187
    /**
188
     * Load and return an array of files.
189
     *
190
     * @param string $info One file or directory of rivescript files.
191
     *
192
     * @return array<string>
193
     */
194
    private function loadFiles(string $info): array
195
    {
196
        if (is_dir($info)) {
197
            $directory = realpath($info);
198
            $files = [];
199
            $brains = glob($directory . '/*.rive');
200
201
            foreach ($brains as $brain) {
202
                $files[] = $brain;
203
            }
204
205
            return $files;
206
        }
207
208
        return (array)$info;
209
    }
210
}
211