├── examples
├── general.php
├── init.php
├── whois.php
├── hlr.php
├── sms.php
├── env.php
├── viber.php
├── voice.php
├── vk.php
├── whatsapp.php
├── social.php
├── token.php
├── call.php
└── account.php
├── src
├── Constants.php
├── IdeHelper
│ ├── Limits.php
│ ├── Webhook.php
│ ├── Blacklist.php
│ ├── Whitelist.php
│ └── Account.php
├── Api
│ ├── MethodInvoker.php
│ ├── Module.php
│ ├── ModuleLoader.php
│ ├── Schema.php
│ └── Modules.php
├── Utils
│ ├── Version.php
│ ├── Validator.php
│ ├── Url.php
│ └── Helpers.php
├── Http
│ ├── RestException.php
│ └── RestClient.php
└── GreenSMS.php
├── tests
├── TestCase.php
├── GeneralTest.php
├── Utility.php
├── EnvTest.php
├── TokenTest.php
├── VoiceTest.php
├── ViberTest.php
├── TelegramTest.php
├── RestClientTest.php
├── WhatsappTest.php
├── VkTest.php
├── HlrTest.php
├── PreSendHandlerTest.php
├── CallTest.php
├── SmsTest.php
└── AccountTest.php
├── .github
└── workflows
│ ├── main.yml
│ └── php.yml
├── composer.json
├── README.md
├── phpcs.xml
└── .gitignore
/examples/general.php:
--------------------------------------------------------------------------------
1 | status();
5 | print_r($response->status);
6 |
--------------------------------------------------------------------------------
/src/Constants.php:
--------------------------------------------------------------------------------
1 | 'test',
9 | 'pass' => 'test'
10 | ]);
11 |
--------------------------------------------------------------------------------
/src/IdeHelper/Limits.php:
--------------------------------------------------------------------------------
1 | whois->lookup([
5 | 'to' => '79260000000',
6 | ]);
7 |
8 | echo "Lookup Response";
9 | print_r($response);
10 | echo "\n\n";
11 |
--------------------------------------------------------------------------------
/src/IdeHelper/Account.php:
--------------------------------------------------------------------------------
1 | $method)) {
12 | $func = $this->$method;
13 | return call_user_func_array($func, $args);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/hlr.php:
--------------------------------------------------------------------------------
1 | hlr->send([
5 | 'to' => '79150000000',
6 | 'txt' => '1221'
7 | ]);
8 |
9 | echo "Hlr Request Id: " . $response->request_id;
10 | echo "\n\n";
11 |
12 | $response = $client->hlr->status([
13 | 'id' => '70d296f5-ac52-403d-a27b-24829c2faebc',
14 | 'to' => '79150000000'
15 | ]);
16 |
17 | echo "Hlr Status: \n";
18 | print_r($response);
19 |
--------------------------------------------------------------------------------
/examples/sms.php:
--------------------------------------------------------------------------------
1 | sms->send([
5 | 'to' => '79260000121',
6 | 'txt' => 'Here is your message for delivery'
7 | ]);
8 |
9 | echo "Sms Request Id: " . $response->request_id;
10 | echo "\n\n";
11 |
12 | $response = $client->sms->status([
13 | 'id' => 'dc2bac6d-f375-4e19-9a02-ef0148991635',
14 | ]);
15 |
16 | echo "Sms Status: \n";
17 | print_r($response);
18 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | account->balance();
13 | echo "Balance : " . $response->balance. "\n";
14 |
15 | // Unsetting the env variable after use
16 | putenv('GREENSMS_USER');
17 | putenv('GREENSMS_PASS');
18 |
--------------------------------------------------------------------------------
/examples/viber.php:
--------------------------------------------------------------------------------
1 | viber->send([
5 | 'to' => '79260000121',
6 | 'txt' => 'Here is your message for delivery'
7 | ]);
8 |
9 | echo "Viber Request Id: " . $response->request_id;
10 | echo "\n\n";
11 |
12 | $response = $client->viber->status([
13 | 'id' => '0b18fab4-0c5d-4a8b-8ee4-057a59596c7d',
14 | ]);
15 |
16 | echo "Viber Status: \n";
17 | print_r($response);
18 |
--------------------------------------------------------------------------------
/examples/voice.php:
--------------------------------------------------------------------------------
1 | voice->send([
5 | 'to' => '79260000121',
6 | 'txt' => '1221',
7 | 'language' => 'en',
8 | ]);
9 |
10 | echo "Voice Request Id: " . $response->request_id;
11 | echo "\n\n";
12 |
13 | $response = $client->voice->status([
14 | 'id' => '41f23094-deda-4cab-ac9c-3ab4f2fee9e6',
15 | ]);
16 |
17 | echo "Voice Status: \n";
18 | print_r($response);
19 |
--------------------------------------------------------------------------------
/examples/vk.php:
--------------------------------------------------------------------------------
1 | vk->send([
5 | 'to' => '79260000121',
6 | 'txt' => '1221',
7 | 'from' => 'GreenSMS',
8 | 'cascade' => 'sms'
9 | ]);
10 |
11 | echo "VK Request Id: " . $response->request_id;
12 | echo "\n\n";
13 |
14 | $response = $client->vk->status([
15 | 'id' => 'caf3efb1-8aca-4387-9ed0-e667d315c5c9',
16 | ]);
17 |
18 | echo "VK Status: \n";
19 | print_r($response);
20 |
--------------------------------------------------------------------------------
/examples/whatsapp.php:
--------------------------------------------------------------------------------
1 | whatsapp->send([
4 | 'to' => '79260000000',
5 | 'txt' => '1234 is your verification code.',
6 | 'from' => 'GREENSMS',
7 | 'tag' => 'test-sdk-node'
8 | ]);
9 | printf("WhatsApp Request ID: %s\n", $response->request_id);
10 |
11 | $response = $client->whatsapp->status(['id' => $response->request_id]);
12 | printf("WhatsApp Status: %s\n", var_export($response, 1));
13 |
--------------------------------------------------------------------------------
/examples/social.php:
--------------------------------------------------------------------------------
1 | social->send([
5 | 'to' => '79260000121',
6 | 'txt' => 'Here is your message for delivery',
7 | 'from' => 'Test'
8 | ]);
9 |
10 | echo "Social Request Id: " . $response->request_id;
11 | echo "\n\n";
12 |
13 | $response = $client->social->status([
14 | 'id' => 'caf3efb1-8aca-4387-9ed0-e667d315c5c9',
15 | ]);
16 |
17 | echo "Social Status: \n";
18 | print_r($response);
19 |
--------------------------------------------------------------------------------
/examples/token.php:
--------------------------------------------------------------------------------
1 | $token
11 | ]);
12 |
13 | $response = $tokenClient->account->balance();
14 | echo "Balance : " . $response->balance. "\n";
15 |
--------------------------------------------------------------------------------
/examples/call.php:
--------------------------------------------------------------------------------
1 | call->send([
5 | 'to' => '79260000111'
6 | ]);
7 |
8 | echo "Call Send Request Id: " . $response->request_id;
9 |
10 | $response = $client->call->receive([
11 | 'to' => '79260000111',
12 | 'toll_free' => 'true',
13 | 'tag' => 'aaeb96d6-cb0e-46f2-8d09-2cd5c9ea4211',
14 | ]);
15 |
16 | echo "Call Receive Request Id: " . $response->request_id;
17 |
18 |
19 | $response = $client->call->status([
20 | 'id' => '1fd4ac4d-6e4f-4e32-b6e4-8087d3466f55'
21 | ]);
22 |
23 | echo "Call Status: \n";
24 | print_r($response);
25 |
--------------------------------------------------------------------------------
/src/Utils/Version.php:
--------------------------------------------------------------------------------
1 | 'v1',
11 | 'v4.0.0' => 'v4.0.0',
12 | ];
13 |
14 | public static function getVersion($version)
15 | {
16 | if (Helpers::isNullOrEmpty($version)) {
17 | return self::VERSIONS['v1'];
18 | }
19 | $version = strtolower($version);
20 |
21 | if (!array_key_exists($version, self::VERSIONS)) {
22 | throw new Exception('Invalid Version');
23 | }
24 |
25 | return self::VERSIONS[$version];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Utils/Validator.php:
--------------------------------------------------------------------------------
1 | mapFieldsRules($schema);
14 |
15 | if ($validator->validate()) {
16 | return null;
17 | } else {
18 | $validationException = new RestException('Validation Error', 0);
19 | $validationException->setParams($validator->errors());
20 | throw $validationException;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tests/GeneralTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
13 | }
14 |
15 | public function testCanFetchLookup()
16 | {
17 | $response = $this->utility->getInstance()->whois->lookup(['to' => '79260000000']);
18 | $this->assertObjectHasAttribute('operator', $response);
19 | $this->assertObjectHasAttribute('region', $response);
20 | }
21 |
22 | public function testCanFetchStatus()
23 | {
24 | $response = $this->utility->getInstance()->status();
25 | $this->assertObjectHasAttribute('status', $response);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: CI
4 |
5 | # Controls when the action will run.
6 | on:
7 | # Allows you to run this workflow manually from the Actions tab
8 | workflow_dispatch:
9 |
10 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
11 | jobs:
12 | # This workflow contains a single job called "build"
13 | build:
14 | # The type of runner that the job will run on
15 | runs-on: ubuntu-latest
16 |
17 | # Steps represent a sequence of tasks that will be executed as part of the job
18 | steps:
19 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
20 | - uses: actions/checkout@v2
21 |
22 | - name: PHP_CodeSniffer Check with Annotations
23 | uses: chekalsky/phpcs-action@v1.2.0
24 |
25 |
--------------------------------------------------------------------------------
/src/Utils/Url.php:
--------------------------------------------------------------------------------
1 | 'test',
13 | 'pass' => 'test'
14 | ]);
15 |
16 | return $client;
17 | }
18 |
19 | public function getRandomPhone($min = 70000000111, $max = 70009999999)
20 | {
21 | $phoneNum = $this->getRandomNumber($min, $max);
22 | return $phoneNum;
23 | }
24 |
25 | public function getRandomNumber($min, $max)
26 | {
27 | return strval(rand($min, $max));
28 | }
29 |
30 | public function getInstanceWithEnv()
31 | {
32 | putenv('GREENSMS_USER=test');
33 | putenv('GREENSMS_PASS=test');
34 | $envClient = new GreenSMS();
35 |
36 | // Unsetting the env variable after use
37 | putenv('GREENSMS_USER');
38 | putenv('GREENSMS_PASS');
39 |
40 | return $envClient;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "greensms/greensms",
3 | "description": "GREENSMS API: SMS, WhatsApp, Viber, VK, Voice, Call, HLR",
4 | "type": "library",
5 | "keywords": [
6 | "greensms",
7 | "sms",
8 | "api",
9 | "rest",
10 | "viber"
11 | ],
12 | "homepage": "https://github.com/greensms-ru/greensms-php",
13 | "license": "MIT",
14 | "authors": [
15 | {
16 | "name": "Team GreenSMS",
17 | "email": "support@greensms.io"
18 | }
19 | ],
20 | "require": {
21 | "php": ">=7.3",
22 | "vlucas/valitron": "^1.4",
23 | "ext-curl": "*",
24 | "ext-json": "*"
25 | },
26 | "require-dev": {
27 | "phpunit/phpunit": "^9",
28 | "friendsofphp/php-cs-fixer": "^2.16"
29 | },
30 | "autoload": {
31 | "psr-4": {
32 | "GreenSMS\\": "src/"
33 | }
34 | },
35 | "autoload-dev": {
36 | "psr-4": {
37 | "GreenSMS\\Tests\\": "tests/"
38 | }
39 | },
40 | "scripts": {
41 | "test": "./vendor/bin/phpunit tests --bootstrap vendor/autoload.php"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tests/EnvTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
14 | }
15 |
16 | public function testCanFetchLookup()
17 | {
18 | $client = $this->utility->getInstanceWithEnv();
19 | $response = $client->account->balance();
20 | $this->assertObjectHasAttribute('balance', $response);
21 | }
22 |
23 | public function testBaseUrl()
24 | {
25 | $subDomain = (string) bin2hex(random_bytes(10));
26 | putenv('GREENSMS_BASE_URL=https://'. $subDomain .'.greensms.io:');
27 | $client = $this->utility->getInstance();
28 | $response = $client->account->balance();
29 | putenv('GREENSMS_BASE_URL');
30 |
31 | $this->assertInstanceOf(RestException::class, $response);
32 | $this->assertStringContainsString($subDomain, $response->getMessage());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Api/Module.php:
--------------------------------------------------------------------------------
1 | restClient = $restClient;
18 | $this->moduleSchema = $moduleSchema;
19 | $this->params = $options;
20 | $this->uri = $uri;
21 | $this->preSendHandler = $preSendHandler;
22 | }
23 |
24 | public function apiFunction($data = [])
25 | {
26 | if (!is_null($this->preSendHandler)) {
27 | call_user_func_array($this->preSendHandler, [&$data, $this->uri, $this->params['method']]);
28 | }
29 |
30 | if ($this->moduleSchema) {
31 | $validationResult = Validator::validate($this->moduleSchema, $data);
32 | }
33 |
34 | $requestParams = $this->params;
35 | $requestParams['params'] = $data;
36 | $response = $this->restClient->request($requestParams);
37 | return $response;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/account.php:
--------------------------------------------------------------------------------
1 | account->balance();
5 | echo "Balance : " . $response->balance. "\n";
6 |
7 | $response = $client->account->token(['token' => 100]);
8 | echo "Auth Token: " . $response->access_token;
9 |
10 | $response = $client->account->tariff();
11 | echo "Tariff Response: ";
12 | print_r($response);
13 |
14 | $response = $client->account->blacklist->add(['to' => '70000000000', 'module' => 'ALL', 'comment' => 'test']);
15 | echo "Blacklist Add Response: ";
16 | print_r($response);
17 | $response = $client->account->blacklist->get();
18 | echo "Blacklist Get Response: ";
19 | print_r($response);
20 | $response = $client->account->blacklist->delete(['to' => '70000000000']);
21 | echo "Blacklist Delete Response: ";
22 | print_r($response);
23 |
24 | $response = $client->account->limits->set(['type' => 'IP', 'module' => 'ALL', 'value' => '1.1.1.1,8.8.8.8', 'comment' => 'test']);
25 | echo "Limits Set Response: ";
26 | print_r($response);
27 | $response = $client->account->limits->get();
28 | echo "Limits Get Response: ";
29 | print_r($response);
30 | $response = $client->account->limits->delete(['type' => 'IP']);
31 | echo "Limits Delete Response: ";
32 | print_r($response);
--------------------------------------------------------------------------------
/tests/TokenTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
16 | }
17 |
18 | public function testCanFetchLookup()
19 | {
20 | $tokenResponse = $this->utility->getInstance()->account->token(['expire' => 10]);
21 |
22 | $client = new GreenSMS([
23 | 'token' => $tokenResponse->access_token
24 | ]);
25 | $response = $client->account->balance();
26 | $this->assertObjectHasAttribute('balance', $response);
27 | }
28 |
29 | public function testRaisesExceptionOnNoCredentials()
30 | {
31 | $tokenResponse = $this->utility->getInstance()->account->token([
32 | 'expire' => 5
33 | ]);
34 |
35 | $invalidTokenClient = new GreenSMS([
36 | 'token' => $tokenResponse->access_token
37 | ]);
38 |
39 | sleep(6);
40 |
41 | try {
42 | $invalidTokenClient->account->balance();
43 | $this->fail("Shouldn't allow operations on Expired Auth Token");
44 | } catch (Exception $e) {
45 | $this->assertEquals('Authorization declined', $e->getMessage());
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Http/RestException.php:
--------------------------------------------------------------------------------
1 | name = 'RestException';
16 | parent::__construct($errorMessage, $code, $previous);
17 | $errorType = $this->getErrorType($this->code);
18 | $this->errorType = $errorType;
19 | }
20 |
21 | public function setParams($params)
22 | {
23 | $this->params = $params;
24 | }
25 |
26 | public function getParams()
27 | {
28 | return $this->params;
29 | }
30 |
31 | public function __toString()
32 | {
33 | $params = json_encode($this->params);
34 | return __CLASS__ . ": [{$this->code}]: {$this->message}\n{$this->getTraceAsString()}\n\n{$params}";
35 | }
36 |
37 | public function getErrorType($code)
38 | {
39 | switch ($code) {
40 | case 0:
41 | return 'AUTH_DECLINED';
42 |
43 | case 1:
44 | return 'MISSING_INPUT_PARAM';
45 |
46 | case 2:
47 | return 'INVALID_INPUT_PARAM';
48 |
49 | case 404:
50 | return 'NOT_FOUND';
51 |
52 | default:
53 | return 'INTERNAL_SERVER_ERROR';
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/VoiceTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
16 | }
17 |
18 | public function testCanSendMessage()
19 | {
20 | $phoneNum = $this->utility->getRandomPhone();
21 | $params = [
22 | 'to' => $phoneNum,
23 | 'txt' => '1127',
24 | 'language' => 'en'
25 | ];
26 |
27 | $response = $this->utility->getInstance()->voice->send($params);
28 | $this->assertObjectHasAttribute('request_id', $response);
29 | return $response->request_id;
30 | }
31 |
32 | /**
33 | * @depends testCanSendMessage
34 | */
35 | public function testCanFetchStatus($requestId)
36 | {
37 | sleep(2);
38 | $response = $this->utility->getInstance()->voice->status(['id' => $requestId, 'extended' => true ]);
39 | $this->assertObjectHasAttribute('status', $response);
40 | }
41 |
42 | public function testRaisesValidationException()
43 | {
44 | try {
45 | $response = $this->utility->getInstance()->voice->send([]);
46 | $this->fail("Shouldn't send Voice without parameters");
47 | } catch (Exception $e) {
48 | $this->assertObjectHasAttribute('message', $e);
49 | $this->assertEquals('Validation Error', $e->getMessage());
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/ViberTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
16 | }
17 |
18 | public function testCanSendMessage()
19 | {
20 | $phoneNum = $this->utility->getRandomPhone();
21 | $params = [
22 | 'to' => $phoneNum,
23 | 'txt' => 'Text Message Hampshire',
24 | 'from' => 'PHPTest',
25 | 'cascade' => 'sms'
26 | ];
27 |
28 | $response = $this->utility->getInstance()->viber->send($params);
29 | $this->assertObjectHasAttribute('request_id', $response);
30 | return $response->request_id;
31 | }
32 |
33 | /**
34 | * @depends testCanSendMessage
35 | */
36 | public function testCanFetchStatus($requestId)
37 | {
38 | sleep(2);
39 | $response = $this->utility->getInstance()->viber->status(['id' => $requestId, 'extended' => true ]);
40 | $this->assertObjectHasAttribute('status', $response);
41 | }
42 |
43 | public function testRaisesValidationException()
44 | {
45 | try {
46 | $response = $this->utility->getInstance()->viber->send([]);
47 | $this->fail("Shouldn't send Viber without parameters");
48 | } catch (Exception $e) {
49 | $this->assertObjectHasAttribute('message', $e);
50 | $this->assertEquals('Validation Error', $e->getMessage());
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/TelegramTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
14 | }
15 |
16 | public function testCanSendMessage()
17 | {
18 | $phoneNum = $this->utility->getRandomPhone();
19 | $params = [
20 | 'to' => $phoneNum,
21 | 'txt' => '1127',
22 | ];
23 |
24 | $response = $this->utility->getInstance()->telegram->send($params);
25 | $this->assertObjectHasAttribute('request_id', $response);
26 | return $response->request_id;
27 | }
28 |
29 | /**
30 | * @depends testCanSendMessage
31 | */
32 | public function testCanFetchStatus($requestId)
33 | {
34 | try {
35 | $this->utility->getInstance()->telegram->status(['id' => $requestId, 'extended' => true ]);
36 | } catch (RestException $e) {
37 | $this->assertObjectHasAttribute('message', $e);
38 | $this->assertEquals('Message not found', $e->getMessage());
39 | }
40 | }
41 |
42 | public function testRaisesValidationException()
43 | {
44 | try {
45 | $this->utility->getInstance()->telegram->send([]);
46 | $this->fail("Shouldn't send Telegram without parameters");
47 | } catch (Exception $e) {
48 | $this->assertObjectHasAttribute('message', $e);
49 | $this->assertEquals('Validation Error', $e->getMessage());
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/RestClientTest.php:
--------------------------------------------------------------------------------
1 | request([
14 | 'url'=>'https://gethttpstatus.com/400',
15 | 'method' => 'get',
16 | ]);
17 |
18 | $this->assertInstanceOf(RestException::class, $response);
19 | $this->assertEquals('Bad Request', $response->getMessage());
20 | $this->assertEquals(400, $response->getCode());
21 | }
22 |
23 | public function testFail500()
24 | {
25 | $client = new RestClient([]);
26 | $response = $client->request([
27 | 'url'=>'https://gethttpstatus.com/500',
28 | 'method' => 'get',
29 | ]);
30 |
31 | $this->assertInstanceOf(RestException::class, $response);
32 | $this->assertEquals('Internal Server Error', $response->getMessage());
33 | $this->assertEquals(500, $response->getCode());
34 | }
35 |
36 | public function testNativeRequestTimeout ()
37 | {
38 | $client = new RestClient([]);
39 | $response = $client->request([
40 | 'url'=>'https://some.some',
41 | 'method' => 'get',
42 | 'CURLOPT_TIMEOUT_MS' => 2,
43 | ]);
44 |
45 | $this->assertInstanceOf(RestException::class, $response);
46 | $this->assertMatchesRegularExpression(
47 | '/Resolving timed out after (\d){1,3} milliseconds/',
48 | $response->getMessage()
49 | );
50 | $this->assertEquals(0, $response->getCode());
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/WhatsappTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
14 | }
15 |
16 | public function testCanSendMessage()
17 | {
18 | $phoneNum = $this->utility->getRandomPhone();
19 | $params = [
20 | 'to' => $phoneNum,
21 | 'txt' => '1234 is your verification code.',
22 | 'from' => 'GREENSMS',
23 | 'tag' => 'test-sdk-node'
24 | ];
25 |
26 | $response = $this->utility->getInstance()->whatsapp->send($params);
27 | $this->assertObjectHasAttribute('request_id', $response);
28 | return $response->request_id;
29 | }
30 |
31 | /**
32 | * @depends testCanSendMessage
33 | */
34 | /*
35 | public function testCanFetchStatus($requestId)
36 | {
37 | sleep(2);
38 | $response = $this->utility->getInstance()->whatsapp->status(['id' => $requestId, 'extended' => true ]);
39 | $this->assertObjectHasAttribute('status', $response);
40 | }
41 | */
42 | public function testCanGetChannelList()
43 | {
44 | $response = $this->utility->getInstance()->whatsapp->channel();
45 | $this->assertObjectHasAttribute('channels', $response);
46 | $this->assertIsArray($response->channels);
47 | }
48 |
49 | public function testRaisesValidationException()
50 | {
51 | try {
52 | $response = $this->utility->getInstance()->whatsapp->send([]);
53 | $this->fail("Shouldn't send Whatsapp without parameters");
54 | } catch (Exception $e) {
55 | $this->assertObjectHasAttribute('message', $e);
56 | $this->assertEquals('Validation Error', $e->getMessage());
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/.github/workflows/php.yml:
--------------------------------------------------------------------------------
1 | name: PHP Composer
2 |
3 | on:
4 | push:
5 | branches: [main, build-matrix]
6 | pull_request:
7 | branches: [main]
8 | release:
9 | workflow_dispatch:
10 |
11 | jobs:
12 | build:
13 | runs-on: ${{ matrix.operating-system }}
14 | strategy:
15 | matrix:
16 | #@wontfix 'windows-latest', 'macos-latest'
17 | operating-system: ["ubuntu-latest"]
18 | #@wontfix v.5.3 conflicts with php-cs-fixer
19 | #@todo fix v.8 conflicts with phpunit
20 | php-versions: ["7.3", "7.4", "8.2", "8.3"]
21 | #phpunit-versions: ['latest']
22 |
23 | steps:
24 | - uses: actions/checkout@v2
25 |
26 | - name: Setup PHP
27 | uses: shivammathur/setup-php@v2
28 | with:
29 | php-version: ${{ matrix.php-versions }}
30 | extensions: mbstring, intl, curl
31 | ini-values: post_max_size=256M, max_execution_time=180
32 | #coverage: xdebug
33 | #tools: php-cs-fixer, phpunit:${{ matrix.phpunit-versions }}
34 |
35 | - name: Validate composer.json and composer.lock
36 | run: composer validate
37 |
38 | # - name: Cache Composer packages
39 | # id: composer-cache
40 | # uses: actions/cache@v2
41 | # with:
42 | # path: vendor
43 | # key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
44 | # restore-keys: |
45 | # ${{ runner.os }}-php-
46 |
47 | - name: Install dependencies
48 | if: steps.composer-cache.outputs.cache-hit != 'true'
49 | run: composer install --prefer-dist --no-progress --no-suggest
50 |
51 | # Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
52 | # Docs: https://getcomposer.org/doc/articles/scripts.md
53 | - name: Composer Autoload
54 | run: composer dump-autoload
55 |
56 | - name: Run test suite
57 | if: ${{ matrix.php-versions == '8.2' }}
58 | run: composer run-script test
59 |
--------------------------------------------------------------------------------
/tests/VkTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
16 | }
17 |
18 | public function testCanSendMessage()
19 | {
20 | $phoneNum = $this->utility->getRandomPhone();
21 | $params = [
22 | 'to' => $phoneNum,
23 | 'txt' => '1127',
24 | 'from' => 'GreenSMS',
25 | 'cascade' => 'voice,viber,sms',
26 | ];
27 |
28 | $response = $this->utility->getInstance()->vk->send($params);
29 | $this->assertObjectHasAttribute('request_id', $response);
30 | return $response->request_id;
31 | }
32 |
33 | /**
34 | * @depends testCanSendMessage
35 | */
36 | public function testCanFetchStatus($requestId)
37 | {
38 | sleep(2);
39 | $response = $this->utility->getInstance()->vk->status(['id' => $requestId, 'extended' => true ]);
40 | $this->assertObjectHasAttribute('status', $response);
41 | }
42 |
43 | public function testRaisesValidationException()
44 | {
45 | try {
46 | $response = $this->utility->getInstance()->vk->send([]);
47 | $this->fail("Shouldn't send Vk without parameters");
48 | } catch (Exception $e) {
49 | $this->assertObjectHasAttribute('message', $e);
50 | $this->assertEquals('Validation Error', $e->getMessage());
51 | }
52 | }
53 |
54 | public function testCommaSeparatedInStrictValidationFailure()
55 | {
56 | $this->expectException(RestException::class);
57 |
58 | $this->utility->getInstance()->vk->send([
59 | 'to' => $this->utility->getRandomPhone(),
60 | 'txt' => 'Test Message',
61 | 'from' => 'GreenSMS',
62 | 'cascade' => 'voice,viber,invalid',
63 | ]);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/tests/HlrTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
16 | }
17 |
18 | public function testCanSendMessage()
19 | {
20 | $phoneNum = $this->utility->getRandomPhone(79150000000, 79150999999);
21 | $params = [
22 | 'to' => $phoneNum,
23 | ];
24 |
25 | $response = $this->utility->getInstance()->hlr->send($params);
26 | $this->assertObjectHasAttribute('request_id', $response);
27 | return [
28 | 'id' => $response->request_id,
29 | 'to' => $phoneNum,
30 | ];
31 | }
32 |
33 | /**
34 | * @depends testCanSendMessage
35 | */
36 | public function testCanFetchStatus($params)
37 | {
38 | sleep(2);
39 | $response = $this->utility->getInstance()->hlr->status($params);
40 | $this->assertObjectHasAttribute('status', $response);
41 | }
42 |
43 | public function testRaisesValidationException()
44 | {
45 | try {
46 | $this->utility->getInstance()->hlr->send([]);
47 | $this->fail("Shouldn't send Hlr without parameters");
48 | } catch (Exception $e) {
49 | $this->assertObjectHasAttribute('message', $e);
50 | $this->assertEquals('Validation Error', $e->getMessage());
51 | }
52 | }
53 |
54 | public function testHlrSend()
55 | {
56 | $to = $this->utility->getRandomPhone(79150000000, 79150999999);
57 | $response = $this->utility->getInstance()->hlr->send([
58 | 'to' => $to,
59 | ]);
60 | $this->assertObjectHasAttribute('request_id', $response);
61 |
62 | return [$to, $response->request_id];
63 | }
64 |
65 | /**
66 | * @depends testHlrSend
67 | */
68 | public function testHlrStatus($params)
69 | {
70 | $response = $this->utility->getInstance()->hlr->status([
71 | 'to' => $params[0],
72 | 'id' => $params[1],
73 | ]);
74 |
75 | $this->assertObjectHasAttribute('status', $response);
76 |
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/tests/PreSendHandlerTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
14 | }
15 |
16 | public function testReplacement()
17 | {
18 | $client = new GreenSMS([
19 | 'user' => 'test',
20 | 'pass' => 'test',
21 | 'preSendHandler' => function(&$data, $uri, $method) {
22 | $this->assertEquals('someVal', $data['someKey']);
23 | $this->assertEquals('sms/send', $uri);
24 | $this->assertEqualsIgnoringCase('post', $method);
25 | $data['to'] = $this->utility->getRandomPhone();
26 | $data['txt'] = 'txt';
27 | }
28 | ]);
29 |
30 | $response = $client->sms->send(['someKey' => 'someVal', 'to'=> 111]);
31 |
32 | $this->assertObjectHasProperty('request_id', $response);
33 | }
34 |
35 | public function testEmptyData()
36 | {
37 | $client = new GreenSMS([
38 | 'user' => 'test',
39 | 'pass' => 'test',
40 | 'preSendHandler' => function($data, $uri, $method) {
41 | $this->assertEquals('account/balance', $uri);
42 | $this->assertEmpty($data);
43 | $this->assertEqualsIgnoringCase('get', $method);
44 | }
45 | ]);
46 |
47 | $response = $client->account->balance();
48 |
49 | $this->assertObjectHasProperty('balance', $response);
50 | $this->assertEquals(4, $this->getCount());
51 | }
52 |
53 | public function testInvocable()
54 | {
55 | $invocable = new class extends TestCase{
56 | public function __invoke($data, $uri, $method) {
57 | $this->assertEquals('account/balance', $uri);
58 | $this->assertEmpty($data);
59 | $this->assertEqualsIgnoringCase('get', $method);
60 | }
61 | };
62 |
63 | (new GreenSMS([
64 | 'user' => 'test',
65 | 'pass' => 'test',
66 | 'preSendHandler' => $invocable,
67 | ]))->account->balance();
68 | }
69 |
70 | public function testV4()
71 | {
72 | $client = new GreenSMS([
73 | 'user' => 'test',
74 | 'pass' => 'test',
75 | 'preSendHandler' => function($data, $uri, $method) {
76 | $this->assertEmpty($data);
77 | $this->assertEquals('account/webhook', $uri);
78 | $this->assertEqualsIgnoringCase('get', $method);
79 | }
80 | ]);
81 |
82 | $response = $client->account->webhook->get();
83 |
84 | $this->assertObjectHasProperty('webhook', $response);
85 | $this->assertEquals(4, $this->getCount());
86 | }
87 |
88 | public function testNotCallable()
89 | {
90 | $this->expectException(BadFunctionCallException::class);
91 |
92 | new GreenSMS([
93 | 'user' => 'test',
94 | 'pass' => 'test',
95 | 'preSendHandler' => '',
96 | ]);
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/tests/CallTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
15 | }
16 |
17 | public function testCanReceive()
18 | {
19 | $phoneNum = $this->utility->getRandomPhone();
20 | $params = [
21 | 'to' => $phoneNum,
22 | 'toll_free' => 'true'
23 | ];
24 |
25 | $response = $this->utility->getInstance()->call->receive($params);
26 | $this->assertObjectHasAttribute('request_id', $response);
27 | $this->assertObjectHasAttribute('number', $response);
28 | }
29 |
30 | public function testCanSendMessage()
31 | {
32 | $phoneNum = $this->utility->getRandomPhone();
33 | $params = [
34 | 'to' => $phoneNum,
35 | ];
36 |
37 | $response = $this->utility->getInstance()->call->send($params);
38 | $this->assertObjectHasAttribute('request_id', $response);
39 | $this->assertObjectHasAttribute('code', $response);
40 | $requestId = $response->request_id;
41 | return $requestId;
42 | }
43 |
44 | /**
45 | * @depends testCanSendMessage
46 | * */
47 | public function testCanFetchStatus($requestId)
48 | {
49 | sleep(2);
50 | $response = $this->utility->getInstance()->call->status(['id' => $requestId, 'extended' => true ]);
51 | $this->assertObjectHasAttribute('status', $response);
52 | }
53 |
54 | public function testRaisesValidationException()
55 | {
56 | try {
57 | $response = $this->utility->getInstance()->call->send([]);
58 | $this->fail("Shouldn't send Call without parameters");
59 | } catch (Exception $e) {
60 | $this->assertObjectHasAttribute('message', $e);
61 | $this->assertEquals('Validation Error', $e->getMessage());
62 | }
63 | }
64 |
65 | /** @dataProvider receiveParamsDataProvider*/
66 | public function testReceiveParams($params)
67 | {
68 | $this->expectException(RestException::class);
69 | $this->utility->getInstance()->call->receive($params);
70 | }
71 |
72 | public static function receiveParamsDataProvider(): iterable
73 | {
74 | return [
75 | 'minTo' => [[
76 | 'to' => str_repeat('1', 10),
77 | ]],
78 | 'maxTo' => [[
79 | 'to' => str_repeat('1', 15),
80 | ]],
81 | 'maxTag' => [[
82 | 'to' => '01234567890',
83 | 'tag' => str_repeat('1', 37),
84 | ]],
85 | ];
86 | }
87 |
88 | /** @dataProvider sendParamsDataProvider*/
89 | public function testSendParams($params)
90 | {
91 | $this->expectException(RestException::class);
92 | $this->utility->getInstance()->call->send($params);
93 | }
94 |
95 | public static function sendParamsDataProvider(): iterable
96 | {
97 | return [
98 | 'minTo' => [[
99 | 'to' => str_repeat('1', 10),
100 | ]],
101 | 'maxTo' => [[
102 | 'to' => str_repeat('1', 15),
103 | ]],
104 | 'maxTag' => [[
105 | 'to' => '01234567890',
106 | 'tag' => str_repeat('s', 37),
107 | ]],
108 | ];
109 | }
110 |
111 | /** @dataProvider statusParamsDataProvider*/
112 | public function testStatusParams($params)
113 | {
114 | $this->expectException(RestException::class);
115 | $this->utility->getInstance()->call->status($params);
116 | }
117 |
118 | public static function statusParamsDataProvider(): iterable
119 | {
120 | return [
121 | 'minId' => [[
122 | 'id' => str_repeat('s', 35),
123 | ]],
124 | 'maxId' => [[
125 | 'id' => str_repeat('s', 37),
126 | ]],
127 | ];
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # greensms-php
2 |
3 | 
4 | 
5 | 
6 | [](https://github.com/greensms-ru/greensms-php/actions/workflows/php.yml)
7 |
8 | ## Documentation
9 |
10 | The documentation for the GREENSMS API can be found [here][apidocs].
11 |
12 | ## Installation
13 |
14 | ```bash
15 | composer require greensms/greensms
16 | ```
17 |
18 | ## Sample Usage
19 |
20 | Check out these [code examples](examples) to get up and running quickly.
21 |
22 | ```php
23 |
24 | use GreenSMS\GreenSMS;
25 |
26 | # Register at my.greeensms.ru first
27 | $client = new GreenSMS([
28 | 'user' => 'test',
29 | 'pass' => 'test'
30 | ]);
31 |
32 | $response = $client->sms->send([
33 | 'to' => '79260000121',
34 | 'txt' => 'Here is your message for delivery'
35 | ]);
36 |
37 |
38 | echo "Sms Request Id: " . $response->request_id;
39 |
40 | ```
41 |
42 | ### Environment Variables
43 |
44 | `greensms-php` supports credential storage in environment variables. If no credentials are provided following env vars will be used: `GREENSMS_USER`/`GREENSMS_PASS` OR `GREENSMS_TOKEN`.
45 |
46 | ### Token Auth
47 |
48 | ```php
49 |
50 | use GreenSMS\GreenSMS;
51 |
52 | $tokenClient = new GreenSMS([
53 | 'token' => 'yourtoken'
54 | ]);
55 |
56 | $response = $tokenClient->account->balance();
57 | echo "Balance : " . $response->balance. "\n";
58 |
59 |
60 | ```
61 |
62 | ## Compatibility
63 |
64 | `greensms-php` is compatible with PHP 7.3+ onwards until the latest PHP Version
65 |
66 | ## Methods
67 |
68 | - You can either use username/password combination or auth token to create an object with constructor
69 | - Each API Function is available as `MODULE.FUNCTION()`
70 | - Parameters for each API can be referred from [here][apidocs]
71 | - Response keys by default are available in `snake_case`. If you want to use `camelCase`, then pass `'camelCaseResponse'= > true`, in the constructor
72 |
73 | ## Handling Exceptions
74 |
75 | - Exceptions for all APIs are thrown with RestException class. It extends the default PHP Exception class.
76 | - Each error, will have a message and code similar to PHP Exceptions.
77 | - In case of _Validation Error_, additional params are available to show field-wise rule failures. Can be accessed by `$e->getParams()` method on the error object
78 |
79 | ## Getting help
80 |
81 | If you need help installing or using the library, please contact us: [support@greensms.io](mailto:support@greensms.io).
82 |
83 | If you've instead found a bug in the library or would like new features added, go ahead and open issues or pull requests against this repo!
84 |
85 | ## Contributing
86 |
87 | Bug fixes, docs, and library improvements are always welcome. Please refer to our [Contributing Guide](CONTRIBUTING.md) for detailed information on how you can contribute.
88 | If you're not familiar with the GitHub pull request/contribution process, [this is a nice tutorial](https://gun.io/blog/how-to-github-fork-branch-and-pull-request/).
89 |
90 | ### Getting Started
91 |
92 | If you want to familiarize yourself with the project, you can start by [forking the repository](https://help.github.com/articles/fork-a-repo/) and [cloning it in your local development environment](https://help.github.com/articles/cloning-a-repository/). The project requires [Node.js](https://nodejs.org) to be installed on your machine.
93 |
94 | After cloning the repository, install the dependencies by running the following command in the directory of your cloned repository:
95 |
96 | ```bash
97 | composer require
98 | ```
99 |
100 | GreenSMS has all the unit tests defined under **tests** folder with `*Test.php` extension. It uses PHPUnit, which is added as a dev dependency. You can run all the tests using the following command.
101 |
102 | ```bash
103 | ./vendor/bin/phpunit tests
104 | ```
105 |
106 | [apidocs]: https://api.greensms.io/
107 |
--------------------------------------------------------------------------------
/tests/SmsTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
16 | }
17 |
18 | public function testCanSendMessage()
19 | {
20 | $phoneNum = $this->utility->getRandomPhone();
21 | $params = [
22 | 'to' => $phoneNum,
23 | 'txt' => 'Text Message Hampshire',
24 | 'from' => 'PHPTest',
25 | 'tag' => 'PHPTest',
26 | 'hidden' => 'Hampshire'
27 | ];
28 |
29 | $response = $this->utility->getInstance()->sms->send($params);
30 | $this->assertObjectHasAttribute('request_id', $response);
31 | return $response->request_id;
32 | }
33 |
34 | /**
35 | * @depends testCanSendMessage
36 | */
37 | public function testCanFetchStatus($requestId)
38 | {
39 | sleep(2);
40 | $response = $this->utility->getInstance()->sms->status(['id' => $requestId, 'extended' => true ]);
41 | $this->assertObjectHasAttribute('status', $response);
42 | }
43 |
44 | public function testRaisesValidationException()
45 | {
46 | try {
47 | $response = $this->utility->getInstance()->sms->send([]);
48 | $this->fail("Shouldn't send SMS without parameters");
49 | } catch (Exception $e) {
50 | $this->assertObjectHasAttribute('message', $e);
51 | $this->assertEquals('Validation Error', $e->getMessage());
52 | }
53 | }
54 |
55 | public function testMinimalTo()
56 | {
57 | $this->expectException(\GreenSMS\Http\RestException::class);
58 | $this->utility->getInstance()->sms->send([
59 | 'to' => '0123456789',
60 | 'txt' => 'message',
61 | ]);
62 | }
63 |
64 | public function testMaximalTo()
65 | {
66 | $this->expectException(\GreenSMS\Http\RestException::class);
67 | $this->utility->getInstance()->sms->send([
68 | 'to' => '012345678912345',
69 | 'txt' => 'message',
70 | ]);
71 | }
72 |
73 | public function testMinimalTxt()
74 | {
75 | $this->expectException(\GreenSMS\Http\RestException::class);
76 | $this->utility->getInstance()->sms->send([
77 | 'to' => '01234567891',
78 | 'txt' => str_repeat('s',0),
79 | ]);
80 | }
81 |
82 | public function testMaximalTxt()
83 | {
84 | $this->expectException(\GreenSMS\Http\RestException::class);
85 | $this->utility->getInstance()->sms->send([
86 | 'to' => '01234567891',
87 | 'txt' => str_repeat('s',919),
88 | ]);
89 | }
90 |
91 | public function testMaximalFrom()
92 | {
93 | $this->expectException(\GreenSMS\Http\RestException::class);
94 | $this->utility->getInstance()->sms->send([
95 | 'to' => '01234567891',
96 | 'txt' => 's',
97 | 'from' => str_repeat('s',12),
98 | ]);
99 | }
100 |
101 | public function testMaximalTag()
102 | {
103 | $this->expectException(\GreenSMS\Http\RestException::class);
104 | $this->utility->getInstance()->sms->send([
105 | 'to' => '01234567891',
106 | 'txt' => 's',
107 | 'tag' => str_repeat('s',37),
108 | ]);
109 | }
110 |
111 | public function testMaximalHidden()
112 | {
113 | $this->expectException(\GreenSMS\Http\RestException::class);
114 | $this->utility->getInstance()->sms->send([
115 | 'to' => '01234567891',
116 | 'txt' => 's',
117 | 'hidden' => str_repeat('s',919),
118 | ]);
119 | }
120 |
121 | public function testMinimalId()
122 | {
123 | $this->expectException(\GreenSMS\Http\RestException::class);
124 | $this->utility->getInstance()->sms->status([
125 | 'id' => str_repeat('i', 35),
126 | ]);
127 | }
128 |
129 | public function testMaximalId()
130 | {
131 | $this->expectException(\GreenSMS\Http\RestException::class);
132 | $this->utility->getInstance()->sms->status([
133 | 'id' => str_repeat('i', 37),
134 | ]);
135 | }
136 |
137 | public function testSendAndFetchWithDifferentArgSeparator()
138 | {
139 | ini_set('arg_separator.output', ';');
140 | $this->testCanFetchStatus($this->testCanSendMessage());
141 | ini_set('arg_separator.output', '&');
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/Http/RestClient.php:
--------------------------------------------------------------------------------
1 | token = $options['token'];
27 | }
28 |
29 | if (array_key_exists('defaultData', $options)) {
30 | $this->defaultData = $options['defaultData'];
31 | }
32 |
33 | if (array_key_exists('defaultParams', $options)) {
34 | $this->defaultParams = $options['defaultParams'];
35 | }
36 |
37 | if (array_key_exists('useCamelCase', $options)) {
38 | $this->useCamelCase = $options['useCamelCase'];
39 | }
40 | }
41 |
42 | public function request($options)
43 | {
44 | if (is_null($options)) {
45 | $options = [];
46 | }
47 |
48 | if (!array_key_exists('method', $options)) {
49 | throw new Exception('Http Method is required!');
50 | }
51 |
52 | if (!array_key_exists('url', $options)) {
53 | throw new Exception('URL is required!');
54 | }
55 |
56 | $this->ch = curl_init();
57 |
58 | $headers = [
59 | 'Content-Type' => 'application/json',
60 | 'User-Agent' => Constants::SDK_NAME. " ".Constants::SDK_VERSION,
61 | ];
62 |
63 | if ($this->token) {
64 | $headers['Authorization'] = 'Bearer '. $this->token;
65 | }
66 |
67 | if (array_key_exists('headers', $options)) {
68 | $headers = array_merge($headers, $options['headers']);
69 | }
70 |
71 | if (!empty($headers)) {
72 | $headers_arr = [];
73 | foreach ($headers as $k => $v) {
74 | if (is_array($v)) {
75 | foreach ($v as $val) {
76 | $headers_arr[] = "$k: $val";
77 | }
78 | } else {
79 | $headers_arr[] = "$k: $v";
80 | }
81 | }
82 | curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headers_arr);
83 | }
84 |
85 |
86 | $params = [];
87 | if (!empty($this->defaultParams)) {
88 | $params = $this->defaultParams;
89 | }
90 | if (array_key_exists('params', $options)) {
91 | $params = array_merge($params, $options['params']);
92 | }
93 |
94 | $url = $options['url'];
95 |
96 | if (strtolower($options['method']) === 'post') {
97 | curl_setopt($this->ch, CURLOPT_POST, 1);
98 | curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($params));
99 | } elseif (strtolower($options['method']) === 'delete') {
100 | curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, "DELETE");
101 | curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($params));
102 | } else {
103 | curl_setopt($this->ch, CURLOPT_POST, 0);
104 | $params_str = http_build_query($params, '', '&');
105 | if (strlen($params_str) > 0) {
106 | $url .= (parse_url($url, PHP_URL_QUERY) ? '&' : '?') . $params_str;
107 | }
108 | }
109 |
110 | curl_setopt($this->ch, CURLOPT_URL, $url);
111 | curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
112 | curl_setopt($this->ch, CURLOPT_HEADER, 0);
113 | curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, 0);
114 | curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, 0);
115 | if (isset($options['CURLOPT_TIMEOUT_MS'])) {
116 | curl_setopt($this->ch, CURLOPT_TIMEOUT_MS, $options['CURLOPT_TIMEOUT_MS']);
117 | }
118 |
119 | $apiResult = curl_exec($this->ch);
120 | $response = json_decode($apiResult, true);
121 |
122 | if (curl_errno($this->ch)) {
123 | $error = curl_error($this->ch);
124 | $response = new RestException($error, curl_getinfo($this->ch)['http_code']);
125 | } elseif (!is_array($response) && curl_getinfo($this->ch)['http_code'] >= 400 ) {
126 | $response = new RestException($apiResult, curl_getinfo($this->ch)['http_code']);
127 | }
128 |
129 | curl_close($this->ch);
130 |
131 | if (is_array($response) && array_key_exists('error', $response)) {
132 | throw new RestException($response['error'], $response['code']);
133 | }
134 |
135 | if ($this->useCamelCase) {
136 | $response = Helpers::camelizeKeys($response);
137 | }
138 |
139 | return (object)$response;
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/GreenSMS.php:
--------------------------------------------------------------------------------
1 | token = $options['token'];
37 | }
38 |
39 |
40 | if (array_key_exists('user', $options)) {
41 | $this->user = $options['user'];
42 | }
43 |
44 | if (array_key_exists('pass', $options)) {
45 | $this->pass = $options['pass'];
46 | }
47 |
48 | if (array_key_exists('useTokenForRequests', $options)) {
49 | $this->useTokenForRequests = $options['useTokenForRequests'];
50 | }
51 |
52 | if (array_key_exists('camelCaseResponse', $options)) {
53 | $this->camelCaseResponse = $options['camelCaseResponse'];
54 | }
55 |
56 | if (array_key_exists('version', $options)) {
57 | $this->version = $options['version'];
58 | }
59 |
60 | if (array_key_exists('baseUrl', $options)) {
61 | $this->baseUrl = $options['baseUrl'];
62 | }
63 |
64 | if (!$this->token) {
65 | $this->token = getenv('GREENSMS_TOKEN');
66 | }
67 |
68 |
69 | if (!$this->token && !$this->user) {
70 | $this->user = getenv('GREENSMS_USER');
71 | }
72 |
73 | if (!$this->token && !$this->pass) {
74 | $this->pass = getenv('GREENSMS_PASS');
75 | }
76 |
77 | if (!$this->token && (!$this->user || !$this->pass)) {
78 | throw new Exception('Either User/Pass or Auth Token is required!');
79 | }
80 |
81 | $sharedOptions = [
82 | 'useTokenForRequests' => $this->useTokenForRequests,
83 | 'baseUrl' => $this->baseUrl ?? Url::baseUrl(),
84 | 'restClient' => $this->getHttpClient([
85 | 'useCamelCase' => $this->camelCaseResponse
86 | ]),
87 | 'version' => Version::getVersion($this->version),
88 | 'preSendHandler' => $this->getPreSendHandler($options),
89 | ];
90 |
91 | $this->addModules($sharedOptions);
92 | }
93 |
94 | public function addModules($sharedOptions)
95 | {
96 | $moduleLoader = new ModuleLoader();
97 | $modules = $moduleLoader->registerModules($sharedOptions);
98 | foreach ($modules as $moduleName => $moduleTree) {
99 | $this->{$moduleName} = $moduleTree;
100 | }
101 |
102 | ValitronValidator::addRule('ipsCommaSeparator', function($field, $value, array $params, array $fields) {
103 | if ($fields['type'] == 'IP') {
104 | foreach (explode(',', $value) as $val) {
105 | if (!filter_var($val, FILTER_VALIDATE_IP)) {
106 | return false;
107 | }
108 | }
109 | }
110 |
111 | return true;
112 | }, 'IP incorrect');
113 |
114 | Validator::addRule('commaSeparatedInStrict', function ($field, $value, array $params) {
115 | if (!is_string($value)) {
116 | return false;
117 | }
118 |
119 | $allowedValues = $params[0] ?? [];
120 | $items = explode(',', $value);
121 |
122 | foreach ($items as $item) {
123 | if (!in_array($item, $allowedValues, true)) {
124 | return false;
125 | }
126 | }
127 |
128 | return true;
129 | }, 'must contain values from the list with comma: %s');
130 | }
131 |
132 | public function getHttpClient($args)
133 | {
134 | $defaultParams = [];
135 |
136 | if (!$this->token && $this->user) {
137 | $defaultParams['user'] = $this->user;
138 | $defaultParams['pass'] = $this->pass;
139 | }
140 |
141 | $httpParams = [
142 | 'defaultParams' => $defaultParams,
143 | 'defaultData' => [],
144 | 'token' => $this->token
145 | ];
146 |
147 | $httpParams = array_merge($httpParams, $args);
148 |
149 | $restClient = new RestClient($httpParams);
150 | return $restClient;
151 | }
152 |
153 | private function getPreSendHandler($options): ?callable
154 | {
155 | if (array_key_exists('preSendHandler', $options)) {
156 | if (is_callable($options['preSendHandler'])) {
157 | return $options['preSendHandler'];
158 | } else {
159 | throw new BadFunctionCallException('Key `preSendHandler` must be callable');
160 | }
161 | }
162 |
163 | return null;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/tests/AccountTest.php:
--------------------------------------------------------------------------------
1 | utility = new Utility();
15 | }
16 |
17 | public function testCanFetchBalance(): void
18 | {
19 | $response = $this->utility->getInstance()->account->balance();
20 | $this->assertObjectHasAttribute('balance', $response);
21 | }
22 |
23 | public function testCanFetchToken(): void
24 | {
25 | $response = $this->utility->getInstance()->account->token(['expire' => 10]);
26 | $this->assertObjectHasAttribute('access_token', $response);
27 | }
28 |
29 | public function testCanFetchTariff()
30 | {
31 | $response = $this->utility->getInstance()->account->tariff();
32 | $this->assertObjectHasAttribute('tariff', $response);
33 | $this->assertGreaterThan(0, count($response->tariff));
34 | }
35 |
36 | public function testRaisesExceptionOnNoCredentials()
37 | {
38 | try {
39 | $client = new GreenSMS();
40 | $this->fail("Shouldn't create client without Credentials");
41 | } catch (Exception $e) {
42 | $this->assertObjectHasAttribute('message', $e);
43 | }
44 | }
45 |
46 | public function testRaisesExceptionOnInvalidCredentials()
47 | {
48 | $client = new GreenSMS([
49 | 'user' => 'randomusername',
50 | 'pass' => 'pass'
51 | ]);
52 | $this->expectException(RestException::class);
53 | $this->expectExceptionMessage('Authorization declined');
54 | $this->expectExceptionCode(0);
55 |
56 | $client->account->balance();
57 | }
58 |
59 | public function testRaisesExceptionOnInsufficientFunds()
60 | {
61 | $client = new GreenSMS([
62 | 'user' => 'test_block_user',
63 | 'pass' => '183456'
64 | ]);
65 |
66 | $this->expectException(RestException::class);
67 | $this->expectExceptionMessage('Insufficient funds');
68 | $this->expectExceptionCode(-1);
69 |
70 | $client->sms->send([
71 | 'to' => $this->utility->getRandomPhone(),
72 | 'txt' => 'Test134'
73 | ]);
74 | }
75 |
76 | public function testBlackList()
77 | {
78 | $response = $this->utility->getInstance()->account->blacklist->delete(['to' => '70000000000']);
79 | $this->assertEquals((object)['success' => 1], $response);
80 | $response = $this->utility->getInstance()->account->blacklist->add(['to' => '70000000000', 'module' => 'ALL', 'comment' => 'test']);
81 | $this->assertEquals((object)['success' => 1], $response);
82 | $this->utility->getInstance()->account->blacklist->get();
83 | }
84 |
85 | public function testLimits()
86 | {
87 | $response = $this->utility->getInstance()->account->limits->delete(['type' => 'IP']);
88 | $this->assertEquals((object)['success' => 1], $response);
89 | $this->utility->getInstance()->account->limits->get();
90 | $response = $this->utility->getInstance()->account->limits->set(['type' => 'IP', 'module' => 'ALL', 'value' => '1.1.1.1,8.8.8.8', 'comment' => 'test']);
91 | $this->assertEquals((object)['success' => 1], $response);
92 | $response = $this->utility->getInstance()->account->limits->set(['type' => 'REQ_PER_DAY', 'module' => 'ALL', 'value' => '6000', 'comment' => 'test']);
93 | $this->assertEquals((object)['success' => 1], $response);
94 | }
95 |
96 | public function testWebhook()
97 | {
98 | $response = $this->utility->getInstance()->account->webhook->delete();
99 | $this->assertEquals((object)['success' => 1], $response);
100 | $this->utility->getInstance()->account->webhook->get();
101 | $response = $this->utility->getInstance()->account->webhook->set(['url' => 'http://localhost', 'token' => 'test']);
102 | $this->assertEquals((object)['success' => 1], $response);
103 | }
104 |
105 | public function testWhitelist()
106 | {
107 | $response = $this->utility->getInstance()->account->whitelist->delete(['to' => '70000000000']);
108 | $this->assertEquals((object)['success' => 1], $response);
109 | $this->utility->getInstance()->account->whitelist->get();
110 | $response = $this->utility->getInstance()->account->whitelist->add(['to' => '70000000000', 'module' => 'ALL', 'comment' => 'test']);
111 | $this->assertEquals((object)['success' => 1], $response);
112 | }
113 |
114 | public function testWhitelistUseRules()
115 | {
116 | $this->expectException(RestException::class);
117 |
118 | $this->utility->getInstance()->account->whitelist->add([
119 | 'to' => '70000000000',
120 | 'module' => 'ALL',
121 | 'comment' => str_repeat('s', 51),
122 | ]);
123 | }
124 |
125 | public function testPasswordResetCode()
126 | {
127 | try {
128 | $response = $this->utility->getInstance()->account->password->resetCode();
129 | $this->assertObjectHasAttribute('success', $response);
130 | $this->assertTrue($response->success);
131 | } catch (RestException $t) {
132 | $this->assertEquals(3, $t->getCode());
133 | }
134 | }
135 |
136 | public function testPasswordResetValidation()
137 | {
138 | $this->expectException(RestException::class);
139 | $this->expectExceptionMessage('Validation Error');
140 |
141 | $this->utility->getInstance()->account->password->reset();
142 | }
143 |
144 | public function testPasswordReset()
145 | {
146 | $this->expectException(RestException::class);
147 | $this->expectExceptionMessage('Invalid or expired code');
148 | $this->expectExceptionCode(2);
149 |
150 | $this->utility->getInstance()->account->password->reset(['code' => '123456']);
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/phpcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | The coding standard for PHP_CodeSniffer itself.
4 |
5 | examples
6 | src
7 | tests
8 |
9 | */src/Standards/*/Tests/*\.(inc|css|js)$
10 | */tests/Core/*/*\.(inc|css|js)$
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | error
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | 0
87 |
88 |
89 | 0
90 |
91 |
92 | 0
93 |
94 |
95 | 0
96 |
97 |
98 |
99 |
100 |
101 | 0
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | error
135 |
136 |
137 |
138 |
139 | error
140 |
141 |
142 |
143 |
144 | tests/bootstrap\.php
145 |
146 |
147 |
148 |
149 | tests/Core/Tokenizer/StableCommentWhitespaceWinTest\.php
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/src/Api/ModuleLoader.php:
--------------------------------------------------------------------------------
1 | moduleMap = new MethodInvoker;
19 |
20 | $currentVersion = $sharedOptions['version'];
21 | $modules = Modules::getModules();
22 |
23 | foreach ($modules as $moduleName => $moduleInfo) {
24 | if (!property_exists($this->moduleMap, $moduleName)) {
25 | $this->moduleMap->{$moduleName} = new MethodInvoker;
26 | }
27 |
28 | $moduleVersions = $moduleInfo['versions'];
29 | $processStaticOnly = Helpers::keyExistsAndTrue('loadStatic', $filters) && Helpers::keyExistsAndTrue('static', $moduleInfo);
30 |
31 | if ($processStaticOnly) {
32 | continue;
33 | }
34 |
35 | $isStaticModule = Helpers::keyExistsAndTrue('static', $moduleInfo);
36 |
37 |
38 |
39 | foreach ($moduleVersions as $version => $versionFunctions) {
40 |
41 | if ($version === 'v1') {
42 | $this->addModuleMapV1($versionFunctions, $moduleInfo, $version, $isStaticModule, $moduleName, $sharedOptions, $currentVersion);
43 | } elseif ($version === 'v4.0.0') {
44 | $this->addModuleMapV4_0_0($versionFunctions, $moduleInfo, $version, $isStaticModule, $moduleName, $sharedOptions, $currentVersion);
45 | }
46 |
47 | }
48 | }
49 |
50 | return $this->moduleMap;
51 | }
52 |
53 | private function doesSchemaExists($moduleInfo, $version, $functionName)
54 | {
55 | if (!array_key_exists('schema', $moduleInfo)) {
56 | return false;
57 | } elseif (!array_key_exists($version, $moduleInfo['schema'])) {
58 | return false;
59 | } elseif (!array_key_exists($functionName, $moduleInfo['schema'][$version])) {
60 | return false;
61 | }
62 | return true;
63 | }
64 |
65 | private function getFunctionInstance($options)
66 | {
67 | $restClient = $options['sharedOptions']['restClient'];
68 | $moduleSchema = $options['moduleSchema'];
69 |
70 | $requestArgs = [
71 | 'url' => $options['url'],
72 | 'method' => $options['definition']['method']
73 | ];
74 |
75 | $module = new Module($restClient, $moduleSchema, $requestArgs, $options['uri'], $options['sharedOptions']['preSendHandler']);
76 | $functionInstance = array($module, 'apiFunction');
77 | return $functionInstance;
78 | }
79 |
80 | private function addModuleMapV1($versionFunctions, $moduleInfo, $version, $isStaticModule, $moduleName, $sharedOptions, $currentVersion)
81 | {
82 | if (!property_exists($this->moduleMap->{$moduleName}, $version)) {
83 | $this->moduleMap->{$moduleName}->{$version} = new MethodInvoker;
84 | }
85 |
86 | foreach ($versionFunctions as $functionName => $functionDefinition) {
87 | $moduleSchema = null;
88 | $schemaExists = self::doesSchemaExists($moduleInfo, $version, $functionName);
89 | if ($schemaExists) {
90 | $moduleSchema = $moduleInfo['schema'][$version][$functionName];
91 | }
92 |
93 | $urlParts = [];
94 | if (!$isStaticModule) {
95 | array_push($urlParts, $moduleName);
96 | }
97 |
98 | array_push($urlParts, $functionName);
99 | $apiUrl = Url::buildUrl($sharedOptions['baseUrl'], $urlParts);
100 |
101 | $functionOptions = [
102 | 'url' => $apiUrl,
103 | 'definition' => $functionDefinition,
104 | 'sharedOptions' => $sharedOptions,
105 | 'moduleSchema' => $moduleSchema,
106 | 'uri' => implode('/', array_map(function($v) {
107 | return rtrim($v, '/');
108 | }, $urlParts)),
109 | ];
110 |
111 | $this->moduleMap->{$moduleName}->{$version}->{$functionName} = $this->getFunctionInstance($functionOptions);
112 |
113 | if ($version === $currentVersion) {
114 | $this->moduleMap->{$moduleName}->{$functionName} = $this->moduleMap->{$moduleName}->{$version}->{$functionName};
115 | }
116 |
117 | if ($isStaticModule) {
118 | $this->moduleMap->{$functionName} = $this->moduleMap->{$moduleName}->{$version}->{$functionName};
119 | unset($this->moduleMap->{$moduleName});
120 | }
121 | }
122 | }
123 |
124 | private function addModuleMapV4_0_0($functions, $moduleInfo, $version, $isStaticModule, $moduleName, $sharedOptions, $currentVersion)
125 | {
126 |
127 | foreach ($functions as $prop => $definitions) {
128 | if (!property_exists($this->moduleMap->{$moduleName}, $prop)) {
129 | $this->moduleMap->{$moduleName}->{$prop} = new MethodInvoker;
130 | }
131 | if (!property_exists($this->moduleMap->{$moduleName}->{$prop}, $version)) {
132 | $this->moduleMap->{$moduleName}->{$prop}->{$version} = new MethodInvoker;
133 | }
134 |
135 | foreach ($definitions as $functionName => $functionDefinition) {
136 | $urlParts = [];
137 | if (!$isStaticModule) {
138 | array_push($urlParts, $moduleName);
139 | }
140 |
141 | array_push($urlParts, $prop);
142 |
143 | if (isset($functionDefinition['segments'])) {
144 | array_push($urlParts, ...$functionDefinition['segments']);
145 | }
146 |
147 | $apiUrl = Url::buildUrl($sharedOptions['baseUrl'], $urlParts);
148 |
149 | $functionOptions = [
150 | 'url' => $apiUrl,
151 | 'definition' => $functionDefinition,
152 | 'sharedOptions' => $sharedOptions,
153 | 'moduleSchema' => $this->getSchema($moduleInfo['schema'] ?? [], $version, $prop, $functionName),
154 | 'uri' => implode('/', array_map(function($v) {
155 | return rtrim($v, '/');
156 | }, $urlParts)),
157 | ];
158 |
159 |
160 | $this->moduleMap->{$moduleName}->{$prop}->{$version}->{$functionName} = $this->getFunctionInstance($functionOptions);
161 | $this->moduleMap->{$moduleName}->{$prop}->{$functionName} = $this->moduleMap->{$moduleName}->{$prop}->{$version}->{$functionName};
162 |
163 | }
164 | }
165 | }
166 |
167 | private function getSchema($schema, $version, $prop, $functionName): ?array
168 | {
169 | if (!array_key_exists($version, $schema)) {
170 | return null;
171 | } elseif (!array_key_exists($prop, $schema[$version])) {
172 | return null;
173 | } elseif (!array_key_exists($functionName, $schema[$version][$prop])) {
174 | return null;
175 | }
176 |
177 | return $schema[$version][$prop][$functionName];
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/src/Api/Schema.php:
--------------------------------------------------------------------------------
1 | [
38 | 'send' => [
39 | 'to' => self::getToSchema()
40 | ],
41 | 'status' => [
42 | 'id' => self::getIdSchema(),
43 | 'extended' => [['subset', ['true', 'false']]]
44 | ]
45 | ]
46 | ];
47 |
48 | return $commonSchema;
49 | }
50 |
51 | public static function getSchema()
52 | {
53 | $commonSchema = self::getCommonSchema();
54 | $toSchema = self::getToSchema();
55 |
56 | $schema = [
57 | 'account' => [
58 | 'v1' => [
59 | 'token' => [
60 | 'expire' => ['integer', ['min', 0]],
61 | ]
62 | ],
63 | 'v4.0.0' => [
64 | 'blacklist' => [
65 | 'add' => [
66 | 'to' => $toSchema,
67 | 'module' => self::getModuleSchema(),
68 | 'comment' => [['lengthMax', 50]],
69 | ],
70 | 'delete' => [
71 | 'to' => $toSchema,
72 | ],
73 | ],
74 | 'password' => [
75 | 'reset' => [
76 | 'code' => ['required'],
77 | ],
78 | ],
79 | 'limits' => [
80 | 'set' => [
81 | 'type' => self::getTypeSchema(),
82 | 'value' => ['required',['ipsCommaSeparator']],
83 | 'module' => self::getModuleSchemaWithAccount(),
84 | 'comment' => [['lengthMax', 50]],
85 | ],
86 | 'delete' => [
87 | 'type' => self::getTypeSchema(),
88 | 'module' => self::getModuleSchemaWithAccount(),
89 | ],
90 | ],
91 | 'webhook' => [
92 | 'set' => [
93 | 'url' => ['required',['url'], ['lengthMax', 1024]],
94 | 'token' => [['lengthMax', 256]],
95 | ],
96 | ],
97 | 'whitelist' => [
98 | 'add' => [
99 | 'to' => $toSchema,
100 | 'module' => self::getModuleSchema(),
101 | 'comment' => [['lengthMax', 50]],
102 | ],
103 | 'delete' => [
104 | 'to' => $toSchema,
105 | ],
106 | ],
107 | ],
108 | ],
109 | 'call' => array_merge_recursive($commonSchema, [
110 | 'v1' => [
111 | 'send' => [
112 | 'voice' => [['subset', ['true', 'false']]],
113 | 'tag' => [['lengthMax', 36]],
114 | 'lang' => [['subset', ['ru', 'en']]]
115 | ],
116 | 'receive' => [
117 | 'to' => self::getToSchema(),
118 | 'toll_free' => [['subset', ['true', 'false']]],
119 | 'tag' => [['lengthMax', 36]],
120 | ]
121 | ]
122 | ]) ,
123 | 'hlr' => [
124 | 'v1' => [
125 | 'send' => [
126 | 'to' => self::getToSchema(),
127 | ],
128 | 'status' => [
129 | 'to' => self::getToSchema(),
130 | 'id' => self::getIdSchema(),
131 | ],
132 | ],
133 | ],
134 | 'general' => [],
135 | 'whois' => [
136 | 'v1' => [
137 | 'lookup' => [
138 | 'to' => $toSchema
139 | ]
140 | ],
141 | ],
142 | 'voice' => array_merge_recursive($commonSchema, [
143 | 'v1' => [
144 | 'send' => [
145 | 'txt' => ['required', ['lengthMin', 1], ['lengthMax', 5], ['regex', '/^[0-9]+$/']],
146 | 'tag' => [['lengthMax', 36]],
147 | 'lang' => [['subset', ['ru', 'en']]]
148 | ]
149 | ]
150 | ]),
151 | 'vk' => array_merge_recursive($commonSchema, [
152 | 'v1' => [
153 | 'send' => [
154 | 'txt' => ['required', ['lengthMin', 1], ['lengthMax', 2048]],
155 | 'from' => ['required',['lengthMax', 11], ['lengthMin', 1]],
156 | 'tag' => [['lengthMax', 36]],
157 | 'cascade' => [['commaSeparatedInStrict', ['viber', 'sms', 'voice']]],
158 | ],
159 | ],
160 | ]),
161 | 'sms' => array_merge_recursive($commonSchema, [
162 | 'v1' => [
163 | 'send' => [
164 | 'txt' => ['required', ['lengthMin', 1], ['lengthMax', 918]],
165 | 'from' => [['lengthMax', 11]],
166 | 'tag' => [['lengthMax', 36]],
167 | 'hidden' => [['lengthMax', 918]]
168 | ]
169 | ]
170 | ]),
171 | 'viber' => array_merge_recursive($commonSchema, [
172 | 'v1' => [
173 | 'send' => [
174 | 'txt' => ['required', ['lengthMin', 1]],
175 | 'from' => [['lengthMin', 1],['lengthMax', 11]],
176 | 'cascade' => [['subset', ['sms', 'voice']]],
177 | ]
178 | ]
179 | ]),
180 | 'whatsapp' => array_merge_recursive($commonSchema, [
181 | 'v1' => [
182 | 'send' => [
183 | 'from' => ['required'],
184 | 'txt' => ['required', ['lengthMin', 1],['lengthMax', 1000]],
185 | 'file' => [['lengthMax', 256]],
186 | 'tag' => [['lengthMax', 36]],
187 | ],
188 | 'channel' => [],
189 | ]
190 | ]),
191 | 'telegram' => array_merge_recursive($commonSchema, [
192 | 'v1' => [
193 | 'send' => [
194 | 'txt' => ['required', ['lengthMin', 4], ['lengthMax', 8], ['regex', '/^[0-9]+$/']],
195 | 'tag' => [['lengthMax', 36]],
196 | ]
197 | ]
198 | ]),
199 | ];
200 |
201 | return $schema;
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/src/Api/Modules.php:
--------------------------------------------------------------------------------
1 | [
13 | 'schema' => $schema['account'],
14 | 'versions' => [
15 | 'v1' => [
16 | 'balance' => [
17 | 'args' => null,
18 | 'method' => 'GET'
19 | ],
20 | 'token' => [
21 | 'args' => ['params'],
22 | 'method' => 'POST'
23 | ],
24 | 'tariff' => [
25 | 'args' => null,
26 | 'method' => 'GET'
27 | ],
28 | ],
29 | 'v4.0.0' => [
30 | 'blacklist' => [
31 | 'get' => [
32 | 'args' => null,
33 | 'method' => 'GET',
34 | ],
35 | 'add' => [
36 | 'args' => ['params'],
37 | 'method' => 'POST',
38 | ],
39 | 'delete' => [
40 | 'args' => ['params'],
41 | 'method' => 'DELETE',
42 | ],
43 | ],
44 | 'password' => [
45 | 'resetCode' => [
46 | 'args' => null,
47 | 'method' => 'POST',
48 | 'segments' => ['reset-code']
49 | ],
50 | 'reset' => [
51 | 'args' => ['params'],
52 | 'method' => 'POST',
53 | 'segments' => ['reset']
54 | ],
55 | ],
56 | 'limits' => [
57 | 'get' => [
58 | 'args' => null,
59 | 'method' => 'GET',
60 | ],
61 | 'set' => [
62 | 'args' => null,
63 | 'method' => 'POST',
64 | ],
65 | 'delete' => [
66 | 'args' => null,
67 | 'method' => 'DELETE',
68 | ],
69 | ],
70 | 'webhook' => [
71 | 'get' => [
72 | 'args' => null,
73 | 'method' => 'GET',
74 | ],
75 | 'set' => [
76 | 'args' => null,
77 | 'method' => 'POST',
78 | ],
79 | 'delete' => [
80 | 'args' => null,
81 | 'method' => 'DELETE',
82 | ],
83 | ],
84 | 'whitelist' => [
85 | 'get' => [
86 | 'args' => null,
87 | 'method' => 'GET',
88 | ],
89 | 'add' => [
90 | 'args' => null,
91 | 'method' => 'POST',
92 | ],
93 | 'delete' => [
94 | 'args' => null,
95 | 'method' => 'DELETE',
96 | ],
97 | ],
98 | ],
99 | ]
100 | ],
101 | 'call' => [
102 | 'schema' => $schema['call'],
103 | 'versions' => [
104 | 'v1' => [
105 | 'send'=> [
106 | 'args'=> ['params'],
107 | 'method'=> 'POST',
108 | ],
109 | 'receive'=> [
110 | 'args'=> ['params'],
111 | 'method'=> 'POST',
112 | ],
113 | 'status'=> [
114 | 'args'=> ['params'],
115 | 'method'=> 'GET',
116 | ],
117 | ]
118 | ]
119 | ],
120 | 'whois' => [
121 | 'schema' => $schema['whois'],
122 | 'versions' => [
123 | 'v1' => [
124 | 'lookup' => [
125 | 'args' => ['params'],
126 | 'method' => 'GET'
127 | ]
128 | ]
129 | ]
130 | ],
131 | 'general' => [
132 | 'schema' => $schema['general'],
133 | 'static' => true,
134 | 'versions' => [
135 | 'v1' => [
136 | 'status' => [
137 | 'args' => null,
138 | 'method' => 'GET',
139 | ],
140 | ],
141 | ],
142 | ],
143 | 'voice' => [
144 | 'schema' => $schema['voice'],
145 | 'versions' => [
146 | 'v1' => [
147 | 'send' => [
148 | 'args' => ['params'],
149 | 'method' => 'POST',
150 | ],
151 | 'status' => [
152 | 'args' => ['params'],
153 | 'method' => 'GET',
154 | ],
155 | ]
156 | ]
157 | ],
158 | 'vk' => [
159 | 'schema' => $schema['vk'],
160 | 'versions' => [
161 | 'v1' => [
162 | 'send' => [
163 | 'args' => ['params'],
164 | 'method' => 'POST',
165 | ],
166 | 'status' => [
167 | 'args' => ['params'],
168 | 'method' => 'GET',
169 | ],
170 | ]
171 | ]
172 | ],
173 | 'whatsapp' => [
174 | 'schema' => $schema['whatsapp'],
175 | 'versions' => [
176 | 'v1' => [
177 | 'send' => [
178 | 'args' => ['params'],
179 | 'method' => 'POST',
180 | ],
181 | 'status' => [
182 | 'args' => ['params'],
183 | 'method' => 'GET',
184 | ],
185 | 'channel' => [
186 | 'args' => null,
187 | 'method' => 'GET',
188 | ],
189 | ]
190 | ]
191 | ],
192 | 'hlr' => [
193 | 'schema' => $schema['hlr'],
194 | 'versions' => [
195 | 'v1' => [
196 | 'send' => [
197 | 'args' => ['params'],
198 | 'method' => 'POST',
199 | ],
200 | 'status' => [
201 | 'args' => ['params'],
202 | 'method' => 'GET',
203 | ],
204 | ]
205 | ]
206 | ],
207 | 'sms' => [
208 | 'schema' => $schema['sms'],
209 | 'versions' => [
210 | 'v1' => [
211 | 'send' => [
212 | 'args' => ['params'],
213 | 'method' => 'POST',
214 | ],
215 | 'status' => [
216 | 'args' => ['params'],
217 | 'method' => 'GET',
218 | ],
219 | ]
220 | ]
221 | ],
222 | 'viber' => [
223 | 'schema' => $schema['viber'],
224 | 'versions' => [
225 | 'v1' => [
226 | 'send' => [
227 | 'args' => ['params'],
228 | 'method' => 'POST',
229 | ],
230 | 'status' => [
231 | 'args' => ['params'],
232 | 'method' => 'GET',
233 | ],
234 | ]
235 | ]
236 | ],
237 | 'telegram' => [
238 | 'schema' => $schema['telegram'],
239 | 'versions' => [
240 | 'v1' => [
241 | 'send' => [
242 | 'args' => ['params'],
243 | 'method' => 'POST',
244 | ],
245 | 'status' => [
246 | 'args' => ['params'],
247 | 'method' => 'GET',
248 | ],
249 | ]
250 | ]
251 | ],
252 | ];
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ##### Windows
2 | # Windows thumbnail cache files
3 | Thumbs.db
4 | Thumbs.db:encryptable
5 | ehthumbs.db
6 | ehthumbs_vista.db
7 |
8 | # Dump file
9 | *.stackdump
10 |
11 | # Folder config file
12 | [Dd]esktop.ini
13 |
14 | # Recycle Bin used on file shares
15 | $RECYCLE.BIN/
16 |
17 | # Windows Installer files
18 | *.cab
19 | *.msi
20 | *.msix
21 | *.msm
22 | *.msp
23 |
24 | # Windows shortcuts
25 | *.lnk
26 |
27 | ##### Linux
28 | *~
29 | package.xml
30 | *.tgz
31 |
32 |
33 | # temporary files which can be created if a process still has a handle open of a deleted file
34 | .fuse_hidden*
35 |
36 | # KDE directory preferences
37 | .directory
38 |
39 | # Linux trash folder which might appear on any partition or disk
40 | .Trash-*
41 |
42 | # .nfs files are created when an open file is removed but is still being accessed
43 | .nfs*
44 |
45 | ##### MacOS
46 | # General
47 | .DS_Store
48 | .AppleDouble
49 | .LSOverride
50 |
51 | # Icon must end with two \r
52 | Icon
53 |
54 | # Thumbnails
55 | ._*
56 |
57 | # Files that might appear in the root of a volume
58 | .DocumentRevisions-V100
59 | .fseventsd
60 | .Spotlight-V100
61 | .TemporaryItems
62 | .Trashes
63 | .VolumeIcon.icns
64 | .com.apple.timemachine.donotpresent
65 |
66 | # Directories potentially created on remote AFP share
67 | .AppleDB
68 | .AppleDesktop
69 | Network Trash Folder
70 | Temporary Items
71 | .apdisk
72 |
73 | ##### Backup
74 | *.bak
75 | *.gho
76 | *.ori
77 | *.orig
78 | *.tmp
79 |
80 | ##### GPG
81 | secring.*
82 |
83 | ##### Dropbox
84 | # Dropbox settings and caches
85 | .dropbox
86 | .dropbox.attr
87 | .dropbox.cache
88 |
89 | ##### SynopsysVCS
90 | # Waveform formats
91 | *.vcd
92 | *.vpd
93 | *.evcd
94 | *.fsdb
95 |
96 | # Default name of the simulation executable. A different name can be
97 | # specified with this switch (the associated daidir database name is
98 | # also taken from here): -o /
99 | simv
100 |
101 | # Generated for Verilog and VHDL top configs
102 | simv.daidir/
103 | simv.db.dir/
104 |
105 | # Infrastructure necessary to co-simulate SystemC models with
106 | # Verilog/VHDL models. An alternate directory may be specified with this
107 | # switch: -Mdir=
108 | csrc/
109 |
110 | # Log file - the following switch allows to specify the file that will be
111 | # used to write all messages from simulation: -l
112 | *.log
113 | /logs/*
114 |
115 |
116 | # Coverage results (generated with urg) and database location. The
117 | # following switch can also be used: urg -dir .vdb
118 | simv.vdb/
119 | urgReport/
120 | coverage
121 |
122 | # DVE and UCLI related files.
123 | DVEfiles/
124 | ucli.key
125 |
126 | # When the design is elaborated for DirectC, the following file is created
127 | # with declarations for C/C++ functions.
128 | vc_hdrs.h
129 |
130 | ##### SVN
131 | .svn/
132 |
133 | ##### Mercurial
134 | .hg/
135 | .hgignore
136 | .hgsigs
137 | .hgsub
138 | .hgsubstate
139 | .hgtags
140 |
141 | ##### Bazaar
142 | .bzr/
143 | .bzrignore
144 |
145 | ##### CVS
146 | /CVS/*
147 | **/CVS/*
148 | .cvsignore
149 | */.cvsignore
150 |
151 | ##### TortoiseGit
152 | # Project-level settings
153 | /.tgitconfig
154 |
155 | ##### PuTTY
156 | # Private key
157 | *.ppk
158 |
159 | ##### Vim
160 | # Swap
161 | [._]*.s[a-v][a-z]
162 | !*.svg # comment out if you don't need vector files
163 | [._]*.sw[a-p]
164 | [._]s[a-rt-v][a-z]
165 | [._]ss[a-gi-z]
166 | [._]sw[a-p]
167 |
168 | # Session
169 | Session.vim
170 | Sessionx.vim
171 |
172 | # Temporary
173 | .netrwhist
174 | *~
175 | # Auto-generated tag files
176 | tags
177 | # Persistent undo
178 | [._]*.un~
179 |
180 | ##### Emacs
181 | # -*- mode: gitignore; -*-
182 | *~
183 | \#*\#
184 | /.emacs.desktop
185 | /.emacs.desktop.lock
186 | *.elc
187 | auto-save-list
188 | tramp
189 | .\#*
190 |
191 | # Org-mode
192 | .org-id-locations
193 | *_archive
194 |
195 | # flymake-mode
196 | *_flymake.*
197 |
198 | # eshell files
199 | /eshell/history
200 | /eshell/lastdir
201 |
202 | # elpa packages
203 | /elpa/
204 |
205 | # reftex files
206 | *.rel
207 |
208 | # AUCTeX auto folder
209 | /auto/
210 |
211 | # cask packages
212 | .cask/
213 | dist/
214 |
215 | # Flycheck
216 | flycheck_*.el
217 |
218 | # server auth directory
219 | /server/
220 |
221 | # projectiles files
222 | .projectile
223 |
224 | # directory configuration
225 | .dir-locals.el
226 |
227 | # network security
228 | /network-security.data
229 |
230 | ##### SublimeText
231 | # Cache files for Sublime Text
232 | *.tmlanguage.cache
233 | *.tmPreferences.cache
234 | *.stTheme.cache
235 |
236 | # Workspace files are user-specific
237 | *.sublime-workspace
238 |
239 | # Project files should be checked into the repository, unless a significant
240 | # proportion of contributors will probably not be using Sublime Text
241 | # *.sublime-project
242 |
243 | # SFTP configuration file
244 | sftp-config.json
245 | sftp-config-alt*.json
246 |
247 | # Package control specific files
248 | Package Control.last-run
249 | Package Control.ca-list
250 | Package Control.ca-bundle
251 | Package Control.system-ca-bundle
252 | Package Control.cache/
253 | Package Control.ca-certs/
254 | Package Control.merged-ca-bundle
255 | Package Control.user-ca-bundle
256 | oscrypto-ca-bundle.crt
257 | bh_unicode_properties.cache
258 |
259 | # Sublime-github package stores a github token in this file
260 | # https://packagecontrol.io/packages/sublime-github
261 | GitHub.sublime-settings
262 |
263 | ##### Notepad++
264 | # Notepad++ backups #
265 | *.bak
266 |
267 | ##### TextMate
268 | *.tmproj
269 | *.tmproject
270 | tmtags
271 |
272 | ##### VisualStudioCode
273 | .vscode/*
274 | !.vscode/settings.json
275 | !.vscode/tasks.json
276 | !.vscode/launch.json
277 | !.vscode/extensions.json
278 | *.code-workspace
279 |
280 | # Local History for Visual Studio Code
281 | .history/
282 |
283 | ##### NetBeans
284 | **/nbproject/private/
285 | **/nbproject/Makefile-*.mk
286 | **/nbproject/Package-*.bash
287 | build/
288 | nbbuild/
289 | dist/
290 | nbdist/
291 | .nb-gradle/
292 |
293 | ##### JetBrains
294 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
295 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
296 |
297 | # User-specific stuff
298 | .idea/**/workspace.xml
299 | .idea/**/tasks.xml
300 | .idea/**/usage.statistics.xml
301 | .idea/**/dictionaries
302 | .idea/**/shelf
303 |
304 | # Generated files
305 | .idea/**/contentModel.xml
306 |
307 | # Sensitive or high-churn files
308 | .idea/**/dataSources/
309 | .idea/**/dataSources.ids
310 | .idea/**/dataSources.local.xml
311 | .idea/**/sqlDataSources.xml
312 | .idea/**/dynamic.xml
313 | .idea/**/uiDesigner.xml
314 | .idea/**/dbnavigator.xml
315 |
316 | # Gradle
317 | .idea/**/gradle.xml
318 | .idea/**/libraries
319 |
320 | # Gradle and Maven with auto-import
321 | # When using Gradle or Maven with auto-import, you should exclude module files,
322 | # since they will be recreated, and may cause churn. Uncomment if using
323 | # auto-import.
324 | # .idea/artifacts
325 | # .idea/compiler.xml
326 | # .idea/jarRepositories.xml
327 | # .idea/modules.xml
328 | # .idea/*.iml
329 | # .idea/modules
330 | # *.iml
331 | # *.ipr
332 |
333 | # CMake
334 | cmake-build-*/
335 |
336 | # Mongo Explorer plugin
337 | .idea/**/mongoSettings.xml
338 |
339 | # File-based project format
340 | *.iws
341 |
342 | # IntelliJ
343 | out/
344 |
345 | # mpeltonen/sbt-idea plugin
346 | .idea_modules/
347 |
348 | # JIRA plugin
349 | atlassian-ide-plugin.xml
350 |
351 | # Cursive Clojure plugin
352 | .idea/replstate.xml
353 |
354 | # Crashlytics plugin (for Android Studio and IntelliJ)
355 | com_crashlytics_export_strings.xml
356 | crashlytics.properties
357 | crashlytics-build.properties
358 | fabric.properties
359 |
360 | # Editor-based Rest Client
361 | .idea/httpRequests
362 |
363 | # Android studio 3.1+ serialized cache file
364 | .idea/caches/build_file_checksums.ser
365 |
366 | ##### Eclipse
367 | .metadata
368 | bin/
369 | tmp/
370 | *.tmp
371 | *.bak
372 | *.swp
373 | *~.nib
374 | local.properties
375 | .settings/
376 | .loadpath
377 | .recommenders
378 |
379 | # External tool builders
380 | .externalToolBuilders/
381 |
382 | # Locally stored "Eclipse launch configurations"
383 | *.launch
384 |
385 | # PyDev specific (Python IDE for Eclipse)
386 | *.pydevproject
387 |
388 | # CDT-specific (C/C++ Development Tooling)
389 | .cproject
390 |
391 | # CDT- autotools
392 | .autotools
393 |
394 | # Java annotation processor (APT)
395 | .factorypath
396 |
397 | # PDT-specific (PHP Development Tools)
398 | .buildpath
399 |
400 | # sbteclipse plugin
401 | .target
402 |
403 | # Tern plugin
404 | .tern-project
405 |
406 | # TeXlipse plugin
407 | .texlipse
408 |
409 | # STS (Spring Tool Suite)
410 | .springBeans
411 |
412 | # Code Recommenders
413 | .recommenders/
414 |
415 | # Annotation Processing
416 | .apt_generated/
417 | .apt_generated_test/
418 |
419 | # Scala IDE specific (Scala & Java development for Eclipse)
420 | .cache-main
421 | .scala_dependencies
422 | .worksheet
423 |
424 | # Uncomment this line if you wish to ignore the project description file.
425 | # Typically, this file would be tracked if it contains build/dependency configurations:
426 | #.project
427 |
428 | ##### Dreamweaver
429 | # DW Dreamweaver added files
430 | _notes
431 | _compareTemp
432 | configs/
433 | dwsync.xml
434 | dw_php_codehinting.config
435 | *.mno
436 |
437 | ##### CodeKit
438 | # General CodeKit files to ignore
439 | config.codekit
440 | config.codekit3
441 | /min
442 |
443 | ##### Gradle
444 | .gradle
445 | **/build/
446 | !src/**/build/
447 |
448 | # Ignore Gradle GUI config
449 | gradle-app.setting
450 |
451 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
452 | !gradle-wrapper.jar
453 |
454 | # Cache of project
455 | .gradletasknamecache
456 |
457 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
458 | # gradle/wrapper/gradle-wrapper.properties
459 |
460 | ##### Composer
461 | composer.phar
462 | /vendor/
463 |
464 | # Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
465 | # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
466 | composer.lock
467 |
468 | ##### PHP CodeSniffer
469 | # gitignore for the PHP Codesniffer framework
470 | # website: https://github.com/squizlabs/PHP_CodeSniffer
471 | #
472 | # Recommended template: PHP.gitignore
473 |
474 | /wpcs/*
475 |
476 | ##### SASS
477 | .sass-cache/
478 | *.css.map
479 | *.sass.map
480 | *.scss.map
481 |
482 |
483 | .env
484 | Homestead.yaml
485 | Homestead.json
486 | /.vagrant
487 | .phpunit.result.cache
488 | tests/.phpunit.result.cache
489 | .php_cs.cache
--------------------------------------------------------------------------------