Julien Fastré 839fb4a211 Squashed commit of the following:
commit 977863c2dd56d5c835f2a710cad7f7d3ba42da68
Merge: 5c37b419d 3eb7ffed1
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sun Dec 12 14:45:21 2021 +0100

    Merge remote-tracking branch 'origin/master' into docgen/improve-normalizer

commit 5c37b419ddf0b32b9950c33042396bba1860da84
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sun Dec 12 14:37:05 2021 +0100

    fix normalization for user and type in null value

commit 4469d46cdb19051fedec86bbb84e2351e6fcb72e
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sun Dec 12 13:24:23 2021 +0100

    add civility to person

commit 6cf92fbbde8f4d9f2f4763ec4ee88216257040f7
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sun Dec 12 13:19:36 2021 +0100

    fix person normalization: add a isNull on not null person

commit ed6087ff8fd47b80ea5e9526756fe5d032d478e3
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sun Dec 12 13:08:46 2021 +0100

    fix stan and cs issues

commit 8429c334c33b3545835cbde034fccaa529c134a7
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sun Dec 12 13:08:36 2021 +0100

    fix id type

commit 39ae00d172a9f29320a97abb8518b2ea48d89d9b
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sun Dec 12 12:52:41 2021 +0100

    fix test and fix null or not-null value have same keys

commit 312fcc44c07affa7aa60f6c5fce58f9d1c564cc3
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sat Dec 11 03:27:30 2021 +0100

    improve normalization wip

commit f91a29635827005fc58617dc1c9d210091372be5
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sat Dec 11 01:15:32 2021 +0100

    improve normalization

commit 56060e5e6a2191ef441039fdc91a01fb4653a553
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Sat Dec 11 00:41:09 2021 +0100

    handle changelog with translatable string

commit 9004686a13f816309806cb1231bccd3159a652df
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Fri Dec 10 01:10:55 2021 +0100

    improve docgen wip

commit e266fa0e5dc8c0da446851e5f79f2dc7dab00f70
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Dec 9 21:50:56 2021 +0100

    show errors from relatorio driver

commit 75ba56fa096917da3b284e3d5a3bb8fe490df2a3
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Dec 9 21:14:12 2021 +0100

    add verification tool for admin

commit 12d6829b98d73eab232b345f2a611d58ae95874b
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Dec 9 14:17:42 2021 +0100

    fix type with phonenumber helper

commit 7b5e96771f4ca76fc131b9f2f6b5cac70996bec9
Merge: 8a9024de1 8a4748dc2
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Dec 9 14:11:14 2021 +0100

    Merge remote-tracking branch 'origin/master' into docgen/improve-normalizer

commit 8a9024de13ed702373fffd19c15ca638eb398520
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Dec 9 13:51:36 2021 +0100

    add docgen:normalization for relation

commit 24a404964b75d1a31108c6b90d82b1592305dccb
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Thu Dec 9 12:44:41 2021 +0100

    docgen normalization for relation

commit 5d24bd4d11db1627527497a8b83dd2bd59726fee
Merge: 70ab23214 455b225f4
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 21:08:30 2021 +0100

    Merge branch 'master' into docgen/improve-normalizer

commit 70ab23214906088b9287061ca3d721ba3ea624f3
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 13:58:49 2021 +0100

    improve docgen, trnslations, admin

commit 027c01fc58d679cfcc2e71ca16825a38533bdf79
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 12:23:24 2021 +0100

    fix css block

commit fdc5127c74ff0be3828c4bb0fc989010d28eca6f
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 11:57:16 2021 +0100

    fix some error in test (wip)

commit b8d48f04ae2e2c5dfec6a876c2a7d5733f8b8a58
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 11:47:50 2021 +0100

    fix tests (wip)

commit f1b1771d6baf8e585e01859b138f3ad5ad6f8c7a
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 11:35:00 2021 +0100

    fix tests (wip)

commit 62dabbe1e76dfead6b5ec94b354f8b4e1b9a7476
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 11:14:46 2021 +0100

    fix code style

commit 4101392190f3e0a3772553235a44268d8144c2e8
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 11:13:49 2021 +0100

    fix tests and type hinting

commit 3f1bed0b1cc4a4248f7041f81e633fedb762f5c9
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 11:05:41 2021 +0100

    fix tests (wip)

commit 79fbdcdee4a1b9e63cf5ab7d33d61b75e638837e
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 11:05:29 2021 +0100

    type hint User class

commit 3d8d79323e47c9dd27361a212dbf73f58600ed9a
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 10:56:30 2021 +0100

    remove error messages

commit 32178e22feced2256ae58a3c63d4d7d863c555e6
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 10:51:30 2021 +0100

    fix tests (wip)

commit 60a8c20896908d7e434871527910c83eefb8d849
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 10:29:54 2021 +0100

    update app

commit 9d8011da617c83a96451b33b0a0b43054a1f0c7c
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 10:06:35 2021 +0100

    fix loading origin

commit 789eeadb404c1950f4b33d201bd78bda915664df
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 09:52:51 2021 +0100

    fix loading fixtures for doc generator template

commit f206fdb08ccd85d7d2a460e51578b1acc6dc153c
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 09:52:35 2021 +0100

    fix code style

commit 9d5409d8d9e0df8c5f0e53e758dde60817732b9b
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 09:52:06 2021 +0100

    fix casting

commit e297d8253344a13f297793e7102ed1f51ff28235
Author: Julien Fastré <julien.fastre@champs-libres.coop>
Date:   Wed Dec 8 09:26:13 2021 +0100

    fixes on tests [WIP]
2021-12-12 14:46:37 +01:00

275 lines
7.9 KiB
PHP

<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Phonenumber;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\ServerException;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerInterface;
use function array_key_exists;
use function in_array;
use function json_decode;
use function preg_replace;
use function strlen;
/**
* Helper to some task linked to phonenumber.
*
* Currently, only Twilio is supported (https://www.twilio.com/lookup). A method
* allow to check if the helper is configured for validation. This should be used
* before doing some validation.
*/
class PhonenumberHelper
{
public const FORMAT_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
public const LOOKUP_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
protected CacheItemPoolInterface $cachePool;
/**
* TRUE if the client is properly configured.
*/
protected bool $isConfigured = false;
protected LoggerInterface $logger;
/**
* Twilio client.
*/
protected Client $twilioClient;
public function __construct(
CacheItemPoolInterface $cachePool,
$config,
LoggerInterface $logger
) {
$this->logger = $logger;
$this->cachePool = $cachePool;
if (array_key_exists('twilio_sid', $config)
&& !empty($config['twilio_sid'])
&& strlen($config['twilio_sid']) > 2
&& array_key_exists('twilio_secret', $config)
&& !empty($config['twilio_secret'])
&& strlen($config['twilio_secret']) > 2
) {
$this->twilioClient = new Client([
'auth' => [$config['twilio_sid'], $config['twilio_secret']],
]);
$this->isConfigured = true;
}
}
public function format($phonenumber)
{
return $this->performTwilioFormat($phonenumber);
}
/**
* Get type (mobile, landline, ...) for phone number.
*/
public function getType(string $phonenumber): string
{
return $this->performTwilioLookup($phonenumber) ?? 'unknown';
}
/**
* Return true if the validation is configured and available.
*/
public function isPhonenumberValidationConfigured(): bool
{
return $this->isConfigured;
}
/**
* Return true if the phonenumber is a landline or voip phone. Return always true
* if the validation is not configured.
*
* @param string $phonenumber
*/
public function isValidPhonenumberAny($phonenumber): bool
{
if (false === $this->isPhonenumberValidationConfigured()) {
return true;
}
$validation = $this->performTwilioLookup($phonenumber);
if (null === $validation) {
return false;
}
return in_array($validation, ['landline', 'voip', 'mobile'], true);
}
/**
* Return true if the phonenumber is a landline or voip phone. Return always true
* if the validation is not configured.
*
* @param string $phonenumber
*/
public function isValidPhonenumberLandOrVoip($phonenumber): bool
{
if (false === $this->isPhonenumberValidationConfigured()) {
return true;
}
$validation = $this->performTwilioLookup($phonenumber);
if (null === $validation) {
return true;
}
return in_array($validation, ['landline', 'voip'], true);
}
/**
* REturn true if the phoennumber is a mobile phone. Return always true
* if the validation is not configured.
*
* @param string $phonenumber
*/
public function isValidPhonenumberMobile($phonenumber): bool
{
if (false === $this->isPhonenumberValidationConfigured()) {
return true;
}
$validation = $this->performTwilioLookup($phonenumber);
if (null === $validation) {
return true;
}
return 'mobile' === $validation;
}
protected function performTwilioFormat($phonenumber)
{
if (false === $this->isPhonenumberValidationConfigured()) {
return $phonenumber;
}
// filter only number
$filtered = preg_replace('/[^0-9]/', '', $phonenumber);
$item = $this->cachePool->getItem('pnum_format_nat_' . $filtered);
if ($item->isHit()) {
return $item->get();
}
try {
$response = $this->twilioClient->get(sprintf(self::FORMAT_URI, '+' . $filtered), [
'http_errors' => true,
]);
} catch (ClientException $e) {
$response = $e->getResponse();
$this->logger->error('[phonenumber helper] Could not format number '
. 'due to client error', [
'message' => $response->getBody()->getContents(),
'status_code' => $response->getStatusCode(),
'phonenumber' => $phonenumber,
]);
return $phonenumber;
} catch (ServerException $e) {
$response = $e->getResponse();
$this->logger->error('[phonenumber helper] Could not format number '
. 'due to server error', [
'message' => $response->getBody()->getContents(),
'status_code' => $response->getStatusCode(),
'phonenumber' => $phonenumber,
]);
return null;
} catch (ConnectException $e) {
$this->logger->error('[phonenumber helper] Could not format number '
. 'due to connect error', [
'message' => $e->getMessage(),
'phonenumber' => $phonenumber,
]);
return null;
}
$format = json_decode($response->getBody()->getContents())->national_format;
$item
->set($format)
// expires after 3d
->expiresAfter(3600 * 24 * 3);
$this->cachePool->save($item);
return $format;
}
protected function performTwilioLookup($phonenumber)
{
if (false === $this->isPhonenumberValidationConfigured()) {
return null;
}
// filter only number
$filtered = preg_replace('/[^0-9]/', '', $phonenumber);
$item = $this->cachePool->getItem('pnum_' . $filtered);
if ($item->isHit()) {
//return $item->get();
}
try {
$response = $this->twilioClient->get(sprintf(self::LOOKUP_URI, '+' . $filtered), [
'http_errors' => true,
'query' => [
'Type' => 'carrier',
],
]);
} catch (ClientException $e) {
return 'invalid';
} catch (ServerException $e) {
$response = $e->getResponse();
$this->logger->error('[phonenumber helper] Could not perform validation '
. 'due to server error', [
'message' => $response->getBody()->getContents(),
'status_code' => $response->getStatusCode(),
'phonenumber' => $phonenumber,
]);
return null;
} catch (ConnectException $e) {
$this->logger->error('[phonenumber helper] Could not format number '
. 'due to connect error', [
'message' => $e->getMessage(),
'phonenumber' => $phonenumber,
]);
return null;
}
$validation = json_decode($response->getBody()->getContents())->carrier->type;
$item
->set($validation)
// expires after 12h
->expiresAfter(3600 * 12);
$this->cachePool->save($item);
return $validation;
}
}