Skip to content

Commit 486257e

Browse files
committed
Add support for Output via symfony/console
1 parent 9fb48f7 commit 486257e

File tree

2 files changed

+53
-33
lines changed

2 files changed

+53
-33
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
"type": "library",
55
"version": "1.0.1",
66
"require": {
7-
"nemiah/php-fints": "^3.4"
7+
"nemiah/php-fints": "^3.4",
8+
"symfony/console": "^7.0"
89
},
910
"license": "MIT",
1011
"autoload": {

src/ActionService.php

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,32 @@
1212
use Fhp\Model\TanRequestChallengeImage;
1313
use InvalidArgumentException;
1414
use RuntimeException;
15+
use Symfony\Component\Console\Output\OutputInterface;
1516

1617
class ActionService
1718
{
18-
public static function login(FinTs $finTs): void
19+
private ?OutputInterface $output;
20+
21+
public function setOutput(OutputInterface $output): void
22+
{
23+
$this->output = $output;
24+
}
25+
26+
public function login(FinTs $finTs): void
1927
{
2028
$login = $finTs->login();
2129

2230
if ($login->needsTan()) {
23-
self::handleStrongAuthentication($finTs, $login);
31+
$this->handleStrongAuthentication($finTs, $login);
2432
}
2533
}
2634

27-
public static function action(FinTs $finTs, BaseAction $action): void
35+
public function action(FinTs $finTs, BaseAction $action): void
2836
{
2937
$finTs->execute($action);
3038

3139
if ($action->needsTan()) {
32-
self::handleStrongAuthentication($finTs, $action);
40+
$this->handleStrongAuthentication($finTs, $action);
3341
}
3442
}
3543

@@ -57,12 +65,12 @@ public static function action(FinTs $finTs, BaseAction $action): void
5765
*
5866
* @param BaseAction $action Some action that requires strong authentication.
5967
*/
60-
public static function handleStrongAuthentication(FinTs $finTs, BaseAction $action): void
68+
public function handleStrongAuthentication(FinTs $finTs, BaseAction $action): void
6169
{
6270
if ($finTs->getSelectedTanMode()?->isDecoupled() === true) {
63-
self::handleDecoupled($finTs, $action);
71+
$this->handleDecoupled($finTs, $action);
6472
} else {
65-
self::handleTan($finTs, $action);
73+
$this->handleTan($finTs, $action);
6674
}
6775
}
6876

@@ -71,39 +79,40 @@ public static function handleStrongAuthentication(FinTs $finTs, BaseAction $acti
7179
* application.
7280
* @param BaseAction $action Some action that requires a TAN.
7381
*/
74-
protected static function handleTan(FinTs $finTs, BaseAction $action): void
82+
protected function handleTan(FinTs $finTs, BaseAction $action): void
7583
{
7684
// Find out what sort of TAN we need, tell the user about it.
7785
$tanRequest = $action->getTanRequest();
78-
echo 'The bank requested a TAN.';
86+
$this->output('The bank requested a TAN.');
7987
if ($tanRequest?->getChallenge() !== null) {
80-
echo ' Instructions: ' . $tanRequest?->getChallenge();
88+
$this->output(' Instructions: ' . $tanRequest?->getChallenge());
8189
}
8290

83-
echo "\n";
8491
if ($tanRequest?->getTanMediumName() !== null) {
85-
echo 'Please use this device: ' . $tanRequest?->getTanMediumName() . "\n";
92+
$this->output('Please use this device: ' . $tanRequest?->getTanMediumName());
8693
}
8794

8895
// Challenge Image for PhotoTan/ChipTan
8996
if ($tanRequest?->getChallengeHhdUc() instanceof \Fhp\Syntax\Bin) {
9097
try {
9198
$flicker = new TanRequestChallengeFlicker($tanRequest?->getChallengeHhdUc());
92-
echo 'There is a challenge flicker.' . PHP_EOL;
99+
$this->output('There is a challenge flicker.');
93100
// save or output svg
94101
$flickerPattern = $flicker->getFlickerPattern();
95102
// other renderers can be implemented with this pattern
96103
$svg = new SvgRenderer($flickerPattern);
97-
echo $svg->getImage();
104+
$this->output($svg->getImage());
98105
} catch (InvalidArgumentException) {
99106
// was not a flicker
100107
$challengeImage = new TanRequestChallengeImage($tanRequest?->getChallengeHhdUc());
101-
echo 'There is a challenge image.' . PHP_EOL;
108+
$this->output('There is a challenge image.');
102109
// Save the challenge image somewhere
103110
// Alternative: HTML sample code
104-
echo '<img src="data:' . htmlspecialchars($challengeImage->getMimeType()) . ';base64,' . base64_encode(
105-
$challengeImage->getData()
106-
) . '" />' . PHP_EOL;
111+
$this->output(
112+
'<img src="data:' . htmlspecialchars($challengeImage->getMimeType()) . ';base64,' . base64_encode(
113+
$challengeImage->getData()
114+
) . '" />'
115+
);
107116
}
108117
}
109118

@@ -115,10 +124,10 @@ protected static function handleTan(FinTs $finTs, BaseAction $action): void
115124
// handling, not just one like in this simplified example). You *only* need to carry over the $persistedInstance
116125
// and the $persistedAction (which are simple strings) by storing them in some database or file where you can load
117126
// them again in a new PHP process when the user sends the TAN.
118-
echo "Please enter the TAN:\n";
127+
$this->output("Please enter the TAN:");
119128
$tan = trim(fgets(STDIN));
120129

121-
echo sprintf('Submitting TAN: %s%s', $tan, PHP_EOL);
130+
$this->output(sprintf('Submitting TAN: %s', $tan));
122131
$finTs->submitTan($action, $tan);
123132
}
124133

@@ -128,18 +137,17 @@ protected static function handleTan(FinTs $finTs, BaseAction $action): void
128137
* authentication at all, i.e. you could filter out any decoupled TanModes when letting the user choose.
129138
* @param BaseAction $action Some action that requires decoupled authentication.
130139
*/
131-
protected static function handleDecoupled(FinTs $finTs, BaseAction $action): void
140+
protected function handleDecoupled(FinTs $finTs, BaseAction $action): void
132141
{
133142
$tanMode = $finTs->getSelectedTanMode();
134143
$tanRequest = $action->getTanRequest();
135-
echo 'The bank requested authentication on another device.';
144+
$this->output('The bank requested authentication on another device.');
136145
if ($tanRequest?->getChallenge() !== null) {
137-
echo ' Instructions: ' . $tanRequest?->getChallenge();
146+
$this->output(' Instructions: ' . $tanRequest?->getChallenge());
138147
}
139148

140-
echo "\n";
141149
if ($tanRequest?->getTanMediumName() !== null) {
142-
echo 'Please check this device: ' . $tanRequest?->getTanMediumName() . "\n";
150+
$this->output('Please check this device: ' . $tanRequest?->getTanMediumName());
143151
}
144152

145153
// IMPORTANT: In your real application, you don't have to use sleep() in PHP. You can persist the state in the same
@@ -148,18 +156,18 @@ protected static function handleDecoupled(FinTs $finTs, BaseAction $action): voi
148156
// without polling entirely and just let the user confirm manually in all cases (i.e. only implement the `else`
149157
// branch below).
150158
if ($tanMode?->allowsAutomatedPolling() === true) {
151-
echo "Polling server to detect when the decoupled authentication is complete.\n";
159+
$this->output("Polling server to detect when the decoupled authentication is complete.");
152160
sleep($tanMode?->getFirstDecoupledCheckDelaySeconds());
153161
for ($attempt = 0;
154162
$tanMode?->getMaxDecoupledChecks() === 0 || $attempt < $tanMode?->getMaxDecoupledChecks();
155163
++$attempt
156164
) {
157165
if ($finTs->checkDecoupledSubmission($action)) {
158-
echo "Confirmed.\n";
166+
$this->output("Confirmed.");
159167
return;
160168
}
161169

162-
echo "Still waiting...\n";
170+
$this->output("Still waiting...");
163171
sleep($tanMode?->getPeriodicDecoupledCheckDelaySeconds());
164172
}
165173

@@ -168,17 +176,28 @@ protected static function handleDecoupled(FinTs $finTs, BaseAction $action): voi
168176

169177
if ($tanMode?->allowsManualConfirmation() === true) {
170178
do {
171-
echo "Please type 'done' and hit Return when you've completed the authentication on the other device.\n";
179+
$this->output(
180+
"Please type 'done' and hit Return when you've completed the authentication on the other device."
181+
);
172182
while (trim(fgets(STDIN)) !== 'done') {
173-
echo "Try again.\n";
183+
$this->output("Try again.");
174184
}
175185

176-
echo "Confirming that the action is done.\n";
186+
$this->output("Confirming that the action is done.");
177187
} while (! $finTs->checkDecoupledSubmission($action));
178188

179-
echo "Confirmed\n";
189+
$this->output("Confirmed");
180190
} else {
181191
throw new AssertionError('Server allows neither automated polling nor manual confirmation');
182192
}
183193
}
194+
195+
private function output(string $string): void
196+
{
197+
if ($this->output instanceof OutputInterface) {
198+
$this->output->writeln($string);
199+
} else {
200+
echo $string . PHP_EOL;
201+
}
202+
}
184203
}

0 commit comments

Comments
 (0)