Skip to content

Commit 799e66d

Browse files
dragonmantankGreg Holmes
andauthored
Added support for PSR-3 (#272)
* Added support for PSR-3 Co-authored-by: Greg Holmes <[email protected]>
1 parent 0994517 commit 799e66d

File tree

8 files changed

+200
-6
lines changed

8 files changed

+200
-6
lines changed

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -814,8 +814,20 @@ If you have a conflicting package installation that cannot co-exist with our rec
814814

815815
See the [Packagist page for client-implementation](https://packagist.org/providers/php-http/client-implementation) for options.
816816

817-
Contributing
818-
------------
817+
### Enabling Request/Response Logging
818+
819+
Our client library has support for logging the request and response for debugging via PSR-3 compatible logging mechanisms. If the `debug` option is passed into the client and a PSR-3 compatible logger is set in our client's service factory, we will use the logger for debugging purposes.
820+
821+
```php
822+
$client = new \Vonage\Client(new \Vonage\Client\Credentials\Basic('abcd1234', 's3cr3tk3y'), ['debug' => true]);
823+
$logger = new \Monolog\Logger('test');
824+
$logger->pushHandler(new \Monolog\Handler\StreamHandler(__DIR__ . '/log.txt', \Monolog\Logger::DEBUG));
825+
$client->getFactory()->set(\PSR\Log\LoggerInterface::class, $logger);
826+
```
827+
828+
**ENABLING DEBUGING LOGGING HAS THE POTENTIAL FOR LOGGING SENSITIVE INFORMATION, DO NOT ENABLE IN PRODUCTION**
829+
830+
## Contributing
819831

820832
This library is actively developed, and we love to hear from you! Please feel free to [create an issue][issues] or [open a pull request][pulls] with your questions, comments, suggestions and feedback.
821833

@@ -828,4 +840,3 @@ This library is actively developed, and we love to hear from you! Please feel fr
828840
[spec]: https://github.com/Nexmo/client-library-specification
829841
[issues]: https://github.com/Vonage/vonage-php-core/issues
830842
[pulls]: https://github.com/Vonage/vonage-php-core/pulls
831-

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"composer/package-versions-deprecated": "^1.11",
2626
"psr/container": "^1.0",
2727
"psr/http-client-implementation": "^1.0",
28-
"vonage/nexmo-bridge": "^0.1.0"
28+
"vonage/nexmo-bridge": "^0.1.0",
29+
"psr/log": "^1.1"
2930
},
3031
"require-dev": {
3132
"guzzlehttp/guzzle": ">=6",

src/Client.php

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
use Psr\Http\Client\ClientInterface;
2323
use Psr\Http\Message\RequestInterface;
2424
use Psr\Http\Message\ResponseInterface;
25+
use Psr\Log\LoggerInterface;
26+
use Psr\Log\LogLevel;
2527
use RuntimeException;
2628
use Vonage\Account\ClientFactory;
2729
use Vonage\Application\ClientFactory as ApplicationClientFactory;
@@ -41,6 +43,7 @@
4143
use Vonage\Conversion\ClientFactory as ConversionClientFactory;
4244
use Vonage\Entity\EntityInterface;
4345
use Vonage\Insights\ClientFactory as InsightsClientFactory;
46+
use Vonage\Logger\LoggerAwareInterface;
4447
use Vonage\Message\Client as MessageClient;
4548
use Vonage\Numbers\ClientFactory as NumbersClientFactory;
4649
use Vonage\Redact\ClientFactory as RedactClientFactory;
@@ -68,6 +71,7 @@
6871
use function str_replace;
6972
use function strpos;
7073
use function unserialize;
74+
use Vonage\Logger\LoggerTrait;
7175

7276
/**
7377
* Vonage API Client, allows access to the API from PHP.
@@ -86,8 +90,10 @@
8690
* @property string restUrl
8791
* @property string apiUrl
8892
*/
89-
class Client
93+
class Client implements LoggerAwareInterface
9094
{
95+
use LoggerTrait;
96+
9197
public const VERSION = '2.5.0';
9298
public const BASE_API = 'https://api.nexmo.com';
9399
public const BASE_REST = 'https://rest.nexmo.com';
@@ -106,15 +112,25 @@ class Client
106112
*/
107113
protected $client;
108114

115+
/**
116+
* @var bool
117+
*/
118+
protected $debug = false;
119+
109120
/**
110121
* @var ContainerInterface
111122
*/
112123
protected $factory;
113124

125+
/**
126+
* @var LoggerInterface
127+
*/
128+
protected $logger;
129+
114130
/**
115131
* @var array
116132
*/
117-
protected $options = ['show_deprecations' => false];
133+
protected $options = ['show_deprecations' => false, 'debug' => false];
118134

119135
/**
120136
* Create a new API client using the provided credentials.
@@ -176,6 +192,10 @@ public function __construct(CredentialsInterface $credentials, $options = [], ?C
176192
$this->apiUrl = $options['base_api_url'];
177193
}
178194

195+
if (isset($options['debug'])) {
196+
$this->debug = $options['debug'];
197+
}
198+
179199
$this->setFactory(
180200
new MapFactory(
181201
[
@@ -532,6 +552,32 @@ public function send(RequestInterface $request): ResponseInterface
532552
/** @noinspection PhpUnnecessaryLocalVariableInspection */
533553
$response = $this->client->sendRequest($request);
534554

555+
if ($this->debug) {
556+
$id = uniqid();
557+
$request->getBody()->rewind();
558+
$response->getBody()->rewind();
559+
$this->log(
560+
LogLevel::DEBUG,
561+
'Request ' . $id,
562+
[
563+
'url' => $request->getUri()->__toString(),
564+
'headers' => $request->getHeaders(),
565+
'body' => explode("\n", $request->getBody()->__toString())
566+
]
567+
);
568+
$this->log(
569+
LogLevel::DEBUG,
570+
'Response ' . $id,
571+
[
572+
'headers ' => $response->getHeaders(),
573+
'body' => explode("\n", $response->getBody()->__toString())
574+
]
575+
);
576+
577+
$request->getBody()->rewind();
578+
$response->getBody()->rewind();
579+
}
580+
535581
return $response;
536582
}
537583

@@ -637,4 +683,13 @@ protected function getVersion(): string
637683
{
638684
return Versions::getVersion('vonage/client-core');
639685
}
686+
687+
public function getLogger(): ?LoggerInterface
688+
{
689+
if (!$this->logger && $this->getFactory()->has(LoggerInterface::class)) {
690+
$this->setLogger($this->getFactory()->get(LoggerInterface::class));
691+
}
692+
693+
return $this->logger;
694+
}
640695
}

src/Client/Factory/MapFactory.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@
1212
namespace Vonage\Client\Factory;
1313

1414
use Psr\Container\ContainerInterface;
15+
use Psr\Log\LoggerInterface;
1516
use RuntimeException;
1617
use Vonage\Client;
18+
use Vonage\Logger\LoggerAwareInterface;
1719

1820
use function is_callable;
1921
use function sprintf;
@@ -120,11 +122,18 @@ public function make($key)
120122
$instance->setClient($this->client);
121123
}
122124

125+
if ($instance instanceof LoggerAwareInterface && $this->has(LoggerInterface::class)) {
126+
$instance->setLogger($this->get(LoggerInterface::class));
127+
}
128+
123129
return $instance;
124130
}
125131

126132
public function set($key, $value): void
127133
{
128134
$this->map[$key] = $value;
135+
if (!is_callable($value)) {
136+
$this->cache[$key] = $value;
137+
}
129138
}
130139
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Vonage\Logger;
4+
5+
use Psr\Log\LoggerInterface;
6+
7+
interface LoggerAwareInterface
8+
{
9+
public function getLogger(): ?LoggerInterface;
10+
11+
/**
12+
* @param string|int $level Level of message that we are logging
13+
* @param array<mixed> $context Additional information for context
14+
*/
15+
public function log($level, string $message, array $context = []): void;
16+
17+
/**
18+
* @return self
19+
*/
20+
public function setLogger(LoggerInterface $logger);
21+
}

src/Logger/LoggerTrait.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace Vonage\Logger;
4+
5+
use Psr\Log\LoggerInterface;
6+
7+
trait LoggerTrait
8+
{
9+
/**
10+
* @var LoggerInterface
11+
*/
12+
protected $logger;
13+
14+
public function getLogger(): ?LoggerInterface
15+
{
16+
return $this->logger;
17+
}
18+
19+
/**
20+
* @param string|int $level Level of message that we are logging
21+
* @param array<mixed> $context Additional information for context
22+
*/
23+
public function log($level, string $message, array $context = []): void
24+
{
25+
$logger = $this->getLogger();
26+
if ($logger) {
27+
$logger->log($level, $message, $context);
28+
}
29+
}
30+
31+
public function setLogger(LoggerInterface $logger)
32+
{
33+
$this->logger = $logger;
34+
}
35+
}

test/ClientTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Laminas\Diactoros\Response;
2020
use PHPUnit\Framework\TestCase;
2121
use Psr\Http\Client\ClientExceptionInterface;
22+
use Psr\Log\LoggerInterface;
2223
use RuntimeException;
2324
use Vonage\Client;
2425
use Vonage\Client\Credentials\Basic;
@@ -664,6 +665,21 @@ public function testGenericDeleteMethod($url, $params): void
664665
$this->assertRequestBodyIsEmpty($request);
665666
}
666667

668+
public function testLoggerIsNullWhenNotSet(): void
669+
{
670+
$client = new Client($this->basic_credentials, [], $this->http);
671+
$this->assertNull($client->getLogger());
672+
}
673+
674+
public function testCanGetLoggerWhenOneIsSet(): void
675+
{
676+
$client = new Client($this->basic_credentials, [], $this->http);
677+
$logger = $this->prophesize(LoggerInterface::class);
678+
$client->getFactory()->set(LoggerInterface::class, $logger->reveal());
679+
680+
$this->assertNotNull($client->getLogger());
681+
}
682+
667683
public function genericDeleteProvider(): array
668684
{
669685
$baseUrl = 'https://rest.nexmo.com';

test/Logger/LoggerTraitTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace VonageTest\Logger;
4+
5+
use Psr\Log\LoggerInterface;
6+
use Vonage\Logger\LoggerTrait;
7+
use PHPUnit\Framework\TestCase;
8+
9+
class LoggerTraitTest extends TestCase
10+
{
11+
public function testCanSetAndGetLogger()
12+
{
13+
/** @var LoggerTrait $trait */
14+
$trait = $this->getMockForTrait(LoggerTrait::class);
15+
$logger = $this->prophesize(LoggerInterface::class)->reveal();
16+
$trait->setLogger($logger);
17+
18+
$this->assertSame($logger, $trait->getLogger());
19+
}
20+
21+
public function testNoLoggerReturnsNull()
22+
{
23+
/** @var LoggerTrait $trait */
24+
$trait = $this->getMockForTrait(LoggerTrait::class);
25+
26+
$this->assertNull($trait->getLogger());
27+
}
28+
29+
public function testCanLogMessageWithLogger()
30+
{
31+
/** @var LoggerTrait $trait */
32+
$trait = $this->getMockForTrait(LoggerTrait::class);
33+
$logger = $this->prophesize(LoggerInterface::class)->reveal();
34+
$trait->setLogger($logger);
35+
36+
$this->assertNull($trait->log('debug', 'This is a message'));
37+
}
38+
39+
public function testLoggingAcceptsMessageWithLogger()
40+
{
41+
/** @var LoggerTrait $trait */
42+
$trait = $this->getMockForTrait(LoggerTrait::class);
43+
44+
$this->assertNull($trait->log('debug', 'This is a message'));
45+
}
46+
}

0 commit comments

Comments
 (0)