From 03a6b8accaf24412ff59f77c5cd3ac2c3b6367f4 Mon Sep 17 00:00:00 2001 From: Akkarinage Date: Wed, 21 Aug 2024 11:18:38 +0100 Subject: [PATCH] Add SendGrid API support --- config/application.php | 2 + lib/Flux/MailerSendGrid.php | 72 + lib/Flux/PaymentNotifyRequest.php | 9 +- lib/sendgrid-php/.editorconfig | 8 + lib/sendgrid-php/.env.sample | 1 + lib/sendgrid-php/CHANGELOG.md | 647 +++ lib/sendgrid-php/CODE_OF_CONDUCT.md | 73 + lib/sendgrid-php/CONTRIBUTING.md | 160 + lib/sendgrid-php/Dockerfile | 16 + lib/sendgrid-php/FIRST_TIMERS.md | 53 + lib/sendgrid-php/ISSUE_TEMPLATE.md | 30 + lib/sendgrid-php/LICENSE | 21 + lib/sendgrid-php/Makefile | 37 + lib/sendgrid-php/PULL_REQUEST_TEMPLATE.md | 31 + lib/sendgrid-php/README.md | 206 + lib/sendgrid-php/TROUBLESHOOTING.md | 144 + lib/sendgrid-php/UPGRADE.md | 8 + lib/sendgrid-php/USAGE.md | 5127 +++++++++++++++++ lib/sendgrid-php/USE_CASES.md | 1627 ++++++ lib/sendgrid-php/composer.lock | 2052 +++++++ .../accesssettings/accesssettings.php | 114 + lib/sendgrid-php/examples/alerts/alerts.php | 88 + lib/sendgrid-php/examples/apikeys/apikeys.php | 116 + lib/sendgrid-php/examples/asm/asm.php | 245 + .../examples/browsers/browsers.php | 23 + .../examples/campaigns/campaigns.php | 210 + .../examples/categories/categories.php | 53 + lib/sendgrid-php/examples/clients/clients.php | 39 + .../examples/contactdb/contactdb.php | 546 ++ .../examples/dataresidency/setregion.php | 75 + lib/sendgrid-php/examples/devices/devices.php | 23 + lib/sendgrid-php/examples/geo/geo.php | 23 + .../examples/helpers/contacts/recipients.php | 55 + .../examples/helpers/eventwebhook/example.php | 20 + .../examples/helpers/mail/example.php | 272 + .../examples/helpers/stats/example.php | 29 + lib/sendgrid-php/examples/ips/ips.php | 223 + lib/sendgrid-php/examples/mail/mail.php | 198 + .../mailboxproviders/mailboxproviders.php | 23 + .../examples/mailsettings/mailsettings.php | 307 + .../partnersettings/partnersettings.php | 56 + lib/sendgrid-php/examples/scopes/scopes.php | 21 + .../senderauthentication.php | 444 ++ lib/sendgrid-php/examples/senders/senders.php | 129 + lib/sendgrid-php/examples/stats/stats.php | 23 + .../examples/subusers/subusers.php | 241 + .../examples/suppression/suppression.php | 288 + .../examples/templates/templates.php | 182 + .../trackingsettings/trackingsettings.php | 153 + lib/sendgrid-php/examples/user/user.php | 412 ++ .../lib/BaseSendGridClientInterface.php | 90 + lib/sendgrid-php/lib/SendGrid.php | 25 + lib/sendgrid-php/lib/TwilioEmail.php | 26 + lib/sendgrid-php/lib/contacts/README.md | 16 + lib/sendgrid-php/lib/contacts/Recipient.php | 84 + .../lib/contacts/RecipientForm.php | 45 + .../lib/eventwebhook/EventWebhook.php | 46 + .../lib/eventwebhook/EventWebhookHeader.php | 15 + lib/sendgrid-php/lib/helper/Assert.php | 386 ++ lib/sendgrid-php/lib/mail/Asm.php | 143 + lib/sendgrid-php/lib/mail/Attachment.php | 226 + lib/sendgrid-php/lib/mail/BatchId.php | 69 + lib/sendgrid-php/lib/mail/Bcc.php | 15 + lib/sendgrid-php/lib/mail/BccSettings.php | 107 + .../lib/mail/BypassBounceManagement.php | 82 + .../lib/mail/BypassListManagement.php | 81 + .../lib/mail/BypassSpamManagement.php | 82 + .../lib/mail/BypassUnsubscribeManagement.php | 83 + lib/sendgrid-php/lib/mail/Category.php | 71 + lib/sendgrid-php/lib/mail/Cc.php | 15 + lib/sendgrid-php/lib/mail/ClickTracking.php | 107 + lib/sendgrid-php/lib/mail/Content.php | 119 + lib/sendgrid-php/lib/mail/CustomArg.php | 112 + lib/sendgrid-php/lib/mail/EmailAddress.php | 196 + lib/sendgrid-php/lib/mail/Footer.php | 136 + lib/sendgrid-php/lib/mail/From.php | 15 + lib/sendgrid-php/lib/mail/Ganalytics.php | 239 + lib/sendgrid-php/lib/mail/GroupId.php | 67 + lib/sendgrid-php/lib/mail/GroupsToDisplay.php | 103 + lib/sendgrid-php/lib/mail/Header.php | 109 + lib/sendgrid-php/lib/mail/HtmlContent.php | 26 + lib/sendgrid-php/lib/mail/IpPoolName.php | 76 + lib/sendgrid-php/lib/mail/Mail.php | 1994 +++++++ lib/sendgrid-php/lib/mail/MailSettings.php | 398 ++ lib/sendgrid-php/lib/mail/MimeType.php | 17 + lib/sendgrid-php/lib/mail/OpenTracking.php | 121 + lib/sendgrid-php/lib/mail/Personalization.php | 331 ++ .../lib/mail/PlainTextContent.php | 26 + lib/sendgrid-php/lib/mail/README.md | 14 + lib/sendgrid-php/lib/mail/ReplyTo.php | 15 + lib/sendgrid-php/lib/mail/SandBoxMode.php | 76 + lib/sendgrid-php/lib/mail/Section.php | 108 + lib/sendgrid-php/lib/mail/SendAt.php | 95 + lib/sendgrid-php/lib/mail/SpamCheck.php | 156 + lib/sendgrid-php/lib/mail/Subject.php | 68 + .../lib/mail/SubscriptionTracking.php | 217 + lib/sendgrid-php/lib/mail/Substitution.php | 118 + lib/sendgrid-php/lib/mail/TemplateId.php | 79 + lib/sendgrid-php/lib/mail/To.php | 15 + .../lib/mail/TrackingSettings.php | 249 + lib/sendgrid-php/lib/mail/TypeException.php | 8 + lib/sendgrid-php/lib/stats/Stats.php | 264 + lib/sendgrid-php/phpcs.xml | 196 + lib/sendgrid-php/sendgrid-php.php | 28 + lib/sendgrid-php/static/img/github-fork.png | Bin 0 -> 15189 bytes .../static/img/github-sign-up.png | Bin 0 -> 116981 bytes lib/sendgrid-php/twilio_sendgrid_logo.png | Bin 0 -> 14596 bytes lib/sendgrid-php/vendor/autoload.php | 25 + .../vendor/composer/ClassLoader.php | 579 ++ .../vendor/composer/InstalledVersions.php | 359 ++ lib/sendgrid-php/vendor/composer/LICENSE | 21 + .../vendor/composer/autoload_classmap.php | 13 + .../vendor/composer/autoload_files.php | 10 + .../vendor/composer/autoload_namespaces.php | 9 + .../vendor/composer/autoload_psr4.php | 15 + .../vendor/composer/autoload_real.php | 50 + .../vendor/composer/autoload_static.php | 68 + .../vendor/composer/installed.json | 117 + .../vendor/composer/installed.php | 47 + .../vendor/composer/platform_check.php | 26 + .../sendgrid/php-http-client/.php_cs.dist | 20 + .../sendgrid/php-http-client/CHANGELOG.md | 279 + .../php-http-client/CODE_OF_CONDUCT.md | 73 + .../sendgrid/php-http-client/CONTRIBUTING.md | 152 + .../sendgrid/php-http-client/Dockerfile | 14 + .../sendgrid/php-http-client/FIRST_TIMERS.md | 53 + .../vendor/sendgrid/php-http-client/LICENSE | 21 + .../vendor/sendgrid/php-http-client/Makefile | 16 + .../php-http-client/PULL_REQUEST_TEMPLATE.md | 31 + .../vendor/sendgrid/php-http-client/README.md | 193 + .../php-http-client/TROUBLESHOOTING.md | 22 + .../sendgrid/php-http-client/UPGRADE.md | 8 + .../vendor/sendgrid/php-http-client/USAGE.md | 126 + .../php-http-client/examples/.env_sample | 1 + .../php-http-client/examples/example.php | 61 + .../sendgrid/php-http-client/lib/Client.php | 667 +++ .../lib/Exception/InvalidRequest.php | 21 + .../sendgrid/php-http-client/lib/Response.php | 105 + .../static/img/github-fork.png | Bin 0 -> 13855 bytes .../static/img/github-sign-up.png | Bin 0 -> 86857 bytes .../php-http-client/twilio_sendgrid_logo.png | Bin 0 -> 14596 bytes .../php-http-client/use_cases/README.md | 4 + .../php-http-client/use_cases/docker.md | 21 + .../vendor/starkbank/ecdsa/.travis.yml | 13 + .../vendor/starkbank/ecdsa/LICENSE | 21 + .../vendor/starkbank/ecdsa/README.md | 161 + .../vendor/starkbank/ecdsa/src/ecdsa.php | 24 + .../starkbank/ecdsa/src/ellipticcurve.php | 9 + .../vendor/starkbank/ecdsa/src/privatekey.php | 81 + .../vendor/starkbank/ecdsa/src/publickey.php | 60 + .../vendor/starkbank/ecdsa/src/signature.php | 29 + .../vendor/starkbank/ecdsa/src/utils/file.php | 14 + modules/account/changemail.php | 9 +- modules/account/create.php | 10 +- modules/account/resend.php | 9 +- modules/account/resetpass.php | 9 +- modules/account/resetpw.php | 10 +- modules/mail/index.php | 9 +- modules/servicedesk/create.php | 9 +- modules/servicedesk/staffview.php | 107 +- 160 files changed, 26313 insertions(+), 58 deletions(-) create mode 100644 lib/Flux/MailerSendGrid.php create mode 100644 lib/sendgrid-php/.editorconfig create mode 100644 lib/sendgrid-php/.env.sample create mode 100644 lib/sendgrid-php/CHANGELOG.md create mode 100644 lib/sendgrid-php/CODE_OF_CONDUCT.md create mode 100644 lib/sendgrid-php/CONTRIBUTING.md create mode 100644 lib/sendgrid-php/Dockerfile create mode 100644 lib/sendgrid-php/FIRST_TIMERS.md create mode 100644 lib/sendgrid-php/ISSUE_TEMPLATE.md create mode 100644 lib/sendgrid-php/LICENSE create mode 100644 lib/sendgrid-php/Makefile create mode 100644 lib/sendgrid-php/PULL_REQUEST_TEMPLATE.md create mode 100644 lib/sendgrid-php/README.md create mode 100644 lib/sendgrid-php/TROUBLESHOOTING.md create mode 100644 lib/sendgrid-php/UPGRADE.md create mode 100644 lib/sendgrid-php/USAGE.md create mode 100644 lib/sendgrid-php/USE_CASES.md create mode 100644 lib/sendgrid-php/composer.lock create mode 100644 lib/sendgrid-php/examples/accesssettings/accesssettings.php create mode 100644 lib/sendgrid-php/examples/alerts/alerts.php create mode 100644 lib/sendgrid-php/examples/apikeys/apikeys.php create mode 100644 lib/sendgrid-php/examples/asm/asm.php create mode 100644 lib/sendgrid-php/examples/browsers/browsers.php create mode 100644 lib/sendgrid-php/examples/campaigns/campaigns.php create mode 100644 lib/sendgrid-php/examples/categories/categories.php create mode 100644 lib/sendgrid-php/examples/clients/clients.php create mode 100644 lib/sendgrid-php/examples/contactdb/contactdb.php create mode 100644 lib/sendgrid-php/examples/dataresidency/setregion.php create mode 100644 lib/sendgrid-php/examples/devices/devices.php create mode 100644 lib/sendgrid-php/examples/geo/geo.php create mode 100644 lib/sendgrid-php/examples/helpers/contacts/recipients.php create mode 100644 lib/sendgrid-php/examples/helpers/eventwebhook/example.php create mode 100644 lib/sendgrid-php/examples/helpers/mail/example.php create mode 100644 lib/sendgrid-php/examples/helpers/stats/example.php create mode 100644 lib/sendgrid-php/examples/ips/ips.php create mode 100644 lib/sendgrid-php/examples/mail/mail.php create mode 100644 lib/sendgrid-php/examples/mailboxproviders/mailboxproviders.php create mode 100644 lib/sendgrid-php/examples/mailsettings/mailsettings.php create mode 100644 lib/sendgrid-php/examples/partnersettings/partnersettings.php create mode 100644 lib/sendgrid-php/examples/scopes/scopes.php create mode 100644 lib/sendgrid-php/examples/senderauthentication/senderauthentication.php create mode 100644 lib/sendgrid-php/examples/senders/senders.php create mode 100644 lib/sendgrid-php/examples/stats/stats.php create mode 100644 lib/sendgrid-php/examples/subusers/subusers.php create mode 100644 lib/sendgrid-php/examples/suppression/suppression.php create mode 100644 lib/sendgrid-php/examples/templates/templates.php create mode 100644 lib/sendgrid-php/examples/trackingsettings/trackingsettings.php create mode 100644 lib/sendgrid-php/examples/user/user.php create mode 100644 lib/sendgrid-php/lib/BaseSendGridClientInterface.php create mode 100644 lib/sendgrid-php/lib/SendGrid.php create mode 100644 lib/sendgrid-php/lib/TwilioEmail.php create mode 100644 lib/sendgrid-php/lib/contacts/README.md create mode 100644 lib/sendgrid-php/lib/contacts/Recipient.php create mode 100644 lib/sendgrid-php/lib/contacts/RecipientForm.php create mode 100644 lib/sendgrid-php/lib/eventwebhook/EventWebhook.php create mode 100644 lib/sendgrid-php/lib/eventwebhook/EventWebhookHeader.php create mode 100644 lib/sendgrid-php/lib/helper/Assert.php create mode 100644 lib/sendgrid-php/lib/mail/Asm.php create mode 100644 lib/sendgrid-php/lib/mail/Attachment.php create mode 100644 lib/sendgrid-php/lib/mail/BatchId.php create mode 100644 lib/sendgrid-php/lib/mail/Bcc.php create mode 100644 lib/sendgrid-php/lib/mail/BccSettings.php create mode 100644 lib/sendgrid-php/lib/mail/BypassBounceManagement.php create mode 100644 lib/sendgrid-php/lib/mail/BypassListManagement.php create mode 100644 lib/sendgrid-php/lib/mail/BypassSpamManagement.php create mode 100644 lib/sendgrid-php/lib/mail/BypassUnsubscribeManagement.php create mode 100644 lib/sendgrid-php/lib/mail/Category.php create mode 100644 lib/sendgrid-php/lib/mail/Cc.php create mode 100644 lib/sendgrid-php/lib/mail/ClickTracking.php create mode 100644 lib/sendgrid-php/lib/mail/Content.php create mode 100644 lib/sendgrid-php/lib/mail/CustomArg.php create mode 100644 lib/sendgrid-php/lib/mail/EmailAddress.php create mode 100644 lib/sendgrid-php/lib/mail/Footer.php create mode 100644 lib/sendgrid-php/lib/mail/From.php create mode 100644 lib/sendgrid-php/lib/mail/Ganalytics.php create mode 100644 lib/sendgrid-php/lib/mail/GroupId.php create mode 100644 lib/sendgrid-php/lib/mail/GroupsToDisplay.php create mode 100644 lib/sendgrid-php/lib/mail/Header.php create mode 100644 lib/sendgrid-php/lib/mail/HtmlContent.php create mode 100644 lib/sendgrid-php/lib/mail/IpPoolName.php create mode 100644 lib/sendgrid-php/lib/mail/Mail.php create mode 100644 lib/sendgrid-php/lib/mail/MailSettings.php create mode 100644 lib/sendgrid-php/lib/mail/MimeType.php create mode 100644 lib/sendgrid-php/lib/mail/OpenTracking.php create mode 100644 lib/sendgrid-php/lib/mail/Personalization.php create mode 100644 lib/sendgrid-php/lib/mail/PlainTextContent.php create mode 100644 lib/sendgrid-php/lib/mail/README.md create mode 100644 lib/sendgrid-php/lib/mail/ReplyTo.php create mode 100644 lib/sendgrid-php/lib/mail/SandBoxMode.php create mode 100644 lib/sendgrid-php/lib/mail/Section.php create mode 100644 lib/sendgrid-php/lib/mail/SendAt.php create mode 100644 lib/sendgrid-php/lib/mail/SpamCheck.php create mode 100644 lib/sendgrid-php/lib/mail/Subject.php create mode 100644 lib/sendgrid-php/lib/mail/SubscriptionTracking.php create mode 100644 lib/sendgrid-php/lib/mail/Substitution.php create mode 100644 lib/sendgrid-php/lib/mail/TemplateId.php create mode 100644 lib/sendgrid-php/lib/mail/To.php create mode 100644 lib/sendgrid-php/lib/mail/TrackingSettings.php create mode 100644 lib/sendgrid-php/lib/mail/TypeException.php create mode 100644 lib/sendgrid-php/lib/stats/Stats.php create mode 100644 lib/sendgrid-php/phpcs.xml create mode 100644 lib/sendgrid-php/sendgrid-php.php create mode 100644 lib/sendgrid-php/static/img/github-fork.png create mode 100644 lib/sendgrid-php/static/img/github-sign-up.png create mode 100644 lib/sendgrid-php/twilio_sendgrid_logo.png create mode 100644 lib/sendgrid-php/vendor/autoload.php create mode 100644 lib/sendgrid-php/vendor/composer/ClassLoader.php create mode 100644 lib/sendgrid-php/vendor/composer/InstalledVersions.php create mode 100644 lib/sendgrid-php/vendor/composer/LICENSE create mode 100644 lib/sendgrid-php/vendor/composer/autoload_classmap.php create mode 100644 lib/sendgrid-php/vendor/composer/autoload_files.php create mode 100644 lib/sendgrid-php/vendor/composer/autoload_namespaces.php create mode 100644 lib/sendgrid-php/vendor/composer/autoload_psr4.php create mode 100644 lib/sendgrid-php/vendor/composer/autoload_real.php create mode 100644 lib/sendgrid-php/vendor/composer/autoload_static.php create mode 100644 lib/sendgrid-php/vendor/composer/installed.json create mode 100644 lib/sendgrid-php/vendor/composer/installed.php create mode 100644 lib/sendgrid-php/vendor/composer/platform_check.php create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/.php_cs.dist create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/CHANGELOG.md create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/CODE_OF_CONDUCT.md create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/CONTRIBUTING.md create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/Dockerfile create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/FIRST_TIMERS.md create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/LICENSE create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/Makefile create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/PULL_REQUEST_TEMPLATE.md create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/README.md create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/TROUBLESHOOTING.md create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/UPGRADE.md create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/USAGE.md create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/examples/.env_sample create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/examples/example.php create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/lib/Client.php create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/lib/Exception/InvalidRequest.php create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/lib/Response.php create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/static/img/github-fork.png create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/static/img/github-sign-up.png create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/twilio_sendgrid_logo.png create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/use_cases/README.md create mode 100644 lib/sendgrid-php/vendor/sendgrid/php-http-client/use_cases/docker.md create mode 100644 lib/sendgrid-php/vendor/starkbank/ecdsa/.travis.yml create mode 100644 lib/sendgrid-php/vendor/starkbank/ecdsa/LICENSE create mode 100644 lib/sendgrid-php/vendor/starkbank/ecdsa/README.md create mode 100644 lib/sendgrid-php/vendor/starkbank/ecdsa/src/ecdsa.php create mode 100644 lib/sendgrid-php/vendor/starkbank/ecdsa/src/ellipticcurve.php create mode 100644 lib/sendgrid-php/vendor/starkbank/ecdsa/src/privatekey.php create mode 100644 lib/sendgrid-php/vendor/starkbank/ecdsa/src/publickey.php create mode 100644 lib/sendgrid-php/vendor/starkbank/ecdsa/src/signature.php create mode 100644 lib/sendgrid-php/vendor/starkbank/ecdsa/src/utils/file.php diff --git a/config/application.php b/config/application.php index cc527cd15..74ece1b4f 100644 --- a/config/application.php +++ b/config/application.php @@ -67,6 +67,8 @@ 'MailerSMTPHosts' => null, // When MailerUseSMTP is true: A string host or array of hosts (e.g., 'host1' or array('host1', 'backuphost')). 'MailerSMTPUsername' => null, // When MailerUseSMTP is true: Authorized username for SMTP server. 'MailerSMTPPassword' => null, // When MailerUseSMTP is true: Authorized password for SMTP server (for above user). + 'SendGridAPIKey' => null, // SendGrid API Key for sending mail. (https://sendgrid.com/docs/Classroom/Send/How_Emails_Are_Sent/api_keys.html) + // If this API Key is set, it will be used instead of the SMTP settings. 'ServerStatusCache' => 2, // Store a cached server status and refresh every X minutes. Default: 2 minutes (value is measured in minutes). 'ServerStatusTimeout' => 2, // For each server, spend X amount of seconds to determine whether it's up or not. 'SessionKey' => 'fluxSessionData', // Shouldn't be changed, just specifies the session key to be used for session data. diff --git a/lib/Flux/MailerSendGrid.php b/lib/Flux/MailerSendGrid.php new file mode 100644 index 000000000..710a9f822 --- /dev/null +++ b/lib/Flux/MailerSendGrid.php @@ -0,0 +1,72 @@ +pm = $pm = new \SendGrid\Mail\Mail(); + $this->errLog = self::$errLog; + $this->log = self::$log; + + } + + public function send($recipient, $subject, $template, array $templateVars = array()) + { + if (array_key_exists('_ignoreTemplate', $templateVars) && $templateVars['_ignoreTemplate']) { + $content = $template; + } else { + $templatePath = FLUX_DATA_DIR."/templates/$template.php"; + if (!file_exists($templatePath)) { + return false; + } + + $find = array(); + $repl = array(); + + foreach ($templateVars as $key => $value) { + $find[] = '{'.$key.'}'; + $repl[] = $value; + } + + ob_start(); + include $templatePath; + $content = ob_get_clean(); + + if (!empty($find) && !empty($repl)) { + $content = str_replace($find, $repl, $content); + } + } + $this->pm->setFrom(Flux::config('MailerFromAddress'), Flux::config('MailerFromName')); + $this->pm->AddTo($recipient, $recipient); + $this->pm->SetSubject($subject); + $this->pm->AddContent("text/html", $content); + + $sendgrid = new \SendGrid(Flux::config('SendGridAPIKey')); + + try { + $response = $sendgrid->send($this->pm); + self::$log->puts("sent e-mail -- Recipient: $recipient, Subject: $subject"); + return $response->statusCode() . "\n"; + //print_r($response->headers()); + //print $response->body() . "\n"; + } catch (Exception $e) { + self::$errLog->puts("{$this->pm->ErrorInfo} (while attempting -- Recipient: $recipient, Subject: $subject)"); + return 'Caught exception: '. $e->getMessage() ."\n"; + } + } +} +?> diff --git a/lib/Flux/PaymentNotifyRequest.php b/lib/Flux/PaymentNotifyRequest.php index 0397fbf79..1fbcd5f5a 100644 --- a/lib/Flux/PaymentNotifyRequest.php +++ b/lib/Flux/PaymentNotifyRequest.php @@ -323,7 +323,13 @@ public function process() $this->logPayPal('Transaction invalid, aborting.'); if(!in_array($received_from, $allowed_hosts) && Flux::config('PaypalHackNotify')){ - require_once 'Flux/Mailer.php'; + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } $customArray = @unserialize(base64_decode((string)$this->ipnVariables->get('custom'))); $customArray = $customArray && is_array($customArray) ? $customArray : array(); @@ -331,7 +337,6 @@ public function process() $accountID = $customData->get('account_id'); $serverName = $customData->get('server_name'); - $mail = new Flux_Mailer(); $tmpl = "

Paypal hack detected!

"; $tmpl .= "

Account: ".$accountID."

"; diff --git a/lib/sendgrid-php/.editorconfig b/lib/sendgrid-php/.editorconfig new file mode 100644 index 000000000..e18928df3 --- /dev/null +++ b/lib/sendgrid-php/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*.php] +indent_style = space +indent_size = 4 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/lib/sendgrid-php/.env.sample b/lib/sendgrid-php/.env.sample new file mode 100644 index 000000000..937e999d4 --- /dev/null +++ b/lib/sendgrid-php/.env.sample @@ -0,0 +1 @@ +export SENDGRID_API_KEY='' diff --git a/lib/sendgrid-php/CHANGELOG.md b/lib/sendgrid-php/CHANGELOG.md new file mode 100644 index 000000000..7bb4c6785 --- /dev/null +++ b/lib/sendgrid-php/CHANGELOG.md @@ -0,0 +1,647 @@ +# Change Log +All notable changes to this project will be documented in this file. + +This project adheres to [Semantic Versioning](http://semver.org/). + +[2024-04-18] Version 8.1.2 +-------------------------- +**Library - Chore** +- [PR #1107](https://github.com/sendgrid/sendgrid-php/pull/1107): updating php-http-client version to dynamic. Thanks to [@tiwarishubham635](https://github.com/tiwarishubham635)! + + +[2023-12-06] Version 8.1.1 +-------------------------- +**Library - Chore** +- [PR #1105](https://github.com/sendgrid/sendgrid-php/pull/1105): updated php-http-client version to enable setHost. Thanks to [@tiwarishubham635](https://github.com/tiwarishubham635)! + + +[2023-12-01] Version 8.1.0 +-------------------------- +**Library - Feature** +- [PR #1104](https://github.com/sendgrid/sendgrid-php/pull/1104): Added Data residency for eu and global region. Thanks to [@tiwarishubham635](https://github.com/tiwarishubham635)! + + +[2022-08-10] Version 8.0.1 +-------------------------- +**Library - Docs** +- [PR #1097](https://github.com/sendgrid/sendgrid-php/pull/1097): rewrite function description. Thanks to [@mohamed-foly](https://github.com/mohamed-foly)! +- [PR #1095](https://github.com/sendgrid/sendgrid-php/pull/1095): Modify README in alignment with SendGrid Support. Thanks to [@garethpaul](https://github.com/garethpaul)! + +**Library - Fix** +- [PR #1081](https://github.com/sendgrid/sendgrid-php/pull/1081): get rid of deprecation warnings regarding the first parameter of mb_convert_encoding. Thanks to [@cclark61](https://github.com/cclark61)! + +**Library - Test** +- [PR #1099](https://github.com/sendgrid/sendgrid-php/pull/1099): Adding misc as PR type. Thanks to [@rakatyal](https://github.com/rakatyal)! + + +[2022-05-04] Version 8.0.0 +-------------------------- +**Note:** This release contains breaking changes, check our [upgrade guide](./UPGRADE.md#2022-05-04-7xx-to-8xx) for detailed migration notes. + +**Library - Chore** +- [PR #1090](https://github.com/sendgrid/sendgrid-php/pull/1090): drop support for EOL PHP versions and add support for PHP 8. Thanks to [@childish-sambino](https://github.com/childish-sambino)! **(breaking change)** + + +[2022-03-09] Version 7.11.5 +--------------------------- +**Library - Chore** +- [PR #1085](https://github.com/sendgrid/sendgrid-php/pull/1085): push Datadog Release Metric upon deploy success. Thanks to [@eshanholtz](https://github.com/eshanholtz)! + + +[2022-02-09] Version 7.11.4 +--------------------------- +**Library - Chore** +- [PR #1079](https://github.com/sendgrid/sendgrid-php/pull/1079): add deploy job to test and deploy gh workflow. Thanks to [@Hunga1](https://github.com/Hunga1)! + + +[2022-01-26] Version 7.11.3 +--------------------------- +**Library - Chore** +- [PR #1075](https://github.com/sendgrid/sendgrid-php/pull/1075): migrate to Github actions. Thanks to [@JenniferMah](https://github.com/JenniferMah)! + + +[2022-01-12] Version 7.11.2 +--------------------------- +**Library - Chore** +- [PR #1074](https://github.com/sendgrid/sendgrid-php/pull/1074): update license year. Thanks to [@JenniferMah](https://github.com/JenniferMah)! + + +[2021-12-15] Version 7.11.1 +--------------------------- +**Library - Fix** +- [PR #1066](https://github.com/sendgrid/sendgrid-php/pull/1066): Suppress deprecation warnings in PHP 8.1. Thanks to [@andreas-aeschlimann](https://github.com/andreas-aeschlimann)! + + +[2021-11-17] Version 7.11.0 +--------------------------- +**Library - Feature** +- [PR #1065](https://github.com/sendgrid/sendgrid-php/pull/1065): add spam, bounce and unsubscribe bypass management filters. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)! + + +[2021-10-18] Version 7.10.0 +--------------------------- +**Library - Feature** +- [PR #1061](https://github.com/sendgrid/sendgrid-php/pull/1061): allow personalization of the From name and email for each email recipient. Thanks to [@beebzz](https://github.com/beebzz)! + +**Library - Docs** +- [PR #1060](https://github.com/sendgrid/sendgrid-php/pull/1060): improve signed webhook event validation docs. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)! + + +[2021-01-27] Version 7.9.2 +-------------------------- +**Library - Fix** +- [PR #1030](https://github.com/sendgrid/sendgrid-php/pull/1030): Fixing namespace for classes on test/unit folder. Thanks to [@peter279k](https://github.com/peter279k)! + + +[2020-11-18] Version 7.9.1 +-------------------------- +**Library - Docs** +- [PR #980](https://github.com/sendgrid/sendgrid-php/pull/980): Incorrect links in Use Cases, section Email Stats. Thanks to [@kampalex](https://github.com/kampalex)! + + +[2020-11-05] Version 7.9.0 +-------------------------- +**Library - Feature** +- [PR #674](https://github.com/sendgrid/sendgrid-php/pull/674): Allows for a user to utilize self-signed certificates. Thanks to [@davcpas1234](https://github.com/davcpas1234)! + +**Library - Test** +- [PR #704](https://github.com/sendgrid/sendgrid-php/pull/704): Adding php static analysis. Thanks to [@reisraff](https://github.com/reisraff)! + + +[2020-10-14] Version 7.8.5 +-------------------------- +**Library - Docs** +- [PR #1009](https://github.com/sendgrid/sendgrid-php/pull/1009): Incorrect value order in example array for addAttachments. Thanks to [@Anorris-NLR](https://github.com/Anorris-NLR)! + + +[2020-09-28] Version 7.8.4 +-------------------------- +**Library - Fix** +- [PR #1006](https://github.com/sendgrid/sendgrid-php/pull/1006): don't wrap names in double-quotes. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + +**Library - Chore** +- [PR #725](https://github.com/sendgrid/sendgrid-php/pull/725): Require conformance to style standards. Thanks to [@jmauerhan](https://github.com/jmauerhan)! + + +[2020-09-02] Version 7.8.3 +-------------------------- +**Library - Fix** +- [PR #1001](https://github.com/sendgrid/sendgrid-php/pull/1001): support 'to' addresses with subjects. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-08-19] Version 7.8.2 +-------------------------- +**Library - Docs** +- [PR #717](https://github.com/sendgrid/sendgrid-php/pull/717): Update documentation for retrieving a list of all templates. Thanks to [@renshuki](https://github.com/renshuki)! +- [PR #729](https://github.com/sendgrid/sendgrid-php/pull/729): Added S3 attachment example. Thanks to [@semijoelon](https://github.com/semijoelon)! +- [PR #984](https://github.com/sendgrid/sendgrid-php/pull/984): Reduce excessive comments in code examples. Thanks to [@kampalex](https://github.com/kampalex)! + +**Library - Test** +- [PR #735](https://github.com/sendgrid/sendgrid-php/pull/735): add unit tests for lib/email. Thanks to [@peter279k](https://github.com/peter279k)! +- [PR #756](https://github.com/sendgrid/sendgrid-php/pull/756): Add more scenarios for email address encoding. Thanks to [@2mt-heuser](https://github.com/2mt-heuser)! + +**Library - Chore** +- [PR #993](https://github.com/sendgrid/sendgrid-php/pull/993): update GitHub branch references to use HEAD. Thanks to [@thinkingserious](https://github.com/thinkingserious)! + + +[2020-08-05] Version 7.8.1 +-------------------------- +**Library - Fix** +- [PR #755](https://github.com/sendgrid/sendgrid-php/pull/755): remove loader.php and update CONTRIBUTING.md. Thanks to [@mkasberg](https://github.com/mkasberg)! + + +[2020-07-22] Version 7.8.0 +-------------------------- +**Library - Chore** +- [PR #990](https://github.com/sendgrid/sendgrid-php/pull/990): migrate to new default sendgrid-oai branch. Thanks to [@eshanholtz](https://github.com/eshanholtz)! + +**Library - Docs** +- [PR #778](https://github.com/sendgrid/sendgrid-php/pull/778): fix grammar issue in CHANGELOG. Thanks to [@Hestersue43](https://github.com/Hestersue43)! +- [PR #759](https://github.com/sendgrid/sendgrid-php/pull/759): add Email Activity API to usage docs. Thanks to [@ajloria](https://github.com/ajloria)! + +**Library - Feature** +- [PR #850](https://github.com/sendgrid/sendgrid-php/pull/850): Add an option to override SendGrid Client path version. Thanks to [@erbaker](https://github.com/erbaker)! +- [PR #762](https://github.com/sendgrid/sendgrid-php/pull/762): enhanced type exception handling. Thanks to [@misantron](https://github.com/misantron)! + + +[2020-07-08] Version 7.7.0 +-------------------------- +**Library - Feature** +- [PR #985](https://github.com/sendgrid/sendgrid-php/pull/985): Additional check for existence of Composer autoloader. Thanks to [@kampalex](https://github.com/kampalex)! +- [PR #978](https://github.com/sendgrid/sendgrid-php/pull/978): Mail constructor: Added support for array of Substitution instances using $globalSubstitutions. Thanks to [@kampalex](https://github.com/kampalex)! + +**Library - Fix** +- [PR #976](https://github.com/sendgrid/sendgrid-php/pull/976): allow a float type as a substitution value in transactional templates. Thanks to [@thinkingserious](https://github.com/thinkingserious)! +- [PR #977](https://github.com/sendgrid/sendgrid-php/pull/977): Mail constructor: Verify arguments $plainTextContent and $htmlContent if provided. Thanks to [@kampalex](https://github.com/kampalex)! +- [PR #971](https://github.com/sendgrid/sendgrid-php/pull/971): Optional Personalization arguments handling. Thanks to [@kampalex](https://github.com/kampalex)! + + +[2020-06-10] Version 7.6.0 +-------------------------- +**Library - Fix** +- [PR #975](https://github.com/sendgrid/sendgrid-php/pull/975): Inspected code of Stats, example helpers. Thanks to [@kampalex](https://github.com/kampalex)! +- [PR #967](https://github.com/sendgrid/sendgrid-php/pull/967): add support for unicode in local part of email address when using PHP>=7.1. Thanks to [@kampalex](https://github.com/kampalex)! +- [PR #974](https://github.com/sendgrid/sendgrid-php/pull/974): Accept string typed Subject in Personalization. Thanks to [@kampalex](https://github.com/kampalex)! +- [PR #973](https://github.com/sendgrid/sendgrid-php/pull/973): Replace dynamic template data typehints. Thanks to [@kampalex](https://github.com/kampalex)! + +**Library - Feature** +- [PR #969](https://github.com/sendgrid/sendgrid-php/pull/969): verify signature from event webhook. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-05-27] Version 7.5.2 +-------------------------- +**Library - Fix** +- [PR #966](https://github.com/sendgrid/sendgrid-php/pull/966): Rename 'BaseInterface' to 'BaseSendGridClientInterface.php'. Thanks to [@childish-sambino](https://github.com/childish-sambino)! +- [PR #965](https://github.com/sendgrid/sendgrid-php/pull/965): use classmap instead of files for Composer autoload. Thanks to [@kampalex](https://github.com/kampalex)! + +**Library - Docs** +- [PR #839](https://github.com/sendgrid/sendgrid-php/pull/839): add documentation for segments in USAGE.md. Thanks to [@rwhirn](https://github.com/rwhirn)! + + +[2020-05-13] Version 7.5.1 +-------------------------- +**Library - Fix** +- [PR #960](https://github.com/sendgrid/sendgrid-php/pull/960): migrate to common prism setup. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-04-29] Version 7.5.0 +-------------------------- +**Library - Fix** +- [PR #952](https://github.com/sendgrid/sendgrid-php/pull/952): refactor and fix personalization inserts/updates. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + +**Library - Feature** +- [PR #951](https://github.com/sendgrid/sendgrid-php/pull/951): add support for Twilio Email. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-04-01] Version 7.4.6 +-------------------------- +**Library - Docs** +- [PR #947](https://github.com/sendgrid/sendgrid-php/pull/947): support verbiage for login issues. Thanks to [@adamchasetaylor](https://github.com/adamchasetaylor)! +- [PR #946](https://github.com/sendgrid/sendgrid-php/pull/946): correct params order in example.php. Thanks to [@spaze](https://github.com/spaze)! + + +[2020-03-18] Version 7.4.5 +-------------------------- +**Library - Docs** +- [PR #749](https://github.com/sendgrid/sendgrid-php/pull/749): add @throws to docblock. Thanks to [@iPoul](https://github.com/iPoul)! + + +[2020-03-04] Version 7.4.4 +-------------------------- +**Library - Chore** +- [PR #941](https://github.com/sendgrid/sendgrid-php/pull/941): add PHP 7.4 to Travis and test with lowest dependencies. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-02-19] Version 7.4.3 +-------------------------- +**Library - Fix** +- [PR #918](https://github.com/sendgrid/sendgrid-php/pull/918): resolve deprecation notices when using Composer 1.10(-dev). Thanks to [@kampalex](https://github.com/kampalex)! +- [PR #939](https://github.com/sendgrid/sendgrid-php/pull/939): drop the prism binary and ignore unneeded files from the archive. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-01-22] Version 7.4.2 +-------------------------- +**Library - Docs** +- [PR #928](https://github.com/sendgrid/sendgrid-php/pull/928): baseline all the templated markdown docs. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-01-09] Version 7.4.1 +-------------------------- +**Library - Docs** +- [PR #710](https://github.com/sendgrid/sendgrid-php/pull/710): correct the *.md files using Grammarly. Thanks to [@myzeprog](https://github.com/myzeprog)! +- [PR #742](https://github.com/sendgrid/sendgrid-php/pull/742): properly capitalize brands GitHub and SendGrid. Thanks to [@aslafy-z](https://github.com/aslafy-z)! +- [PR #737](https://github.com/sendgrid/sendgrid-php/pull/737): remove `sudo` requirement for running docker. Thanks to [@jamietanna](https://github.com/jamietanna)! + +**Library - Chore** +- [PR #926](https://github.com/sendgrid/sendgrid-php/pull/926): prep the repo for automated releasing. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + +**Library - Fix** +- [PR #765](https://github.com/sendgrid/sendgrid-php/pull/765): correct typo in PR template from "Sendgrid" to "SendGrid". Thanks to [@JoeRomeo](https://github.com/JoeRomeo)! +- [PR #757](https://github.com/sendgrid/sendgrid-php/pull/757): remove dead code when setting the subject. Thanks to [@2mt-heuser](https://github.com/2mt-heuser)! +- [PR #761](https://github.com/sendgrid/sendgrid-php/pull/761): correct the License file path in ReadMe. Thanks to [@sanjaysingh](https://github.com/sanjaysingh)! +- [PR #770](https://github.com/sendgrid/sendgrid-php/pull/770): update the link for cURL example in Troubleshooting.MD. Thanks to [@music-mind](https://github.com/music-mind)! +- [PR #887](https://github.com/sendgrid/sendgrid-php/pull/887): correct the mail helper readme link in example. Thanks to [@flashadvocate](https://github.com/flashadvocate)! + + +[2019-12-11] Version 7.4.0 +-------------------------- + +**Library - Fix** +- [PR #705](https://github.com/sendgrid/sendgrid-php/pull/705): Fixing issue #686: NULL Item in Personalizations. Thanks to [@reisraff](https://github.com/reisraff)! +- [PR #788](https://github.com/sendgrid/sendgrid-php/pull/788): Fixes #788 - Add missing parameter for addCc and addBcc methods. Thanks to [@hjmsw](https://github.com/hjmsw)! + +**Library - Docs** +- [PR #836](https://github.com/sendgrid/sendgrid-php/pull/836): Update example.php. Add personalization example as per #792. Thanks to [@hjmsw](https://github.com/hjmsw)! + +**Library - Feature** +- [PR #841](https://github.com/sendgrid/sendgrid-php/pull/841): Make $emailAddress on TypeException to be evaluated. Thanks to [@yehudah](https://github.com/yehudah)! + +## [7.3.0] - 2019-04-15 ## +### Fixed +- PR [#821](https://github.com/sendgrid/sendgrid-php/pull/821): PHP 7.3 support and fix Prism download problem. +- Closes [#669](https://github.com/sendgrid/sendgrid-php/issues/669), PR [#670](https://github.com/sendgrid/sendgrid-php/pull/670): Fix Mail::setGlobalSubject(). Thanks to [Spencer Salisbury](https://github.com/smsalisbury) for the solution! +- Closes [#782](https://github.com/sendgrid/sendgrid-php/issues/782), PR [#783](https://github.com/sendgrid/sendgrid-php/pull/783): Remove references to 'whitelabel'. Thanks to [Chandler Weiner](https://github.com/crweiner) for the solution! +- Closes [#763](https://github.com/sendgrid/sendgrid-php/issues/763), PR [#764](https://github.com/sendgrid/sendgrid-php/pull/764): Update link to license. Thanks to [Pranjal Vyas](https://github.com/vyaspranjal33) for the solution! +- PR [#760](https://github.com/sendgrid/sendgrid-php/pull/760): Clean up Prism shell script. Thanks to [gy741](https://github.com/gy741) for the solution! +- Closes [#739](https://github.com/sendgrid/sendgrid-php/issues/739), PR [#740](https://github.com/sendgrid/sendgrid-php/pull/740): . Thanks to [Alex Borisov](https://github.com/smrtab) for the solution! + +### Added +- PR [#828](https://github.com/sendgrid/sendgrid-php/pull/828): Update Twilio branding, CLA policy. +- Closes [#768](https://github.com/sendgrid/sendgrid-php/issues/768), PR [#769](https://github.com/sendgrid/sendgrid-php/pull/769): Update prerequisites. Thanks to [Rishabh](https://github.com/Rishabh04-02) for the solution! +- Closes [#733](https://github.com/sendgrid/sendgrid-php/issues/733), PR [#736](https://github.com/sendgrid/sendgrid-php/pull/736): Update CONTRIBUTING - contribution guideline to branch off development. Thanks to [Alex](https://github.com/myzeprog) for the solution! +- Closes [#481](https://github.com/sendgrid/sendgrid-php/issues/481), PR [#743](https://github.com/sendgrid/sendgrid-php/pull/743): Added Box attachment example. Thanks to [Joel](https://github.com/semijoelon) for the solution! +- Closes [#690](https://github.com/sendgrid/sendgrid-php/issues/690), PR [#698](https://github.com/sendgrid/sendgrid-php/pull/698): Update prism version. Thanks to [Gergo Juhasz](https://github.com/geryjuhasz) for the solution! + +## [7.2.1] - 2018-09-18 ## +### Fixed +- Closes [#671](https://github.com/sendgrid/sendgrid-php/issues/671), PR [#689](https://github.com/sendgrid/sendgrid-php/pull/689): isBase64 function returning incorrect. Thanks to [Jmky](https://github.com/Jmky) for the solution! + +## [7.2.0] - 2018-08-15 ## +### Added +- Closes [#648](https://github.com/sendgrid/sendgrid-php/issues/648), PR [#657](https://github.com/sendgrid/sendgrid-php/pull/657): Allow for Dynamic Templates Implementation. Thanks to [Mike Willbanks](https://github.com/mwillbanks) for the PR! + +## [7.1.1] - 2018-08-15 ## +### Fixed +- Closes [#667](https://github.com/sendgrid/sendgrid-php/issues/667), PR [#668](https://github.com/sendgrid/sendgrid-php/pull/668): isBase64 function fix. Thanks to [Tigran M](https://github.com/developer-devPHP) for bringing this to our attention! + +## [7.1.0] - 2018-08-14 ## +### Added + +- Closes [#612](https://github.com/sendgrid/sendgrid-php/issues/612), PR [#652](https://github.com/sendgrid/sendgrid-php/pull/652): Fixes #612 Add TypeException and include type validations in classes inside mail/. Thanks to [James Harding](https://github.com/hjmsw) for the PR! +- Closes [#551](https://github.com/sendgrid/sendgrid-php/issues/551), PR [#571](https://github.com/sendgrid/sendgrid-php/pull/571): Add ability to impersonate subuser. Thanks to [Stian Prestholdt](https://github.com/stianpr) for the PR! +- Closes [#617](https://github.com/sendgrid/sendgrid-php/issues/617), PR [#651](https://github.com/sendgrid/sendgrid-php/pull/651): Add try / catch to examples. Thanks to [James Harding](https://github.com/hjmsw) for the PR! +- Closes [#619](https://github.com/sendgrid/sendgrid-php/issues/619), PR [#620](https://github.com/sendgrid/sendgrid-php/pull/620): PHPDoc & code improvements. Thanks to [Martijn Melchers](https://github.com/martijnmelchers) for the PR! +- Closes [#610](https://github.com/sendgrid/sendgrid-php/issues/610), PR [#628](https://github.com/sendgrid/sendgrid-php/pull/628): Removes unnecessary linter warning from phpcs. Thanks to [James Harding](https://github.com/hjmsw) for the PR! +- Closes [#608](https://github.com/sendgrid/sendgrid-php/issues/611), PR [#626](https://github.com/sendgrid/sendgrid-php/pull/626): Add check so that getContents() always returns content with MimeType text/plain first in array of Content objects. Thanks to [James Harding](https://github.com/hjmsw) for the PR! +- Closes [#611](https://github.com/sendgrid/sendgrid-php/issues/611), PR [#618](https://github.com/sendgrid/sendgrid-php/pull/618): Attachments now automatically get base64 encoded if they are not already. Thanks to [Martijn Melchers](https://github.com/martijnmelchers) for the PR! +- PR [#661](https://github.com/sendgrid/sendgrid-php/pull/661): Add Code Triage tag. Thanks to [Anshul Singhal](https://github.com/af4ro) for the PR! +- PR [#663](https://github.com/sendgrid/sendgrid-php/pull/663): Improve Contributing.md readability. Thanks to [Anshul Singhal](https://github.com/af4ro) for the PR! + +### Fixed +- PR [#631](https://github.com/sendgrid/sendgrid-php/pull/631): Broken documentation link. Thanks to [David Duman](https://github.com/dvdnhm) for the PR! +- PR [#633](https://github.com/sendgrid/sendgrid-php/pull/633): Fixes for non-composer environments. Thanks to [Tom Gordon](https://github.com/apcro) for the PR! +- PR [#634](https://github.com/sendgrid/sendgrid-php/pull/634): Fixes missing file extension. Thanks to [Muberra Duman Demirtepe](https://github.com/muberraduman) for the PR! +- PR [#658](https://github.com/sendgrid/sendgrid-php/pull/658): Corrected PHP Syntax. Thanks to [David Passmore](https://github.com/davcpas1234) for the PR! +- Fixes [#624](https://github.com/sendgrid/sendgrid-php/issues/624), PR [#625](https://github.com/sendgrid/sendgrid-php/pull/625): Fix setGroupsToDisplay's handling of array arguments. Thanks to [Mo Ismailzai](https://github.com/moismailzai) for the PR! + +## [7.0.0] - 2018-05-19 ## +### BREAKING CHANGE + +Thanks to the [strong support and feedback of the SendGrid PHP community](https://github.com/sendgrid/sendgrid-php/issues/434), we have a new version of this SDK that should be a big improvement in the developer experience for this SDK. + +In particular, I'd like to make special mention of [@caseyw](https://github.com/caseyw), [@vitya1](https://github.com/vitya1), [@Braunson](https://github.com/Braunson), [@cbschuld](https://github.com/cbschuld), [@paoga87](https://github.com/paoga87), [@Taluu](https://github.com/Taluu), [@mazanax](https://github.com/mazanax), [@ninsuo](https://github.com/ninsuo), [@ianh2](https://github.com/ianh2), [@WadeShuler](https://github.com/WadeShuler), [@jaimehing](https://github.com/jaimehing), [@KnightAR](https://github.com/KnightAR), [@alextech](https://github.com/alextech) (my apologies if I've missed you) + +Since this is a major departure from v6.X, we advise you to refactor your code according to the documentation found in the [README](README.md) and [USE_CASES](USE_CASES.md) files. We hope you find the new interface much easier to work with. Please open an [issue](https://github.com/sendgrid/sendgrid-php/issues) or PR if you run into any trouble or have any feedback. + +If you wish to continue using previous versions of this SDK, no problem. However, we will not be updating versions less than v7 except for critical bugs and/or security issues. + +We hope this will be the last breaking change in the foreseeable future; that said, let the iterations begin! + +## [6.2.0] - 2018-03-28 ## +### Added +- Closes [#454](https://github.com/sendgrid/sendgrid-php/issues/454), PR [#502](https://github.com/sendgrid/sendgrid-php/pull/502): +Add helper for adding new recipients to your contactdb via a webform, thanks to [Kraig Hufstedler](https://github.com/kraigh) for the PR! + +- Closes [#487](https://github.com/sendgrid/sendgrid-php/issues/487), PR [#506](https://github.com/sendgrid/sendgrid-php/pull/506): +Add helper to get all stats from a specified data range, thanks to [Milos Pejanovic](https://github.com/runz0rd) for the PR! + +- Closes [#368](https://github.com/sendgrid/sendgrid-php/issues/368), PR [#511](https://github.com/sendgrid/sendgrid-php/pull/511): +Add support for commas and semicolns in email name, thanks to [Quentin Ligier](https://github.com/qligier) for the PR! + +- Closes [#491](https://github.com/sendgrid/sendgrid-php/issues/491), PR [#493](https://github.com/sendgrid/sendgrid-php/pull/493: +Allow for setting attachment content from path, thanks to [rparpa](https://github.com/rparpa) for the PR! + +## [6.1.0] - 2018-03-27 ## +### Added +- PR [#512](https://github.com/sendgrid/sendgrid-php/pull/512): Omit PHP closing tag in use case sample, thanks to [Sébastien Santoro](https://github.com/dereckson) for the PR! + +- PR [#575](https://github.com/sendgrid/sendgrid-php/pull/575): Add an example to the README.md describing how to send emails as HTML as the content type, thanks to [Benjamin Manford](https://github.com/manfordbenjamin) for the PR! + +- Closes [#547](https://github.com/sendgrid/sendgrid-php/issues/547), PR [#549](https://github.com/sendgrid/sendgrid-php/pull/549): +Added Code Review to Contributing.md, thanks to [tomhorvat](https://github.com/tomhorvat) for the PR! + +- PR [#565](https://github.com/sendgrid/sendgrid-php/pull/565): Add PHP 7.1 and 7.2 to Travis build matrix, thanks to [Emir Beganović](https://github.com/emirb) for the PR! + +- PR [#577](https://github.com/sendgrid/sendgrid-php/pull/577): Update PHP Version terms, thanks to [Siddhant Sharma](https://github.com/ssiddhantsharma) for the PR! + +- Closes [#540](https://github.com/sendgrid/sendgrid-php/issues/540), PR [#543](https://github.com/sendgrid/sendgrid-php/pull/543): +Feature/split unit tests, thanks to [Owen Voke](https://github.com/pxgamer) for the PR! + +- Closes [#441](https://github.com/sendgrid/sendgrid-php/issues/441), PR [#467](https://github.com/sendgrid/sendgrid-php/pull/467): +Add deploy to heroku button, thanks to [pangaunn](https://github.com/pangaunn) for the PR! + +- Closes [#423](https://github.com/sendgrid/sendgrid-php/issues/423), PR [#510](https://github.com/sendgrid/sendgrid-php/pull/510): +Adding Google App engine installation with composer instructions, thanks to [Nalin Bhardwaj](https://github.com/nalinbhardwaj) for the PR! + +- Closes [#541](https://github.com/sendgrid/sendgrid-php/issues/541), PR [#542](https://github.com/sendgrid/sendgrid-php/pull/542): +Added CodeCov support, thanks to [Owen Voke](https://github.com/pxgamer) for the PR! + +- PR [#539](https://github.com/sendgrid/sendgrid-php/pull/539): Rename LICENSE.txt to md, thanks to [Ankit Jain](https://github.com/ankitjain28may) for the PR! + +- Closes [#436](https://github.com/sendgrid/sendgrid-php/issues/436), PR [#535](https://github.com/sendgrid/sendgrid-php/pull/535): Add docker development setup, thanks to [Samundra Shrestha](https://github.com/samundra) for the PR! + +- Closes [#532](https://github.com/sendgrid/sendgrid-php/issues/532), PR [#537](https://github.com/sendgrid/sendgrid-php/pull/537): Add license date range unit test, thanks to [uppe-r](https://github.com/uppe-r) for the PR! + +- Closes [#533](https://github.com/sendgrid/sendgrid-php/issues/533), PR [#536](https://github.com/sendgrid/sendgrid-php/pull/536): Add unit test to check that specific files exist in repo, thanks to [Bertus Steenberg](https://github.com/bertuss) for the PR! + +- Closes [#524](https://github.com/sendgrid/sendgrid-php/issues/524), PR [#527](https://github.com/sendgrid/sendgrid-php/pull/527): Created code climate YML file, thanks to [Prashu Chaudhary](https://github.com/prashuchaudhary) for the PR! + +- Closes [#520](https://github.com/sendgrid/sendgrid-php/issues/520), PR [#523](https://github.com/sendgrid/sendgrid-php/pull/523): Added sample env file, thanks to [Joey Lee](https://github.com/yeoji) for the PR! + +- PR [#519](https://github.com/sendgrid/sendgrid-php/pull/519): Add github PR template, thanks to [Alex](https://github.com/pushkyn) for the PR! + +- PR [#513](https://github.com/sendgrid/sendgrid-php/pull/513): Update to PHP 7.0.0 refactor - Fix syntax error in refactor documentation, thanks to [Sébastien Santoro](https://github.com/dereckson) for the PR! + +- PR [#505](https://github.com/sendgrid/sendgrid-php/pull/505): Update README.md with additional badges, thanks to [Lalit Vijay](https://github.com/lalitvj) for the PR! + +- Closes [#500](https://github.com/sendgrid/sendgrid-php/issues/500), PR [#504](https://github.com/sendgrid/sendgrid-php/pull/504): SEO Friendly Section links, thanks to [Dharma Saputra](https://github.com/ladhadha) for the PR! + +- PR [#503](https://github.com/sendgrid/sendgrid-php/pull/503): Added new badges to README.md, thanks to [Alex](https://github.com/myzeprog) for the PR! + +- Closes [#492](https://github.com/sendgrid/sendgrid-php/issues/492), PR [#494](https://github.com/sendgrid/sendgrid-php/pull/494): Demonstrate how to review the request body for troubleshooting, thanks to [Alex](https://github.com/myzeprog) for the PR! + +- PR [#476](https://github.com/sendgrid/sendgrid-php/pull/476): Update README.md with license information, thanks to [Tarmo Leppänen](https://github.com/tarlepp) for the PR! + +- PR [#475](https://github.com/sendgrid/sendgrid-php/pull/475): Add documentation for setting up domain whitelabel, thanks to [Sourav Sarkar](https://github.com/amsourav) for the PR! + +- PR [#468](https://github.com/sendgrid/sendgrid-php/pull/463): Changes the recommendation to use composer as recommended source, thanks to [Gabriela D'Ávila Ferrara](https://github.com/gabidavila) for the PR! + +- PR [#463](https://github.com/sendgrid/sendgrid-php/pull/463): Add TROUBLESHOOTING.md section about fixing error 415, thanks to [AlbinoDrought](https://github.com/AlbinoDrought) for the PR! + +- PR [#456](https://github.com/sendgrid/sendgrid-php/pull/456): Added Code of Conduct, thanks to [Rubemlrm](https://github.com/Rubemlrm) for the PR! + +- PR [#439](https://github.com/sendgrid/sendgrid-php/pull/439): Update to PHP 7.0.0 refactor - Removal of Collections, thanks to [Joseph Opanel](https://github.com/jopanel) for the PR! + +- PR [#416](https://github.com/sendgrid/sendgrid-php/pull/416): Add release notifications, thanks to [Gabriel Krell](https://github.com/gabrielkrell) for the PR! + +- PR [#415](https://github.com/sendgrid/sendgrid-php/pull/415): Updated example.php to fix that there was no way for the sections to get substituted without their being a substitution that calls them, thanks to [Kyle Roberts](https://github.com/kylearoberts) for the PR! + +### Fixed +- PR [#545](https://github.com/sendgrid/sendgrid-php/pull/545): Fix typo CONTRIBUTING.md, thanks to [thepriefy](https://github.com/thepriefy) for the PR! + +- PR [#588](https://github.com/sendgrid/sendgrid-php/pull/588): Fix broken unit tests + +- PR [#576](https://github.com/sendgrid/sendgrid-php/pull/576): API level addressing of the string-only in addSubstitution arg rule. Every long integer triggers a bad request, thanks to [Ezequiel Villarreal](https://github.com/saruman) for the PR! + +- PR [#517](https://github.com/sendgrid/sendgrid-php/pull/517): Fix typos in USAGE.md, thanks to [Anatoly](https://github.com/anatolyyyyyy) for the PR! + +- PR [#530](https://github.com/sendgrid/sendgrid-php/pull/530): Changed the license period., thanks to [Siddhant Sharma](https://github.com/ssiddhantsharma) for the PR! + +- PR [#514](https://github.com/sendgrid/sendgrid-php/pull/514): Don't close img tag in HTML, thanks to [Sébastien Santoro](https://github.com/dereckson) for the PR! + +- PR [#507](https://github.com/sendgrid/sendgrid-php/pull/507): Fix typos in various files, thanks to [Brandon Smith](https://github.com/brandon93s) for the PR! + +- Fixes [#336](https://github.com/sendgrid/sendgrid-php/issues/336), PR [#479](https://github.com/sendgrid/sendgrid-php/pull/479): Incorrect documentation path fixed, thanks to [Valerian Pereira](https://github.com/valerianpereira) for the PR! + +- PR [#465](https://github.com/sendgrid/sendgrid-php/pull/465): Fix typo in README.md, thanks to [shra1cumar](https://github.com/shra1cumar) for the PR! + +- PR [#449](https://github.com/sendgrid/sendgrid-php/pull/449): Fix typos in USAGE.md, thanks to [Cícero Pablo](https://github.com/ciceropablo) for the PR! + +- PR [#448](https://github.com/sendgrid/sendgrid-php/pull/448): Fix typos in TROUBLESHOOTING.md, thanks to [Cícero Pablo](https://github.com/ciceropablo) for the PR! + +- PR [#435](https://github.com/sendgrid/sendgrid-php/pull/435): Change spam_report() to spam_reports() in documentation and examples, thanks to [mrmxs](https://github.com/mrmxs) for the PR! + +- PR [#431](https://github.com/sendgrid/sendgrid-php/pull/431): Fixed minor typo during Mail creation, thanks to [joeldixon66](https://github.com/joeldixon66) for the PR! + +## [6.0.0] - 2017-06-30 ## +### BREAKING CHANGE +- PR #408: Update Mail constructor to signify which parameters are required for sending all email +- The `Mail()` constructor now requires `$from`, `$subject`, `$to` and `$content` parameters like so: `Mail($from, $subject, $to, $content)`. Those are the minimally required parameters to send an email. +- Thanks to [Casey Wilson](https://github.com/caseyw) for the PR! + +## [5.6.2] - 2017-06-29 ## +### Fix +- PR #410: Adding name for ReplyTo for issue #390 +- Thanks to [Casey Wilson](https://github.com/caseyw) for the PR! + +## [5.6.1] - 2017-06-26 ## +### Fix +- Versioning mistake (forgot the .0 at the end) + +## [5.6.0] - 2017-06-26 ## +### Added +- Pull #405: Updating docs and non-composer includes +- Thanks to [Casey Wilson](https://github.com/caseyw) for the PR! + +## [5.5.1] - 2017-05-18 ## +### Fixed +- Pull #396: Use `print_r` instead of `echo` on Arrays +- Thanks to [Ryan P.C. McQuen](https://github.com/ryanpcmcquen) for the PR! + +## [5.5.0] - 2017-05-04 ## +### Added +- Pull #393: Update [php-http-client](https://github.com/sendgrid/php-http-client) dependency +- [v3.6](https://github.com/sendgrid/php-http-client/releases/tag/v3.6.0): Pass the curlOptions to the client in buildClient +- [v3.7](https://github.com/sendgrid/php-http-client/releases/tag/v3.7.0): Added ability to get headers as associative array + +## [5.4.2] - 2017-04-18 ## +### Fixes +- Fixes #292 +- Removes Prism file in sendgrid-php.zip + +## [5.4.1] - 2017-04-04 ## +### Added +- Pull #373 +- PSR1 & PSR2 Conversion +- Thanks to [Braunson Yager](https://github.com/Braunson) for the PR! + +## [5.4.0] - 2017-03-16 ## +### Added +- Pull #337 +- API level addressing of the string-only custom arg rule +- Thanks to [Chris Schuld](https://github.com/cbschuld) for the PR! + +## [5.3.0] - 2017-03-15 ## +### Added +- Pull #367 +- UTF8 encoding forced for content value and message subject +- Thanks to [Chris Schuld](https://github.com/cbschuld) for the PR! + +## [5.2.3] - 2017-03-03 ## +### Fixed +- Pull #334 +- Fixed serialization of empty JSON objects, fixes #332 & #314 +- Thanks to [Matthew Dreyer](https://github.com/Dreyer) for the PR! + +## [5.2.2] - 2017-03-03 ## +### Fixed +- Pull #323 +- Typo 'user' for 'usr' +- Thanks to [Mike Ralphson](https://github.com/MikeRalphson) for the PR! + +## [5.2.1] - 2017-03-01 ## +### Fixed +- Pull #353 +- Fixed Issue #352 +- Relative path fix for background jobs +- Thanks to [Tarcísio Zotelli Ferraz](https://github.com/tarcisiozf) for the PR! + +## [5.2.0] - 2017-02-23 ## +### Added +- Pull #346 +- Allow passing curlOptions to the client +- Thanks to [Taluu](https://github.com/sendgrid/sendgrid-php/pull/346) for the PR! + +## [5.1.2] - 2016-10-11 ## +### Added +- Pull #330, Fixes #320 +- Delete subaccounts returns 200 issue resolved +- The fix happened at the [php-http-client](https://github.com/sendgrid/php-http-client/releases/tag/v3.5.1) dependency. +- Thanks to [emil](https://github.com/emilva) for the PR! + +## [5.1.1] - 2016-10-11 ## +### Added +- Pull #307, Fixes #276 +- Adds phpdoc and style fixes +- Thanks to [Avishkar Autar](https://github.com/aautar) for the PR! + +## [5.1.0] - 2016-09-29 ## +### Fixed +- Pull #295: [Upgrade sendgrid/php-http-client](https://github.com/sendgrid/sendgrid-php/pull/295/files) +- This adds getters for certain properties, please see [this pull request](https://github.com/sendgrid/php-http-client/pull/9) for details +- Thanks to [Arjan Keeman](https://github.com/akeeman) for the pull request! + +## [5.0.9] - 2016-09-13 ## +### Fixed +- Pull request #289: [Replace "\jsonSerializable" with "\JsonSerializable" ](https://github.com/sendgrid/sendgrid-php/pull/289) +- Thanks to [Issei.M](https://github.com/issei-m) for the pull request! + +## [5.0.8] - 2016-08-24 ## +### Added +- Table of Contents in the README +- Added a [USE_CASES.md](USE_CASES.md) section, with the first use case example for transactional templates + +## [5.0.7] - 2016-07-25 ## +### Added +- [Troubleshooting](TROUBLESHOOTING.md) section + +## [5.0.6] - 2016-07-20 ## +### Added +- README updates +- Update introduction blurb to include information regarding our forward path +- Update the v3 /mail/send example to include non-helper usage +- Update the generic v3 example to include non-fluent interface usage + +## [5.0.5] - 2016-07-12 ## +### Added +- Update docs, unit tests and examples to include Sender ID + +## [5.0.4] - 2016-07-07 ## +### Added +- Tests now mocked automatically against [prism](https://stoplight.io/prism/) + +## [5.0.3] - 2016-07-05 ## +### Updated +- Content based on our updated [Swagger/OAI doc](https://github.com/sendgrid/sendgrid-oai) + +## [5.0.2] - 2016-07-05 ## +### Added +- Accept: application/json header per https://sendgrid.com/docs/API_Reference/Web_API_v3/How_To_Use_The_Web_API_v3/requests.html + +### Updated +- Content based on our updated [Swagger/OAI doc](https://github.com/sendgrid/sendgrid-oai) + +## [5.0.1] - 2016-06-17 ## +### Fixed +- Issue with packaged version for non-composer uses + +## [5.0.0] - 2016-06-13 ## +### Added +- Breaking change to support the v3 Web API +- New HTTP client +- v3 Mail Send helper + +## [v4.0.4] - (2016-02-18) ## +### Added +- Ability to add scopes to API Keys endpoint [POST] + +## [v4.0.3] - (2016-02-18) ## +### Added +- API Keys endpoint [PUT] + +## [v4.0.2] - (2015-12-15) ## +### Added +- Tests for API Keys endpoint [POST, PATCH, DELETE] + +## [v4.0.1] - (2015-12-03) ## +### Fixed +- HTTP 406 Not Acceptable Errors [#177](https://github.com/sendgrid/sendgrid-php/issues/177) + +## [v4.0.0] - (2015-10-16) ## +### Added +- Added support for accessing the [SendGrid Web API v3 endpoints](https://sendgrid.com/docs/API_Reference/Web_API_v3/index.html) +- Implemented part of the /api_keys, /groups and /suppressions endpoints + +## [v3.2.0] - (2015-05-13) ## + +### Added +- Specify Guzzle proxy via [#149](https://github.com/sendgrid/sendgrid-php/pull/149) +- Option to disable exception raising + +## [v3.1.0] - (2015-04-27) +### Added +- Support for API keys + +## [v3.0.0] - (2015-04-14) +### Fixed +- CC and BCC not working with SMTPAPI To + +### Changed +- **Breaking:** A `\SendGrid\Exception` is now raised when response is not 200 +- **Breaking:** `addTo` now uses the Web API parameter as opposed to the SMTPAPI Header. Substitutions will most likely break unless you update to use `addSmtpapiTo` +- The library now depends on Guzzle3 +- Major refactoring + +### Added +- **Breaking:** `send()` now returns an instance of `\SendGrid\Response` +- Numerous missing methods for new functionality +- `addSmtpapiTo` for using the SMTPAPI To + +## [v2.2.1] - (2014-01-29) +### Fixed +- Fix turn_off_ssl_verification option via [#123](https://github.com/sendgrid/sendgrid-php/pull/123) + +## [v2.2.0] - (2014-01-12) +### Changed +- Remove [Unirest](https://github.com/Mashape/unirest-php/) and replace with native cURL +- Add CHANGELOG.md diff --git a/lib/sendgrid-php/CODE_OF_CONDUCT.md b/lib/sendgrid-php/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..2f0727ed5 --- /dev/null +++ b/lib/sendgrid-php/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# 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 open-source@twilio.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 diff --git a/lib/sendgrid-php/CONTRIBUTING.md b/lib/sendgrid-php/CONTRIBUTING.md new file mode 100644 index 000000000..37d2cb7c0 --- /dev/null +++ b/lib/sendgrid-php/CONTRIBUTING.md @@ -0,0 +1,160 @@ +Hello! Thank you for choosing to help contribute to one of the Twilio SendGrid open source libraries. There are many ways you can contribute and help is always welcome. We simply ask that you follow the following contribution policies. + +All third party contributors acknowledge that any contributions they provide will be made under the same open source license that the open source project is provided under. + +- [Improvements to the Codebase](#improvements-to-the-codebase) + - [Development Environment](#development-environment) + - [Install and Run Locally](#install-and-run-locally) + - [Prerequisites](#prerequisites) + - [Initial setup:](#initial-setup) + - [Environment Variables](#environment-variables) + - [Execute:](#execute) +- [Understanding the Code Base](#understanding-the-codebase) +- [Testing](#testing) +- [Style Guidelines & Naming Conventions](#style-guidelines--naming-conventions) +- [Creating a Pull Request](#creating-a-pull-request) +- [Code Reviews](#code-reviews) + +There are a few ways to contribute, which we'll enumerate below: + +## Improvements to the Codebase + +We welcome direct contributions to the sendgrid-php code base. Thank you! + +Please note that we utilize the [Gitflow Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) for Git to help keep project development organized and consistent. + +### Development Environment ### + +#### Install and Run Locally #### + +##### Prerequisites ##### + +- PHP version 7.3, 7.4, 8.0, or 8.1 + +##### Initial setup: ##### + +```bash +git clone https://github.com/sendgrid/sendgrid-php.git +cd sendgrid-php +composer install +``` + +### Environment Variables + +First, get your free Twilio SendGrid account [here](https://sendgrid.com/free?source=sendgrid-php). + +Next, update your environment with your [SENDGRID_API_KEY](https://app.sendgrid.com/settings/api_keys). + +```bash +echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env +echo "sendgrid.env" >> .gitignore +source ./sendgrid.env +``` + +##### Execute: ##### + +See the [examples folder](examples) or [README](README.md) to get started quickly. + +We prefer the use of the Composer autoloader by loading `vendor/autoload.php`. + +The examples will load `sendgrid-php.php` which is in the project root. This file verifies the existence of the Composer autoloader and warns you if dependencies are missing. + +## Understanding the Codebase + +**/examples** + +Working examples that demonstrate usage. + +```bash +php examples/example.php +``` + +**/test/unit** + +Unit tests for the HTTP client. + +**/test/integration** + +Unit tests for the HTTP client. + +**/lib** + +The interface to the Twilio SendGrid API. The subfolders are helpers. + +## Testing + +All PRs require passing tests before the PR will be reviewed. All test files are in the [`/test/unit`](test/unit) directory. For the purposes of contributing to this repo, please update or add relevant test files [here](test) with tests as you modify the code. + +The integration tests require a Twilio SendGrid mock API in order to execute. We've simplified setting this up using Docker to run the tests. You will just need [Docker Desktop](https://docs.docker.com/get-docker/) and `make`. + +Once these are available, simply execute the Docker test target to run all tests: `make test-docker`. This command can also be used to open an interactive shell into the container where this library is installed. To start a *bash* shell for example, use this command: `command=bash make test-docker`. + +## Style Guidelines & Naming Conventions + +Generally, we follow the style guidelines as suggested by the official language. However, we ask that you conform to the styles that already exist in the library. If you wish to deviate, please explain your reasoning. + +- [PSR2 Coding Standards](http://www.php-fig.org/psr/psr-2/) + +Please run your code through: + +- [PHP Code Sniffer](https://github.com/squizlabs/PHP_CodeSniffer) + +## Creating a Pull Request + +1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, + and configure the remotes: + + ```bash + # Clone your fork of the repo into the current directory + git clone https://github.com/sendgrid/sendgrid-php + + # Navigate to the newly cloned directory + cd sendgrid-php + + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com/sendgrid/sendgrid-php + ``` + +2. If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout + git pull upstream + ``` + +3. Create a new topic branch off the `development` branch to + contain your feature, change, or fix: + + ```bash + git checkout development + git checkout -b + ``` + +4. Commit your changes in logical chunks. Please adhere to these [git commit + message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) + or your code is unlikely to be merged into the main project. Use Git's + [interactive rebase](https://help.github.com/articles/interactive-rebase) + feature to tidy up your commits before making them public. + +4a. Create tests. + +4b. Create or update the example code that demonstrates the functionality of this change to the code. + +5. Locally merge (or rebase) the upstream `development` branch into your topic branch: + + ```bash + git pull [--rebase] upstream development + ``` + +6. Push your topic branch up to your fork: + + ```bash + git push origin + ``` + +7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) + with a clear title and description against the `development` branch. All tests must be passing before we will review the PR. + +## Code Reviews + +If you can, please look at open PRs and review them. Give feedback and help us merge these PRs much faster! If you don't know how, GitHub has some [great information on how to review a Pull Request](https://help.github.com/articles/about-pull-request-reviews/). diff --git a/lib/sendgrid-php/Dockerfile b/lib/sendgrid-php/Dockerfile new file mode 100644 index 000000000..8ad1cfd66 --- /dev/null +++ b/lib/sendgrid-php/Dockerfile @@ -0,0 +1,16 @@ +ARG version=latest +FROM php:$version + +RUN apt-get update \ + && apt-get install -y zip + +RUN curl -s https://getcomposer.org/installer | php \ + && mv composer.phar /usr/local/bin/composer + +COPY prism/prism/nginx/cert.crt /usr/local/share/ca-certificates/cert.crt +RUN update-ca-certificates + +WORKDIR /app +COPY . . + +RUN make install diff --git a/lib/sendgrid-php/FIRST_TIMERS.md b/lib/sendgrid-php/FIRST_TIMERS.md new file mode 100644 index 000000000..2b86e33a9 --- /dev/null +++ b/lib/sendgrid-php/FIRST_TIMERS.md @@ -0,0 +1,53 @@ +# How To Contribute to Twilio SendGrid Repositories via GitHub +Contributing to the Twilio SendGrid repositories is easy! All you need to do is find an open issue (see the bottom of this page for a list of repositories containing open issues), fix it and submit a pull request. Once you have submitted your pull request, the team can easily review it before it is merged into the repository. + +To make a pull request, follow these steps: + +1. Log into GitHub. If you do not already have a GitHub account, you will have to create one in order to submit a change. Click the Sign up link in the upper right-hand corner to create an account. Enter your username, password, and email address. If you are an employee of Twilio SendGrid, please use your full name with your GitHub account and enter Twilio SendGrid as your company so we can easily identify you. + + + +2. __[Fork](https://help.github.com/fork-a-repo/)__ the [sendgrid-php](https://github.com/sendgrid/sendgrid-php) repository: + + + +3. __Clone__ your fork via the following commands: + +```bash +# Clone your fork of the repo into the current directory +git clone https://github.com/your_username/sendgrid-php +# Navigate to the newly cloned directory +cd sendgrid-php +# Assign the original repo to a remote called "upstream" +git remote add upstream https://github.com/sendgrid/sendgrid-php +``` + +> Don't forget to replace *your_username* in the URL by your real GitHub username. + +4. __Create a new topic branch__ (off the main project development branch) to contain your feature, change, or fix: + +```bash +git checkout -b +``` + +5. __Commit your changes__ in logical chunks. + +Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) or your code is unlikely be merged into the main project. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public. Probably you will also have to create tests (if needed) or create or update the example code that demonstrates the functionality of this change to the code. + +6. __Locally merge (or rebase)__ the upstream development branch into your topic branch: + +```bash +git pull [--rebase] upstream main +``` + +7. __Push__ your topic branch up to your fork: + +```bash +git push origin +``` + +8. __[Open a Pull Request](https://help.github.com/articles/creating-a-pull-request/#changing-the-branch-range-and-destination-repository/)__ with a clear title and description against the `main` branch. All tests must be passing before we will review the PR. + +## Important notice + +Before creating a pull request, make sure that you respect the repository's constraints regarding contributions. You can find them in the [CONTRIBUTING.md](CONTRIBUTING.md) file. diff --git a/lib/sendgrid-php/ISSUE_TEMPLATE.md b/lib/sendgrid-php/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..49967612d --- /dev/null +++ b/lib/sendgrid-php/ISSUE_TEMPLATE.md @@ -0,0 +1,30 @@ + + +### Issue Summary +A summary of the issue and the environment in which it occurs. If suitable, include the steps required to reproduce the bug. Please feel free to include screenshots, screencasts, or code examples. + +### Steps to Reproduce +1. This is the first step +2. This is the second step +3. Further steps, etc. + +### Code Snippet +```php +# paste code here +``` + +### Exception/Log +``` +# paste exception/log here +``` + +### Technical details: +* sendgrid-php version: +* php version: + diff --git a/lib/sendgrid-php/LICENSE b/lib/sendgrid-php/LICENSE new file mode 100644 index 000000000..3154774a9 --- /dev/null +++ b/lib/sendgrid-php/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2023, Twilio SendGrid, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/sendgrid-php/Makefile b/lib/sendgrid-php/Makefile new file mode 100644 index 000000000..490d4fa81 --- /dev/null +++ b/lib/sendgrid-php/Makefile @@ -0,0 +1,37 @@ +.PHONY: clean install ci-install test test-integ test-docker bundle + +clean: + @rm -rf vendor composer.lock sendgrid-php.zip + +php_version = `php -v | head -n 1 | cut -d " " -f 2` + +install: clean +ifdef GIT_HUB_TOKEN + composer config -g github-oauth.github.com $(GIT_HUB_TOKEN) +endif + + composer install + +ifeq ($(dependencies), lowest) + composer update --prefer-lowest --prefer-stable -n +endif + +ci-install: clean + composer install --no-dev + +test: + vendor/bin/phpunit test/unit --filter test* + vendor/bin/phpcs lib/*/* + vendor/bin/phpstan analyse --memory-limit 1G --no-progress --level 1 lib test + +test-integ: test + vendor/bin/phpunit test --filter test* + +version ?= latest +test-docker: + curl -s https://raw.githubusercontent.com/sendgrid/sendgrid-oai/HEAD/prism/prism.sh -o prism.sh + dependencies=lowest version=$(version) bash ./prism.sh + dependencies=highest version=$(version) bash ./prism.sh + +bundle: ci-install + zip -r sendgrid-php.zip . -x \*.git\* \*composer.json\* \*scripts\* \*test\* \*prism\* diff --git a/lib/sendgrid-php/PULL_REQUEST_TEMPLATE.md b/lib/sendgrid-php/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..7844f2459 --- /dev/null +++ b/lib/sendgrid-php/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,31 @@ + + +# Fixes # + +A short description of what this PR does. + +### Checklist +- [x] I acknowledge that all my contributions will be made under the project's license +- [ ] I have made a material change to the repo (functionality, testing, spelling, grammar) +- [ ] I have read the [Contribution Guidelines](https://github.com/sendgrid/sendgrid-php/blob/main/CONTRIBUTING.md) and my PR follows them +- [ ] I have titled the PR appropriately +- [ ] I have updated my branch with the main branch +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] I have added the necessary documentation about the functionality in the appropriate .md file +- [ ] I have added inline documentation to the code I modified + +If you have questions, please file a [support ticket](https://support.sendgrid.com). diff --git a/lib/sendgrid-php/README.md b/lib/sendgrid-php/README.md new file mode 100644 index 000000000..5cadc94c2 --- /dev/null +++ b/lib/sendgrid-php/README.md @@ -0,0 +1,206 @@ +![SendGrid Logo](twilio_sendgrid_logo.png) + +[![BuildStatus](https://github.com/sendgrid/sendgrid-php/actions/workflows/test-and-deploy.yml/badge.svg)](https://github.com/sendgrid/sendgrid-php/actions/workflows/test-and-deploy.yml) +[![Packagist](https://img.shields.io/packagist/v/sendgrid/sendgrid.svg)](https://packagist.org/packages/sendgrid/sendgrid) +[![Downloads](https://img.shields.io/packagist/dt/sendgrid/sendgrid.svg?maxAge=3600)](https://packagist.org/packages/sendgrid/sendgrid) +[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) +[![Twitter Follow](https://img.shields.io/twitter/follow/sendgrid.svg?style=social&label=Follow)](https://twitter.com/sendgrid) +[![GitHub contributors](https://img.shields.io/github/contributors/sendgrid/sendgrid-php.svg)](https://github.com/sendgrid/sendgrid-php/graphs/contributors) +[![Open Source Helpers](https://www.codetriage.com/sendgrid/sendgrid-php/badges/users.svg)](https://www.codetriage.com/sendgrid/sendgrid-php) + +**NEW:** + +- Send SMS messages with [Twilio](USE_CASES.md#sms). + +**This library allows you to quickly and easily use the Twilio SendGrid Web API v3 via PHP.** + +Version 7.X.X of this library provides full support for all Twilio SendGrid [Web API v3](https://sendgrid.com/docs/API_Reference/Web_API_v3/index.html) endpoints, including the new [v3 /mail/send](https://sendgrid.com/blog/introducing-v3mailsend-sendgrids-new-mail-endpoint). + +**If you need support using SendGrid, please check the [Twilio SendGrid Support Help Center](https://support.sendgrid.com).** + +Please browse the rest of this README for further details. + +We appreciate your continued support, thank you! + +# Table of Contents + +* [Installation](#installation) +* [Quick Start](#quick-start) +* [Use Cases](#use-cases) +* [Usage](#usage) +* [Announcements](#announcements) +* [How to Contribute](#contribute) +* [Troubleshooting](#troubleshooting) +* [About](#about) +* [Support](#support) +* [License](#license) + + +# Installation + +## Prerequisites + +- PHP version 7.3, 7.4, 8.0, or 8.1 +- The Twilio SendGrid service, starting at the [free level](https://sendgrid.com/free?source=sendgrid-php) to send up to 40,000 emails for the first 30 days, then send 100 emails/day free forever or check out [our pricing](https://sendgrid.com/pricing?source=sendgrid-php). +- For SMS messages, you will need a free [Twilio account](https://www.twilio.com/try-twilio?source=sendgrid-php). + +## Setup Environment Variables + +Update the development environment with your [SENDGRID_API_KEY](https://app.sendgrid.com/settings/api_keys), for example: + +1. Copy the sample env file to a new file named `.env` +```bash +cp .env.sample .env +``` +2. Edit the `.env` file to include your `SENDGRID_API_KEY` +3. Source the `.env` file +```bash +source ./.env +``` + +## Install Package + +Add Twilio SendGrid to your `composer.json` file. If you are not using [Composer](http://getcomposer.org), we highly recommend it. It's an excellent way to manage dependencies in your PHP application. + +```json +{ + "require": { + "sendgrid/sendgrid": "~7" + } +} +``` + +### Alternative: Install package from zip + +If you are not using Composer, simply download and install the **[latest packaged release of the library as a zip](https://github.com/sendgrid/sendgrid-php/releases/download/8.1.2/sendgrid-php.zip)**. + +[**⬇︎ Download Packaged Library ⬇︎**](https://github.com/sendgrid/sendgrid-php/releases/download/8.1.2/sendgrid-php.zip) + +Previous versions of the library can be downloaded directly from [GitHub](https://github.com/sendgrid/sendgrid-php/releases). + +## Dependencies + +- The Twilio SendGrid Service, starting at the [free level](https://sendgrid.com/free?source=sendgrid-php) +- The dependency-free [php-http-client](https://github.com/sendgrid/php-http-client) + + +# Quick Start + +Include the proper lines from below at the top of each example based on your installation method: + +```php + with the path to the sendgrid-php.php file +// require_once '/sendgrid-php.php'; +``` + +## Hello Email + +The following is the minimum needed code to send an email. You may find more examples in our USE_CASES file: + +```php +$email = new \SendGrid\Mail\Mail(); +$email->setFrom("test@example.com", "Example User"); +$email->setSubject("Sending with Twilio SendGrid is Fun"); +$email->addTo("test@example.com", "Example User"); +$email->addContent("text/plain", "and easy to do anywhere, even with PHP"); +$email->addContent( + "text/html", "and easy to do anywhere, even with PHP" +); +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage() ."\n"; +} +``` + +The `SendGrid\Mail` constructor creates a [personalization object](https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/personalizations.html) for you. [Here](USE_CASES.md#kitchen-sink) is an example of how to add to it. + +## General v3 Web API Usage (With Fluent Interface) + +```php +$apiKey = getenv('SENDGRID_API_KEY'); +$sg = new \SendGrid($apiKey); + +try { + $response = $sg->client->suppression()->bounces()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + +## General v3 Web API Usage (Without Fluent Interface) + +```php +$apiKey = getenv('SENDGRID_API_KEY'); +$sg = new \SendGrid($apiKey); + +try { + $response = $sg->client->_("suppression/bounces")->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + + +# Use Cases + +[Examples of common API use cases](USE_CASES.md), such as how to send an email with a transactional template. + + +# Usage + +- [Twilio SendGrid Docs](https://sendgrid.com/docs/API_Reference/index.html) +- [Generic Library Usage + Documentation](USAGE.md) +- [Example Code](USE_CASES.md) + + +# Announcements + +v7 has been released! Please see the [release notes](https://github.com/sendgrid/sendgrid-php/releases/tag/v7.0.0) for details. + +All updates to this library are documented in our [CHANGELOG](CHANGELOG.md) and [releases](https://github.com/sendgrid/sendgrid-php/releases). + + +# How to Contribute + +We encourage contribution to our libraries (you might even score some nifty swag), please see our [CONTRIBUTING](CONTRIBUTING.md) guide for details. + +Quick links: + +- [Feature Request](CONTRIBUTING.md#feature_request) +- [Bug Reports](CONTRIBUTING.md#submit_a_bug_report) +- [Improvements to the Codebase](CONTRIBUTING.md#improvements_to_the_codebase) +- [Review Pull Requests](CONTRIBUTING.md#code-reviews) + + +# Troubleshooting + +Please see our [troubleshooting guide](TROUBLESHOOTING.md) for common library issues. + + +# About + +sendgrid-php is maintained and funded by Twilio SendGrid, Inc. The names and logos for sendgrid-php are trademarks of Twilio SendGrid, Inc. + + +# Support + +For product support, please check the [Twilio SendGrid Support Help Center](https://support.sendgrid.com). + +# License +[The MIT License (MIT)](LICENSE) diff --git a/lib/sendgrid-php/TROUBLESHOOTING.md b/lib/sendgrid-php/TROUBLESHOOTING.md new file mode 100644 index 000000000..3628ef549 --- /dev/null +++ b/lib/sendgrid-php/TROUBLESHOOTING.md @@ -0,0 +1,144 @@ +If you have an issue logging into your Twilio SendGrid account, please read this [document](https://sendgrid.com/docs/ui/account-and-settings/troubleshooting-login/). For any questions regarding login issues, please contact our [support team](https://support.sendgrid.com). + +If you have a non-library Twilio SendGrid issue, please contact our [support team](https://support.sendgrid.com). + +If you can't find a solution below, please open an [issue](https://github.com/sendgrid/sendgrid-php/issues). + +## Table of Contents + +- [Table of Contents](#table-of-contents) +- [Migrating from v2 to v3](#migrating-from-v2-to-v3) +- [Continue Using v2](#continue-using-v2) +- [Testing v3 /mail/send Calls Directly](#testing-v3-mailsend-calls-directly) +- [Error Messages](#error-messages) +- [Versions](#versions) +- [Environment Variables and Your Twilio SendGrid API Key](#environment-variables-and-your-twilio-sendgrid-api-key) +- [Using the Package Manager](#using-the-package-manager) +- [Fixing Error 415](#fixing-error-415) +- [Viewing the Request Body](#viewing-the-request-body) +- [Google App Engine installation](#google-app-engine-installation) +- [Verifying Event Webhooks](#signed-webhooks) + + +## Migrating from v2 to v3 + +In this context, we are referring to the version of the Twilio SendGrid API. + +Please review [our guide](https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/how_to_migrate_from_v2_to_v3_mail_send.html) on how to migrate from v2 to v3. + + +## Continue Using v2 + +In this context, we are referring to the version of the Twilio SendGrid API. + +[Here](https://github.com/sendgrid/sendgrid-php/releases/tag/v4.0.4) is the last working version with v2 support. + +Using composer: + +```json +{ + "require": { + "sendgrid/sendgrid": "~4.0.4" + } +} +``` + +Download packaged zip [here](https://sendgrid-open-source.s3.amazonaws.com/sendgrid-php/versions/sendgrid-php-75970eb.zip). + + +## Testing v3 /mail/send Calls Directly + +[Here](https://sendgrid.com/docs/for-developers/sending-email/curl-examples/) are some cURL examples for common use cases. + + +## Error Messages + +Failed requests will always return an error response, including a response code, a message explaining the reason for the error, and a link to any relevant documentation that may help you troubleshoot the problem. + +To read the error message returned by Twilio SendGrid's API: + +```php +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; // Twilio SendGrid specific errors are found here +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} +``` + +You may find complete documentation [here](https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/errors.html). + + +## Versions + +We follow the MAJOR.MINOR.PATCH versioning scheme as described by [SemVer.org](http://semver.org). Therefore, we recommend that you always pin (or vendor) the particular version you are working with to your code and never auto-update to the latest version. Especially when there is a MAJOR point release, since that is guaranteed to be a breaking change. Changes are documented in the [CHANGELOG](CHANGELOG.md) and [releases](https://github.com/sendgrid/sendgrid-php/releases) section. + + +## Environment Variables and Your Twilio SendGrid API Key + +All of our examples assume you are using [environment variables](https://github.com/sendgrid/sendgrid-php#setup-environment-variables) to hold your Twilio SendGrid API key. + +If you choose to add your Twilio SendGrid API key directly (not recommended): + +`$apiKey = getenv('SENDGRID_API_KEY');` + +becomes + +`$apiKey = 'SENDGRID_API_KEY';` + +In the first case SENDGRID_API_KEY is in reference to the name of the environment variable, while the second case references the actual Twilio SendGrid API Key. + + +## Using the Package Manager + +We upload this library to [Packagist](https://packagist.org/packages/sendgrid/sendgrid) whenever we make a release. This allows you to use [composer](https://getcomposer.org) for easy installation. + +In most cases we recommend you download the latest version of the library, but if you need a different version, please use: + +```json +{ + "require": { + "sendgrid/sendgrid": "~X.X.X" + } +} +``` + + +## Fixing Error 415 + +If you're getting the following error while using this library: + +`Content-Type should be application/json.` + +It is most likely due to a linebreak in your API key. Passing your key through `trim` should fix this: + +`$apiKey = trim($apiKey)` + + +## Viewing the Request Body + +When debugging or testing, it may be useful to examine the raw request body to compare against the [documented format](https://sendgrid.com/docs/API_Reference/api_v3.html). + +You can do this right before you call `$response = $sg->send($email);` like so: + +```php +echo json_encode($email, JSON_PRETTY_PRINT); +``` + + +## Google App Engine installation + +Please refer to [`USE_CASES.md`](USE_CASES.md#GAE-instructions) for additional instructions. + + +## Signed Webhook Verification + +Twilio SendGrid's Event Webhook will notify a URL via HTTP POST with information about events that occur as your mail is processed. [This](https://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook-security-features) article covers all you need to know to secure the Event Webhook, allowing you to verify that incoming requests originate from Twilio SendGrid. The sendgrid-php library can help you verify these Signed Event Webhooks. + +You can find the usage example [here](examples/helpers/eventwebhook/example.php) and the tests [here](test/unit/EventWebhookTest.php). +If you are still having trouble getting the validation to work, follow the following instructions: +- Be sure to use the *raw* payload for validation +- Be sure to include a trailing carriage return and newline in your payload +- In case of multi-event webhooks, make sure you include the trailing newline and carriage return after *each* event diff --git a/lib/sendgrid-php/UPGRADE.md b/lib/sendgrid-php/UPGRADE.md new file mode 100644 index 000000000..6de142cea --- /dev/null +++ b/lib/sendgrid-php/UPGRADE.md @@ -0,0 +1,8 @@ +# Upgrade Guide + +_MAJOR version bumps will have upgrade notes posted here._ + +[2022-05-04] 7.x.x to 8.x.x +--------------------------- + +### CHANGED - Drop support for PHP versions 5.6, 7.0, 7.1, and 7.2 which are EOL. diff --git a/lib/sendgrid-php/USAGE.md b/lib/sendgrid-php/USAGE.md new file mode 100644 index 000000000..713f25884 --- /dev/null +++ b/lib/sendgrid-php/USAGE.md @@ -0,0 +1,5127 @@ +This documentation is based on our [OAI specification](https://github.com/sendgrid/sendgrid-oai). + +# INITIALIZATION + +```php +/sendgrid-php.php'; + +$apiKey = getenv('SENDGRID_API_KEY'); +$sg = new \SendGrid($apiKey); +``` + +# Table of Contents + +* [ACCESS SETTINGS](#access-settings) +* [ALERTS](#alerts) +* [API KEYS](#api-keys) +* [ASM](#asm) +* [BROWSERS](#browsers) +* [CAMPAIGNS](#campaigns) +* [CATEGORIES](#categories) +* [CLIENTS](#clients) +* [CONTACTDB](#contactdb) +* [DEVICES](#devices) +* [EMAIL ACTIVITY](#email-activity) +* [GEO](#geo) +* [IPS](#ips) +* [MAIL](#mail) +* [MAIL SETTINGS](#mail-settings) +* [MAILBOX PROVIDERS](#mailbox-providers) +* [PARTNER SETTINGS](#partner-settings) +* [SCOPES](#scopes) +* [SENDERS](#senders) +* [SENDER AUTHENTICATION](#sender-authentication) +* [STATS](#stats) +* [SUBUSERS](#subusers) +* [SUPPRESSION](#suppression) +* [TEMPLATES](#templates) +* [TRACKING SETTINGS](#tracking-settings) +* [USER](#user) + + + +# ACCESS SETTINGS + +## Retrieve all recent access attempts + +**This endpoint allows you to retrieve a list of all of the IP addresses that recently attempted to access your account either through the User Interface or the API.** + +IP Access Management allows you to control which IP addresses can be used to access your account, either through the User Interface or the API. There is no limit to the number of IP addresses that you can add to your whitelist. It is possible to remove your own IP address from the whitelist, thus preventing yourself from accessing your account. + +For more information, please see our [User Guide](http://sendgrid.com/docs/User_Guide/Settings/ip_access_management.html). + +### GET /access_settings/activity + + +```php +$query_params = json_decode('{"limit": 1}'); +$response = $sg->client->access_settings()->activity()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Add one or more IPs to the whitelist + +**This endpoint allows you to add one or more IP addresses to your IP whitelist.** + +When adding an IP to your whitelist, include the IP address in an array. You can whitelist one IP at a time, or you can whitelist multiple IPs at once. + +IP Access Management allows you to control which IP addresses can be used to access your account, either through the User Interface or the API. There is no limit to the number of IP addresses that you can add to your whitelist. It is possible to remove your own IP address from the whitelist, thus preventing yourself from accessing your account. + +For more information, please see our [User Guide](http://sendgrid.com/docs/User_Guide/Settings/ip_access_management.html). + +### POST /access_settings/whitelist + + +```php +$request_body = json_decode('{ + "ips": [ + { + "ip": "192.168.1.1" + }, + { + "ip": "192.*.*.*" + }, + { + "ip": "192.168.1.3/32" + } + ] +}'); +$response = $sg->client->access_settings()->whitelist()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a list of currently whitelisted IPs + +**This endpoint allows you to retrieve a list of IP addresses that are currently whitelisted.** + +IP Access Management allows you to control which IP addresses can be used to access your account, either through the User Interface or the API. There is no limit to the number of IP addresses that you can add to your whitelist. It is possible to remove your own IP address from the whitelist, thus preventing yourself from accessing your account. + +For more information, please see our [User Guide](http://sendgrid.com/docs/User_Guide/Settings/ip_access_management.html). + +### GET /access_settings/whitelist + + +```php +$response = $sg->client->access_settings()->whitelist()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Remove one or more IPs from the whitelist + +**This endpoint allows you to remove one or more IPs from your IP whitelist.** + +You can remove one IP at a time, or you can remove multiple IP addresses. + +IP Access Management allows you to control which IP addresses can be used to access your account, either through the User Interface or the API. There is no limit to the number of IP addresses that you can add to your whitelist. It is possible to remove your own IP address from the whitelist, thus preventing yourself from accessing your account. + +For more information, please see our [User Guide](http://sendgrid.com/docs/User_Guide/Settings/ip_access_management.html). + +### DELETE /access_settings/whitelist + + +```php +$request_body = json_decode('{ + "ids": [ + 1, + 2, + 3 + ] +}'); +$response = $sg->client->access_settings()->whitelist()->delete($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a specific whitelisted IP + +**This endpoint allows you to retrieve a specific IP address that has been whitelisted.** + +You must include the ID for the specific IP address you want to retrieve in your call. + +IP Access Management allows you to control which IP addresses can be used to access your account, either through the User Interface or the API. There is no limit to the number of IP addresses that you can add to your whitelist. It is possible to remove your own IP address from the whitelist, thus preventing yourself from accessing your account. + +For more information, please see our [User Guide](http://sendgrid.com/docs/User_Guide/Settings/ip_access_management.html). + +### GET /access_settings/whitelist/{rule_id} + + +```php +$rule_id = "test_url_param"; +$response = $sg->client->access_settings()->whitelist()->_($rule_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Remove a specific IP from the whitelist + +**This endpoint allows you to remove a specific IP address from your IP whitelist.** + +When removing a specific IP address from your whitelist, you must include the ID in your call. + +IP Access Management allows you to control which IP addresses can be used to access your account, either through the User Interface or the API. There is no limit to the number of IP addresses that you can add to your whitelist. It is possible to remove your own IP address from the whitelist, thus preventing yourself from accessing your account. + +For more information, please see our [User Guide](http://sendgrid.com/docs/User_Guide/Settings/ip_access_management.html). + +### DELETE /access_settings/whitelist/{rule_id} + + +```php +$rule_id = "test_url_param"; +$response = $sg->client->access_settings()->whitelist()->_($rule_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# ALERTS + +## Create a new Alert + +**This endpoint allows you to create a new alert.** + +Alerts allow you to specify an email address to receive notifications regarding your email usage or statistics. +* Usage alerts allow you to set the threshold at which an alert will be sent. +* Stats notifications allow you to set how frequently you would like to receive email statistics reports. For example, "daily", "weekly", or "monthly". + +For more information about alerts, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/alerts.html). + +### POST /alerts + + +```php +$request_body = json_decode('{ + "email_to": "example@example.com", + "frequency": "daily", + "type": "stats_notification" +}'); +$response = $sg->client->alerts()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all alerts + +**This endpoint allows you to retrieve all of your alerts.** + +Alerts allow you to specify an email address to receive notifications regarding your email usage or statistics. +* Usage alerts allow you to set the threshold at which an alert will be sent. +* Stats notifications allow you to set how frequently you would like to receive email statistics reports. For example, "daily", "weekly", or "monthly". + +For more information about alerts, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/alerts.html). + +### GET /alerts + + +```php +$response = $sg->client->alerts()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update an alert + +**This endpoint allows you to update an alert.** + +Alerts allow you to specify an email address to receive notifications regarding your email usage or statistics. +* Usage alerts allow you to set the threshold at which an alert will be sent. +* Stats notifications allow you to set how frequently you would like to receive email statistics reports. For example, "daily", "weekly", or "monthly". + +For more information about alerts, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/alerts.html). + +### PATCH /alerts/{alert_id} + + +```php +$request_body = json_decode('{ + "email_to": "example@example.com" +}'); +$alert_id = "test_url_param"; +$response = $sg->client->alerts()->_($alert_id)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a specific alert + +**This endpoint allows you to retrieve a specific alert.** + +Alerts allow you to specify an email address to receive notifications regarding your email usage or statistics. +* Usage alerts allow you to set the threshold at which an alert will be sent. +* Stats notifications allow you to set how frequently you would like to receive email statistics reports. For example, "daily", "weekly", or "monthly". + +For more information about alerts, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/alerts.html). + +### GET /alerts/{alert_id} + + +```php +$alert_id = "test_url_param"; +$response = $sg->client->alerts()->_($alert_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete an alert + +**This endpoint allows you to delete an alert.** + +Alerts allow you to specify an email address to receive notifications regarding your email usage or statistics. +* Usage alerts allow you to set the threshold at which an alert will be sent. +* Stats notifications allow you to set how frequently you would like to receive email statistics reports. For example, "daily", "weekly", or "monthly". + +For more information about alerts, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/alerts.html). + +### DELETE /alerts/{alert_id} + + +```php +$alert_id = "test_url_param"; +$response = $sg->client->alerts()->_($alert_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# API KEYS + +## Create API keys + +**This endpoint allows you to create a new random API Key for the user.** + +A JSON request body containing a "name" property is required. If a number of maximum keys is reached, HTTP 403 will be returned. + +There is a limit of 100 API Keys on your account. + +The API Keys feature allows customers to be able to generate an API Key credential which can be used for authentication with the Twilio SendGrid v3 Web API or the [Mail API Endpoint](https://sendgrid.com/docs/API_Reference/Web_API/mail.html). + +See the [API Key Permissions List](https://sendgrid.com/docs/API_Reference/Web_API_v3/API_Keys/api_key_permissions_list.html) for a list of all available scopes. + +### POST /api_keys + + +```php +$request_body = json_decode('{ + "name": "My API Key", + "sample": "data", + "scopes": [ + "mail.send", + "alerts.create", + "alerts.read" + ] +}'); +$response = $sg->client->api_keys()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all API Keys belonging to the authenticated user + +**This endpoint allows you to retrieve all API Keys that belong to the authenticated user.** + +The API Keys feature allows customers to be able to generate an API Key credential which can be used for authentication with the Twilio SendGrid v3 Web API or the [Mail API Endpoint](https://sendgrid.com/docs/API_Reference/Web_API/mail.html). + +### GET /api_keys + + +```php +$query_params = json_decode('{"limit": 1}'); +$response = $sg->client->api_keys()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update the name & scopes of an API Key + +**This endpoint allows you to update the name and scopes of a given API key.** + +A JSON request body with a "name" property is required. +Most provide the list of all the scopes an API key should have. + +The API Keys feature allows customers to be able to generate an API Key credential which can be used for authentication with the Twilio SendGrid v3 Web API or the [Mail API Endpoint](https://sendgrid.com/docs/API_Reference/Web_API/mail.html). + + +### PUT /api_keys/{api_key_id} + + +```php +$request_body = json_decode('{ + "name": "A New Hope", + "scopes": [ + "user.profile.read", + "user.profile.update" + ] +}'); +$api_key_id = "test_url_param"; +$response = $sg->client->api_keys()->_($api_key_id)->put($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update API keys + +**This endpoint allows you to update the name of an existing API Key.** + +A JSON request body with a "name" property is required. + +The API Keys feature allows customers to be able to generate an API Key credential which can be used for authentication with the Twilio SendGrid v3 Web API or the [Mail API Endpoint](https://sendgrid.com/docs/API_Reference/Web_API/mail.html). + +## URI Parameters + +| URI Parameter | Type | Required? | Description | +|---|---|---|---| +|api_key_id |string | required | The ID of the API Key you are updating.| + +### PATCH /api_keys/{api_key_id} + + +```php +$request_body = json_decode('{ + "name": "A New Hope" +}'); +$api_key_id = "test_url_param"; +$response = $sg->client->api_keys()->_($api_key_id)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve an existing API Key + +**This endpoint allows you to retrieve a single API key.** + +If the API Key ID does not exist an HTTP 404 will be returned. + +### GET /api_keys/{api_key_id} + + +```php +$api_key_id = "test_url_param"; +$response = $sg->client->api_keys()->_($api_key_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete API keys + +**This endpoint allows you to revoke an existing API Key** + +Authentications using this API Key will fail after this request is made, with some small propagation delay. If the API Key ID does not exist an HTTP 404 will be returned. + +The API Keys feature allows customers to be able to generate an API Key credential which can be used for authentication with the Twilio SendGrid v3 Web API or the [Mail API Endpoint](https://sendgrid.com/docs/API_Reference/Web_API/mail.html). + +## URI Parameters + +| URI Parameter | Type | Required? | Description | +|---|---|---|---| +|api_key_id |string | required | The ID of the API Key you are deleting.| + +### DELETE /api_keys/{api_key_id} + + +```php +$api_key_id = "test_url_param"; +$response = $sg->client->api_keys()->_($api_key_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# ASM + +## Create a new suppression group + +**This endpoint allows you to create a new suppression group.** + +Suppression groups, or unsubscribe groups, are specific types or categories of email that you would like your recipients to be able to unsubscribe from. For example: Daily Newsletters, Invoices, System Alerts. + +The **name** and **description** of the unsubscribe group will be visible by recipients when they are managing their subscriptions. + +Each user can create up to 25 different suppression groups. + +### POST /asm/groups + + +```php +$request_body = json_decode('{ + "description": "Suggestions for products our users might like.", + "is_default": true, + "name": "Product Suggestions" +}'); +$response = $sg->client->asm()->groups()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve information about multiple suppression groups + +**This endpoint allows you to retrieve information about multiple suppression groups.** + +This endpoint will return information for each group ID that you include in your request. To add a group ID to your request, simply append `&id=` followed by the group ID. + +Suppressions are a list of email addresses that will not receive content sent under a given [group](https://sendgrid.com/docs/API_Reference/Web_API_v3/Suppression_Management/groups.html). + +Suppression groups, or [unsubscribe groups](https://sendgrid.com/docs/API_Reference/Web_API_v3/Suppression_Management/groups.html), allow you to label a category of content that you regularly send. This gives your recipients the ability to opt out of a specific set of your email. For example, you might define a group for your transactional email, and one for your marketing email so that your users can continue receiving your transactional email without having to receive your marketing content. + +### GET /asm/groups + + +```php +$query_params = json_decode('{"id": 1}'); +$response = $sg->client->asm()->groups()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update a suppression group. + +**This endpoint allows you to update or change a suppression group.** + +Suppression groups, or unsubscribe groups, are specific types or categories of email that you would like your recipients to be able to unsubscribe from. For example: Daily Newsletters, Invoices, System Alerts. + +The **name** and **description** of the unsubscribe group will be visible by recipients when they are managing their subscriptions. + +Each user can create up to 25 different suppression groups. + +### PATCH /asm/groups/{group_id} + + +```php +$request_body = json_decode('{ + "description": "Suggestions for items our users might like.", + "id": 103, + "name": "Item Suggestions" +}'); +$group_id = "test_url_param"; +$response = $sg->client->asm()->groups()->_($group_id)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Get information on a single suppression group. + +**This endpoint allows you to retrieve a single suppression group.** + +Suppression groups, or unsubscribe groups, are specific types or categories of email that you would like your recipients to be able to unsubscribe from. For example: Daily Newsletters, Invoices, System Alerts. + +The **name** and **description** of the unsubscribe group will be visible by recipients when they are managing their subscriptions. + +Each user can create up to 25 different suppression groups. + +### GET /asm/groups/{group_id} + + +```php +$group_id = "test_url_param"; +$response = $sg->client->asm()->groups()->_($group_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a suppression group. + +**This endpoint allows you to delete a suppression group.** + +You can only delete groups that have not been attached to sent mail in the last 60 days. If a recipient uses the "one-click unsubscribe" option on an email associated with a deleted group, that recipient will be added to the global suppression list. + +Suppression groups, or unsubscribe groups, are specific types or categories of email that you would like your recipients to be able to unsubscribe from. For example: Daily Newsletters, Invoices, System Alerts. + +The **name** and **description** of the unsubscribe group will be visible by recipients when they are managing their subscriptions. + +Each user can create up to 25 different suppression groups. + +### DELETE /asm/groups/{group_id} + + +```php +$group_id = "test_url_param"; +$response = $sg->client->asm()->groups()->_($group_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Add suppressions to a suppression group + +**This endpoint allows you to add email addresses to an unsubscribe group.** + +If you attempt to add suppressions to a group that has been deleted or does not exist, the suppressions will be added to the global suppressions list. + +Suppressions are recipient email addresses that are added to [unsubscribe groups](https://sendgrid.com/docs/API_Reference/Web_API_v3/Suppression_Management/groups.html). Once a recipient's address is on the suppressions list for an unsubscribe group, they will not receive any emails that are tagged with that unsubscribe group. + +### POST /asm/groups/{group_id}/suppressions + + +```php +$request_body = json_decode('{ + "recipient_emails": [ + "test1@example.com", + "test2@example.com" + ] +}'); +$group_id = "test_url_param"; +$response = $sg->client->asm()->groups()->_($group_id)->suppressions()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all suppressions for a suppression group + +**This endpoint allows you to retrieve all suppressed email addresses belonging to the given group.** + +Suppressions are recipient email addresses that are added to [unsubscribe groups](https://sendgrid.com/docs/API_Reference/Web_API_v3/Suppression_Management/groups.html). Once a recipient's address is on the suppressions list for an unsubscribe group, they will not receive any emails that are tagged with that unsubscribe group. + +### GET /asm/groups/{group_id}/suppressions + + +```php +$group_id = "test_url_param"; +$response = $sg->client->asm()->groups()->_($group_id)->suppressions()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Search for suppressions within a group + +**This endpoint allows you to search a suppression group for multiple suppressions.** + +When given a list of email addresses and a group ID, this endpoint will return only the email addresses that have been unsubscribed from the given group. + +Suppressions are a list of email addresses that will not receive content sent under a given [group](https://sendgrid.com/docs/API_Reference/Web_API_v3/Suppression_Management/groups.html). + +### POST /asm/groups/{group_id}/suppressions/search + + +```php +$request_body = json_decode('{ + "recipient_emails": [ + "exists1@example.com", + "exists2@example.com", + "doesnotexists@example.com" + ] +}'); +$group_id = "test_url_param"; +$response = $sg->client->asm()->groups()->_($group_id)->suppressions()->search()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a suppression from a suppression group + +**This endpoint allows you to remove a suppressed email address from the given suppression group.** + +Suppressions are recipient email addresses that are added to [unsubscribe groups](https://sendgrid.com/docs/API_Reference/Web_API_v3/Suppression_Management/groups.html). Once a recipient's address is on the suppressions list for an unsubscribe group, they will not receive any emails that are tagged with that unsubscribe group. + +### DELETE /asm/groups/{group_id}/suppressions/{email} + + +```php +$group_id = "test_url_param"; +$email = "test_url_param"; +$response = $sg->client->asm()->groups()->_($group_id)->suppressions()->_($email)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all suppressions + +**This endpoint allows you to retrieve a list of all suppressions.** + +Suppressions are a list of email addresses that will not receive content sent under a given [group](https://sendgrid.com/docs/API_Reference/Web_API_v3/Suppression_Management/groups.html). + +### GET /asm/suppressions + + +```php +$response = $sg->client->asm()->suppressions()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Add recipient addresses to the global suppression group. + +**This endpoint allows you to add one or more email addresses to the global suppressions group.** + +A global suppression (or global unsubscribe) is an email address of a recipient who does not want to receive any of your messages. A globally suppressed recipient will be removed from any email you send. For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/global_unsubscribes.html). + +### POST /asm/suppressions/global + + +```php +$request_body = json_decode('{ + "recipient_emails": [ + "test1@example.com", + "test2@example.com" + ] +}'); +$response = $sg->client->asm()->suppressions()->global()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a Global Suppression + +**This endpoint allows you to retrieve a global suppression. You can also use this endpoint to confirm if an email address is already globally suppressed.** + +If the email address you include in the URL path parameter `{email}` is already globally suppressed, the response will include that email address. If the address you enter for `{email}` is not globally suppressed, an empty JSON object `{}` will be returned. + +A global suppression (or global unsubscribe) is an email address of a recipient who does not want to receive any of your messages. A globally suppressed recipient will be removed from any email you send. For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/global_unsubscribes.html). + +### GET /asm/suppressions/global/{email} + + +```php +$email = "test_url_param"; +$response = $sg->client->asm()->suppressions()->global()->_($email)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a Global Suppression + +**This endpoint allows you to remove an email address from the global suppressions group.** + +A global suppression (or global unsubscribe) is an email address of a recipient who does not want to receive any of your messages. A globally suppressed recipient will be removed from any email you send. For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/global_unsubscribes.html). + +### DELETE /asm/suppressions/global/{email} + + +```php +$email = "test_url_param"; +$response = $sg->client->asm()->suppressions()->global()->_($email)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all suppression groups for an email address + +**This endpoint returns the list of all groups that the given email address has been unsubscribed from.** + +Suppressions are a list of email addresses that will not receive content sent under a given [group](https://sendgrid.com/docs/API_Reference/Web_API_v3/Suppression_Management/groups.html). + +### GET /asm/suppressions/{email} + + +```php +$email = "test_url_param"; +$response = $sg->client->asm()->suppressions()->_($email)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# BROWSERS + +## Retrieve email statistics by a browser. + +**This endpoint allows you to retrieve your email statistics segmented by browser type.** + +**We only store up to 7 days of email activity in our database.** By default, 500 items will be returned per request via the Advanced Stats API endpoints. + +Advanced Stats provide a more in-depth view of your email statistics and the actions taken by your recipients. You can segment these statistics by geographic location, device type, client type, browser, and mailbox provider. For more information about statistics, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/index.html). + +### GET /browsers/stats + + +```php +$query_params = json_decode('{"end_date": "2016-04-01", "aggregated_by": "day", "browsers": "test_string", "limit": "test_string", "offset": "test_string", "start_date": "2016-01-01"}'); +$response = $sg->client->browsers()->stats()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# CAMPAIGNS + +## Create a Campaign + +**This endpoint allows you to create a campaign.** + +Our Marketing Campaigns API lets you create, manage, send, and schedule campaigns. + +Note: In order to send or schedule the campaign, you will be required to provide a subject, sender ID, content (we suggest both HTML and plain text), and at least one list or segment ID. This information is not required when you create a campaign. + +For more information: + +* [User Guide > Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) + +### POST /campaigns + + +```php +$request_body = json_decode('{ + "categories": [ + "spring line" + ], + "custom_unsubscribe_url": "", + "html_content": "

Check out our spring line!

", + "ip_pool": "marketing", + "list_ids": [ + 110, + 124 + ], + "plain_content": "Check out our spring line!", + "segment_ids": [ + 110 + ], + "sender_id": 124451, + "subject": "New Products for Spring!", + "suppression_group_id": 42, + "title": "March Newsletter" +}'); +$response = $sg->client->campaigns()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all Campaigns + +**This endpoint allows you to retrieve a list of all of your campaigns.** + +Returns campaigns in reverse order they were created (newest first). + +Returns an empty array if no campaigns exist. + +For more information: + +* [User Guide > Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) + +### GET /campaigns + + +```php +$query_params = json_decode('{"limit": 1, "offset": 1}'); +$response = $sg->client->campaigns()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update a Campaign + +Update a campaign. This is especially useful if you only set up the campaign using POST /campaigns, but didn't set many of the parameters. + +For more information: + +* [User Guide > Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) + +### PATCH /campaigns/{campaign_id} + + +```php +$request_body = json_decode('{ + "categories": [ + "summer line" + ], + "html_content": "

Check out our summer line!

", + "plain_content": "Check out our summer line!", + "subject": "New Products for Summer!", + "title": "May Newsletter" +}'); +$campaign_id = "test_url_param"; +$response = $sg->client->campaigns()->_($campaign_id)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a single campaign + +**This endpoint allows you to retrieve a specific campaign.** + +Our Marketing Campaigns API lets you create, manage, send, and schedule campaigns. + +For more information: + +* [User Guide > Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) + +### GET /campaigns/{campaign_id} + + +```php +$campaign_id = "test_url_param"; +$response = $sg->client->campaigns()->_($campaign_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a Campaign + +**This endpoint allows you to delete a specific campaign.** + +Our Marketing Campaigns API lets you create, manage, send, and schedule campaigns. + +For more information: + +* [User Guide > Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) + +### DELETE /campaigns/{campaign_id} + + +```php +$campaign_id = "test_url_param"; +$response = $sg->client->campaigns()->_($campaign_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update a Scheduled Campaign + +**This endpoint allows to you change the scheduled time and date for a campaign to be sent.** + +For more information: + +* [User Guide > Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) + +### PATCH /campaigns/{campaign_id}/schedules + + +```php +$request_body = json_decode('{ + "send_at": 1489451436 +}'); +$campaign_id = "test_url_param"; +$response = $sg->client->campaigns()->_($campaign_id)->schedules()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Schedule a Campaign + +**This endpoint allows you to schedule a specific date and time for your campaign to be sent.** + +For more information: + +* [User Guide > Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) + +### POST /campaigns/{campaign_id}/schedules + + +```php +$request_body = json_decode('{ + "send_at": 1489771528 +}'); +$campaign_id = "test_url_param"; +$response = $sg->client->campaigns()->_($campaign_id)->schedules()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## View Scheduled Time of a Campaign + +**This endpoint allows you to retrieve the date and time that the given campaign has been scheduled to be sent.** + +For more information: + +* [User Guide > Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) + +### GET /campaigns/{campaign_id}/schedules + + +```php +$campaign_id = "test_url_param"; +$response = $sg->client->campaigns()->_($campaign_id)->schedules()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Unschedule a Scheduled Campaign + +**This endpoint allows you to unschedule a campaign that has already been scheduled to be sent.** + +A successful unschedule will return a 204. +If the specified campaign is in the process of being sent, the only option is to cancel (a different method). + +For more information: + +* [User Guide > Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) + +### DELETE /campaigns/{campaign_id}/schedules + + +```php +$campaign_id = "test_url_param"; +$response = $sg->client->campaigns()->_($campaign_id)->schedules()->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Send a Campaign + +**This endpoint allows you to immediately send a campaign at the time you make the API call.** + +Normally a POST would have a request body, but since this endpoint is telling us to send a resource that is already created, a request body is not needed. + +For more information: + +* [User Guide > Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) + +### POST /campaigns/{campaign_id}/schedules/now + + +```php +$campaign_id = "test_url_param"; +$response = $sg->client->campaigns()->_($campaign_id)->schedules()->now()->post(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Send a Test Campaign + +**This endpoint allows you to send a test campaign.** + +To send to multiple addresses, use an array for the JSON "to" value ["one@address", "two@address"] + +For more information: + +* [User Guide > Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) + +### POST /campaigns/{campaign_id}/schedules/test + + +```php +$request_body = json_decode('{ + "to": "your.email@example.com" +}'); +$campaign_id = "test_url_param"; +$response = $sg->client->campaigns()->_($campaign_id)->schedules()->test()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# CATEGORIES + +## Retrieve all categories + +**This endpoint allows you to retrieve a list of all of your categories.** + +Categories can help organize your email analytics by enabling you to tag emails by type or broad topic. You can define your own custom categories. For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/categories.html). + +### GET /categories + + +```php +$query_params = json_decode('{"category": "test_string", "limit": 1, "offset": 1}'); +$response = $sg->client->categories()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve Email Statistics for Categories + +**This endpoint allows you to retrieve all of your email statistics for each of your categories.** + +If you do not define any query parameters, this endpoint will return a sum for each category in groups of 10. + +Categories allow you to group your emails together according to broad topics that you define. For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/categories.html). + +### GET /categories/stats + + +```php +$query_params = json_decode('{"end_date": "2016-04-01", "aggregated_by": "day", "limit": 1, "offset": 1, "start_date": "2016-01-01", "categories": "test_string"}'); +$response = $sg->client->categories()->stats()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve sums of email stats for each category [Needs: Stats object defined, has category ID?] + +**This endpoint allows you to retrieve the total sum of each email statistic for every category over the given date range.** + +If you do not define any query parameters, this endpoint will return a sum for each category in groups of 10. + +Categories allow you to group your emails together according to broad topics that you define. For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/categories.html). + +Note: Category statistics are available for the previous thirteen months only. + +### GET /categories/stats/sums + + +```php +$query_params = json_decode('{"end_date": "2016-04-01", "aggregated_by": "day", "limit": 1, "sort_by_metric": "test_string", "offset": 1, "start_date": "2016-01-01", "sort_by_direction": "asc"}'); +$response = $sg->client->categories()->stats()->sums()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# CLIENTS + +## Retrieve email statistics by client type. + +**This endpoint allows you to retrieve your email statistics segmented by client type.** + +**We only store up to 7 days of email activity in our database.** By default, 500 items will be returned per request via the Advanced Stats API endpoints. + +Advanced Stats provide a more in-depth view of your email statistics and the actions taken by your recipients. You can segment these statistics by geographic location, device type, client type, browser, and mailbox provider. For more information about statistics, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/index.html). + +### GET /clients/stats + + +```php +$query_params = json_decode('{"aggregated_by": "day", "start_date": "2016-01-01", "end_date": "2016-04-01"}'); +$response = $sg->client->clients()->stats()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve stats by a specific client type. + +**This endpoint allows you to retrieve your email statistics segmented by a specific client type.** + +**We only store up to 7 days of email activity in our database.** By default, 500 items will be returned per request via the Advanced Stats API endpoints. + +## Available Client Types +- phone +- tablet +- webmail +- desktop + +Advanced Stats provide a more in-depth view of your email statistics and the actions taken by your recipients. You can segment these statistics by geographic location, device type, client type, browser, and mailbox provider. For more information about statistics, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/index.html). + +### GET /clients/{client_type}/stats + + +```php +$query_params = json_decode('{"aggregated_by": "day", "start_date": "2016-01-01", "end_date": "2016-04-01"}'); +$client_type = "test_url_param"; +$response = $sg->client->clients()->_($client_type)->stats()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# CONTACTDB + +## Create a Custom Field + +**This endpoint allows you to create a custom field.** + +The contactdb is a database of your contacts for [Twilio SendGrid Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html). + +### POST /contactdb/custom_fields + + +```php +$request_body = json_decode('{ + "name": "pet", + "type": "text" +}'); +$response = $sg->client->contactdb()->custom_fields()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all custom fields + +**This endpoint allows you to retrieve all custom fields.** + +The contactdb is a database of your contacts for [Twilio SendGrid Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html). + +### GET /contactdb/custom_fields + + +```php +$response = $sg->client->contactdb()->custom_fields()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a Custom Field + +**This endpoint allows you to retrieve a custom field by ID.** + +The contactdb is a database of your contacts for [Twilio SendGrid Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html). + +### GET /contactdb/custom_fields/{custom_field_id} + + +```php +$custom_field_id = "test_url_param"; +$response = $sg->client->contactdb()->custom_fields()->_($custom_field_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a Custom Field + +**This endpoint allows you to delete a custom field by ID.** + +The contactdb is a database of your contacts for [Twilio SendGrid Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html). + +### DELETE /contactdb/custom_fields/{custom_field_id} + + +```php +$custom_field_id = "test_url_param"; +$response = $sg->client->contactdb()->custom_fields()->_($custom_field_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Create a List + +**This endpoint allows you to create a list for your recipients.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### POST /contactdb/lists + + +```php +$request_body = json_decode('{ + "name": "your list name" +}'); +$response = $sg->client->contactdb()->lists()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all lists + +**This endpoint allows you to retrieve all of your recipient lists. If you don't have any lists, an empty array will be returned.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### GET /contactdb/lists + + +```php +$response = $sg->client->contactdb()->lists()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete Multiple lists + +**This endpoint allows you to delete multiple recipient lists.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### DELETE /contactdb/lists + + +```php +$request_body = json_decode('[ + 1, + 2, + 3, + 4 +]'); +$response = $sg->client->contactdb()->lists()->delete($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update a List + +**This endpoint allows you to update the name of one of your recipient lists.** + + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### PATCH /contactdb/lists/{list_id} + + +```php +$request_body = json_decode('{ + "name": "newlistname" +}'); +$query_params = json_decode('{"list_id": 1}'); +$list_id = "test_url_param"; +$response = $sg->client->contactdb()->lists()->_($list_id)->patch($request_body, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a single list + +This endpoint allows you to retrieve a single recipient list. + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### GET /contactdb/lists/{list_id} + + +```php +$query_params = json_decode('{"list_id": 1}'); +$list_id = "test_url_param"; +$response = $sg->client->contactdb()->lists()->_($list_id)->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a List + +**This endpoint allows you to delete a specific recipient list with the given ID.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### DELETE /contactdb/lists/{list_id} + + +```php +$query_params = json_decode('{"delete_contacts": "true"}'); +$list_id = "test_url_param"; +$response = $sg->client->contactdb()->lists()->_($list_id)->delete(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Add Multiple Recipients to a List + +**This endpoint allows you to add multiple recipients to a list.** + +Adds existing recipients to a list, passing in the recipient IDs to add. Recipient IDs should be passed exactly as they are returned from recipient endpoints. + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### POST /contactdb/lists/{list_id}/recipients + + +```php +$request_body = json_decode('[ + "recipient_id1", + "recipient_id2" +]'); +$list_id = "test_url_param"; +$response = $sg->client->contactdb()->lists()->_($list_id)->recipients()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all recipients on a List + +**This endpoint allows you to retrieve all recipients on the list with the given ID.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### GET /contactdb/lists/{list_id}/recipients + + +```php +$query_params = json_decode('{"page": 1, "page_size": 1}'); +$list_id = "test_url_param"; +$response = $sg->client->contactdb()->lists()->_($list_id)->recipients()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Add a Single Recipient to a List + +**This endpoint allows you to add a single recipient to a list.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### POST /contactdb/lists/{list_id}/recipients/{recipient_id} + + +```php +$list_id = "test_url_param"; +$recipient_id = "test_url_param"; +$response = $sg->client->contactdb()->lists()->_($list_id)->recipients()->_($recipient_id)->post(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a Single Recipient from a Single List + +**This endpoint allows you to delete a single recipient from a list.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### DELETE /contactdb/lists/{list_id}/recipients/{recipient_id} + + +```php +$query_params = json_decode('{"recipient_id": 1, "list_id": 1}'); +$list_id = "test_url_param"; +$recipient_id = "test_url_param"; +$response = $sg->client->contactdb()->lists()->_($list_id)->recipients()->_($recipient_id)->delete(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update Recipient + +**This endpoint allows you to update one or more recipients.** + +The body of an API call to this endpoint must include an array of one or more recipient objects. + +It is of note that you can add custom field data as parameters on recipient objects. We have provided an example using some of the default custom fields Twilio SendGrid provides. + +The contactdb is a database of your contacts for [Twilio SendGrid Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html). + +### PATCH /contactdb/recipients + + +```php +$request_body = json_decode('[ + { + "email": "jones@example.com", + "first_name": "Guy", + "last_name": "Jones" + } +]'); +$response = $sg->client->contactdb()->recipients()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Add recipients + +**This endpoint allows you to add a Marketing Campaigns recipient.** + +It is of note that you can add custom field data as a parameter on this endpoint. We have provided an example using some of the default custom fields Twilio SendGrid provides. + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### POST /contactdb/recipients + + +```php +$request_body = json_decode('[ + { + "age": 25, + "email": "example@example.com", + "first_name": "", + "last_name": "User" + }, + { + "age": 25, + "email": "example2@example.com", + "first_name": "Example", + "last_name": "User" + } +]'); +$response = $sg->client->contactdb()->recipients()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve recipients + +**This endpoint allows you to retrieve all of your Marketing Campaigns recipients.** + +Batch deletion of a page makes it possible to receive an empty page of recipients before reaching the end of +the list of recipients. To avoid this issue; iterate over pages until a 404 is retrieved. + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### GET /contactdb/recipients + + +```php +$query_params = json_decode('{"page": 1, "page_size": 1}'); +$response = $sg->client->contactdb()->recipients()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete Recipient + +**This endpoint allows you to delete one or more recipients.** + +The body of an API call to this endpoint must include an array of recipient IDs of the recipients you want to delete. + +The contactdb is a database of your contacts for [Twilio SendGrid Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html). + +### DELETE /contactdb/recipients + + +```php +$request_body = json_decode('[ + "recipient_id1", + "recipient_id2" +]'); +$response = $sg->client->contactdb()->recipients()->delete($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve the count of billable recipients + +**This endpoint allows you to retrieve the number of Marketing Campaigns recipients that you will be billed for.** + +You are billed for marketing campaigns based on the highest number of recipients you have had in your account at one time. This endpoint will allow you to know the current billable count value. + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### GET /contactdb/recipients/billable_count + + +```php +$response = $sg->client->contactdb()->recipients()->billable_count()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a Count of Recipients + +**This endpoint allows you to retrieve the total number of Marketing Campaigns recipients.** + +The contactdb is a database of your contacts for [Twilio SendGrid Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html). + +### GET /contactdb/recipients/count + + +```php +$response = $sg->client->contactdb()->recipients()->count()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve recipients matching search criteria + +**This endpoint allows you to perform a search on all of your Marketing Campaigns recipients.** + +field_name: + +* is a variable that is substituted for your actual custom field name from your recipient. +* Text fields must be url-encoded. Date fields are searchable only by UNIX timestamp (e.g. 2/2/2015 becomes 1422835200) +* If field_name is a 'reserved' date field, such as created_at or updated_at, the system will internally convert +your epoch time to a date range encompassing the entire day. For example, an epoch time of 1422835600 converts to +Mon, 02 Feb 2015 00:06:40 GMT, but internally the system will search from Mon, 02 Feb 2015 00:00:00 GMT through +Mon, 02 Feb 2015 23:59:59 GMT. + +The contactdb is a database of your contacts for [Twilio SendGrid Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html). + +### GET /contactdb/recipients/search + + +```php +$query_params = json_decode('{"{field_name}": "test_string"}'); +$response = $sg->client->contactdb()->recipients()->search()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve recipients matching search criteria using segment conditions + +**This endpoint allows you to perform a search using segment conditions on all of your Marketing Campaigns recipients.** + +Valid operators for search depend on the type of the field you are segmenting: + +* **Dates:** + * "eq", "ne", "lt" (before), "gt" (after) + * You may use MM/DD/YYYY for day granularity or an epoch for second granularity. + * "empty", "not_empty" + * "is within" + * You may use an ISO 8601 date format or the # of days. +* **Text:** "contains", "eq" (is - matches the full field), "ne" (is not - matches any field where the entire field is not the condition value), "empty", "not_empty" +* **Numbers:** "eq", "lt", "gt", "empty", "not_empty" +* **Email Clicks and Opens:** "eq" (opened), "ne" (not opened) + +Field values must all be a string. + +Search conditions using "eq" or "ne" for email clicks and opens should provide a "field" of either *clicks.campaign_identifier* or *opens.campaign_identifier*. The condition value should be a string containing the id of a completed campaign. + +Search conditions list may contain multiple conditions, joined by an "and" or "or" in the "and_or" field. The first condition in the conditions list must have an empty "and_or", and subsequent conditions must all specify an "and_or". + +The contactdb is a database of your contacts for [Twilio SendGrid Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html). + +### POST /contactdb/recipients/search + + +```php +$query_params = json_decode('{"page": 1, "page_size": 1}'); +$request_body = json_decode('{ + "conditions": [ + { + "and_or": "", + "field": "last_name", + "operator": "eq", + "value": "Miller" + }, + { + "and_or": "and", + "field": "last_clicked", + "operator": "gt", + "value": "01/02/2015" + }, + { + "and_or": "or", + "field": "clicks.campaign_identifier", + "operator": "eq", + "value": "513" + } + ], +}'); +$response = $sg->client->contactdb()->recipients()->search()->post($request_body, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a single recipient + +**This endpoint allows you to retrieve a single recipient by ID from your contact database.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### GET /contactdb/recipients/{recipient_id} + + +```php +$recipient_id = "test_url_param"; +$response = $sg->client->contactdb()->recipients()->_($recipient_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a Recipient + +**This endpoint allows you to delete a single recipient with the given ID from your contact database.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### DELETE /contactdb/recipients/{recipient_id} + + +```php +$recipient_id = "test_url_param"; +$response = $sg->client->contactdb()->recipients()->_($recipient_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve the lists that a recipient is on + +**This endpoint allows you to retrieve the lists that a given recipient belongs to.** + +Each recipient can be on many lists. This endpoint gives you all of the lists that any one recipient has been added to. + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +### GET /contactdb/recipients/{recipient_id}/lists + + +```php +$recipient_id = "test_url_param"; +$response = $sg->client->contactdb()->recipients()->_($recipient_id)->lists()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve reserved fields + +**This endpoint allows you to list all fields that are reserved and can't be used for custom field names.** + +The contactdb is a database of your contacts for [Twilio SendGrid Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html). + +### GET /contactdb/reserved_fields + + +```php +$response = $sg->client->contactdb()->reserved_fields()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Create a Segment + +**This endpoint allows you to create a segment.** + +All recipients in your contactdb will be added or removed automatically depending on whether they match the criteria for this segment. + +List Id: + +* Send this to segment from an existing list +* Don't send this in order to segment from your entire contactdb. + +Valid operators for create and update depend on the type of the field you are segmenting: + +* **Dates:** + * "eq", "ne", "lt" (before), "gt" (after) + * You may use MM/DD/YYYY for day granularity or an epoch for second granularity. + * "empty", "not_empty" + * "is within" + * You may use an ISO 8601 date format or the # of days. +* **Text:** "contains", "eq" (is - matches the full field), "ne" (is not - matches any field where the entire field is not the condition value), "empty", "not_empty" +* **Numbers:** "eq", "lt", "gt", "empty", "not_empty" +* **Email Clicks and Opens:** "eq" (opened), "ne" (not opened) + +Field values must all be a string. + +Segment conditions using "eq" or "ne" for email clicks and opens should provide a "field" of either *clicks.campaign_identifier* or *opens.campaign_identifier*. The condition value should be a string containing the id of a completed campaign. + +Segments may contain multiple conditions, joined by an "and" or "or" in the "and_or" field. The first condition in the conditions list must have an empty "and_or", and subsequent conditions must all specify an "and_or". + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +For more information about segments in Marketing Campaigns, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/lists.html#-Create-a-Segment). + +### POST /contactdb/segments + + +```php +$request_body = json_decode('{ + "conditions": [ + { + "and_or": "", + "field": "last_name", + "operator": "eq", + "value": "Miller" + }, + { + "and_or": "and", + "field": "last_clicked", + "operator": "gt", + "value": "01/02/2015" + }, + { + "and_or": "or", + "field": "clicks.campaign_identifier", + "operator": "eq", + "value": "513" + } + ], + "list_id": 4, + "name": "Last Name Miller" +}'); +$response = $sg->client->contactdb()->segments()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all segments + +**This endpoint allows you to retrieve all of your segments.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +For more information about segments in Marketing Campaigns, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/lists.html#-Create-a-Segment). + +### GET /contactdb/segments + + +```php +$response = $sg->client->contactdb()->segments()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update a segment + +**This endpoint allows you to update a segment.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +For more information about segments in Marketing Campaigns, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/lists.html#-Create-a-Segment). + +### PATCH /contactdb/segments/{segment_id} + + +```php +$request_body = json_decode('{ + "conditions": [ + { + "and_or": "", + "field": "last_name", + "operator": "eq", + "value": "Miller" + } + ], + "list_id": 5, + "name": "The Millers" +}'); +$query_params = json_decode('{"segment_id": "test_string"}'); +$segment_id = "test_url_param"; +$response = $sg->client->contactdb()->segments()->_($segment_id)->patch($request_body, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a segment + +**This endpoint allows you to retrieve a single segment with the given ID.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +For more information about segments in Marketing Campaigns, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/lists.html#-Create-a-Segment). + +### GET /contactdb/segments/{segment_id} + + +```php +$query_params = json_decode('{"segment_id": 1}'); +$segment_id = "test_url_param"; +$response = $sg->client->contactdb()->segments()->_($segment_id)->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a segment + +**This endpoint allows you to delete a segment from your recipients database.** + +You also have the option to delete all the contacts from your Marketing Campaigns recipient database who were in this segment. + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +For more information about segments in Marketing Campaigns, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/lists.html#-Create-a-Segment). + +### DELETE /contactdb/segments/{segment_id} + + +```php +$query_params = json_decode('{"delete_contacts": "true"}'); +$segment_id = "test_url_param"; +$response = $sg->client->contactdb()->segments()->_($segment_id)->delete(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve recipients on a segment + +**This endpoint allows you to retrieve all of the recipients in a segment with the given ID.** + +The Contacts API helps you manage your [Marketing Campaigns](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/index.html) recipients. + +For more information about segments in Marketing Campaigns, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/lists.html#-Create-a-Segment). + +### GET /contactdb/segments/{segment_id}/recipients + + +```php +$query_params = json_decode('{"page": 1, "page_size": 1}'); +$segment_id = "test_url_param"; +$response = $sg->client->contactdb()->segments()->_($segment_id)->recipients()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# DEVICES + +## Retrieve email statistics by device type. + +**This endpoint allows you to retrieve your email statistics segmented by the device type.** + +**We only store up to 7 days of email activity in our database.** By default, 500 items will be returned per request via the Advanced Stats API endpoints. + +## Available Device Types +| **Device** | **Description** | **Example** | +|---|---|---| +| Desktop | Email software on a desktop computer. | I.E., Outlook, Sparrow, or Apple Mail. | +| Webmail | A web-based email client. | I.E., Yahoo, Google, AOL, or Outlook.com. | +| Phone | A smartphone. | iPhone, Android, Blackberry, etc. +| Tablet | A tablet computer. | iPad, Android based tablet, etc. | +| Other | An unrecognized device. | + +Advanced Stats provide a more in-depth view of your email statistics and the actions taken by your recipients. You can segment these statistics by geographic location, device type, client type, browser, and mailbox provider. For more information about statistics, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/index.html). + +### GET /devices/stats + + +```php +$query_params = json_decode('{"aggregated_by": "day", "limit": 1, "start_date": "2016-01-01", "end_date": "2016-04-01", "offset": 1}'); +$response = $sg->client->devices()->stats()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + + +# EMAIL ACTIVITY + +## Getting Started With the Email Activity Feed API + +**This endpoint allows you to query your messages.** + +Email Activity Feed allows you to search and download a CSV of your recent email event activity. For more information on how to get started, please see our [Getting Started Guide](https://sendgrid.com/docs/API_Reference/Web_API_v3/Tutorials/getting_started_email_activity_api.html). + + +# GEO + +## Retrieve email statistics by country and state/province. + +**This endpoint allows you to retrieve your email statistics segmented by country and state/province.** + +**We only store up to 7 days of email activity in our database.** By default, 500 items will be returned per request via the Advanced Stats API endpoints. + +Advanced Stats provide a more in-depth view of your email statistics and the actions taken by your recipients. You can segment these statistics by geographic location, device type, client type, browser, and mailbox provider. For more information about statistics, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/index.html). + +### GET /geo/stats + + +```php +$query_params = json_decode('{"end_date": "2016-04-01", "country": "US", "aggregated_by": "day", "limit": 1, "offset": 1, "start_date": "2016-01-01"}'); +$response = $sg->client->geo()->stats()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# IPS + +## Retrieve all IP addresses + +**This endpoint allows you to retrieve a list of all assigned and unassigned IPs.** + +The response includes warm-up status, pools, assigned subusers, and authentication info. The start_date field corresponds to when warmup started for that IP. + +A single IP address or a range of IP addresses may be dedicated to an account in order to send email for multiple domains. The reputation of this IP is based on the aggregate performance of all the senders who use it. + +### GET /ips + + +```php +$query_params = json_decode('{"subuser": "test_string", "ip": "test_string", "limit": 1, "exclude_whitelabels": "true", "offset": 1}'); +$response = $sg->client->ips()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all assigned IPs + +**This endpoint allows you to retrieve only assigned IP addresses.** + +A single IP address or a range of IP addresses may be dedicated to an account in order to send email for multiple domains. The reputation of this IP is based on the aggregate performance of all the senders who use it. + +### GET /ips/assigned + + +```php +$response = $sg->client->ips()->assigned()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Create an IP pool. + +**This endpoint allows you to create an IP pool.** + +**Each user can create up to 10 different IP pools.** + +IP Pools allow you to group your dedicated Twilio SendGrid IP addresses together. For example, you could create separate pools for your transactional and marketing email. When sending marketing emails, specify that you want to use the marketing IP pool. This allows you to maintain separate reputations for your different email traffic. + +IP pools can only be used with authenticated IP addresses. + +If an IP pool is NOT specified for an email, it will use any IP available, including ones in pools. + +### POST /ips/pools + + +```php +$request_body = json_decode('{ + "name": "marketing" +}'); +$response = $sg->client->ips()->pools()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all IP pools. + +**This endpoint allows you to retrieve all of your IP pools.** + +IP Pools allow you to group your dedicated Twilio SendGrid IP addresses together. For example, you could create separate pools for your transactional and marketing email. When sending marketing emails, specify that you want to use the marketing IP pool. This allows you to maintain separate reputations for your different email traffic. + +IP pools can only be used with authenticated IP addresses. + +If an IP pool is NOT specified for an email, it will use any IP available, including ones in pools. + +### GET /ips/pools + + +```php +$response = $sg->client->ips()->pools()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update an IP pools name. + +**This endpoint allows you to update the name of an IP pool.** + +IP Pools allow you to group your dedicated Twilio SendGrid IP addresses together. For example, you could create separate pools for your transactional and marketing email. When sending marketing emails, specify that you want to use the marketing IP pool. This allows you to maintain separate reputations for your different email traffic. + +IP pools can only be used with authenticated IP addresses. + +If an IP pool is NOT specified for an email, it will use any IP available, including ones in pools. + +### PUT /ips/pools/{pool_name} + + +```php +$request_body = json_decode('{ + "name": "new_pool_name" +}'); +$pool_name = "test_url_param"; +$response = $sg->client->ips()->pools()->_($pool_name)->put($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all IPs in a specified pool. + +**This endpoint allows you to list all of the IP addresses that are in a specific IP pool.** + +IP Pools allow you to group your dedicated Twilio SendGrid IP addresses together. For example, you could create separate pools for your transactional and marketing email. When sending marketing emails, specify that you want to use the marketing IP pool. This allows you to maintain separate reputations for your different email traffic. + +IP pools can only be used with authenticated IP addresses. + +If an IP pool is NOT specified for an email, it will use any IP available, including ones in pools. + +### GET /ips/pools/{pool_name} + + +```php +$pool_name = "test_url_param"; +$response = $sg->client->ips()->pools()->_($pool_name)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete an IP pool. + +**This endpoint allows you to delete an IP pool.** + +IP Pools allow you to group your dedicated Twilio SendGrid IP addresses together. For example, you could create separate pools for your transactional and marketing email. When sending marketing emails, specify that you want to use the marketing IP pool. This allows you to maintain separate reputations for your different email traffic. + +IP pools can only be used with authenticated IP addresses. + +If an IP pool is NOT specified for an email, it will use any IP available, including ones in pools. + +### DELETE /ips/pools/{pool_name} + + +```php +$pool_name = "test_url_param"; +$response = $sg->client->ips()->pools()->_($pool_name)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Add an IP address to a pool + +**This endpoint allows you to add an IP address to an IP pool.** + +You can add the same IP address to multiple pools. It may take up to 60 seconds for your IP address to be added to a pool after your request is made. + +A single IP address or a range of IP addresses may be dedicated to an account in order to send email for multiple domains. The reputation of this IP is based on the aggregate performance of all the senders who use it. + +### POST /ips/pools/{pool_name}/ips + + +```php +$request_body = json_decode('{ + "ip": "0.0.0.0" +}'); +$pool_name = "test_url_param"; +$response = $sg->client->ips()->pools()->_($pool_name)->ips()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Remove an IP address from a pool. + +**This endpoint allows you to remove an IP address from an IP pool.** + +The same IP address can be added to multiple IP pools. + +A single IP address or a range of IP addresses may be dedicated to an account in order to send email for multiple domains. The reputation of this IP is based on the aggregate performance of all the senders who use it. + +### DELETE /ips/pools/{pool_name}/ips/{ip} + + +```php +$pool_name = "test_url_param"; +$ip = "test_url_param"; +$response = $sg->client->ips()->pools()->_($pool_name)->ips()->_($ip)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Add an IP to warmup + +**This endpoint allows you to enter an IP address into warmup mode.** + +Twilio SendGrid can automatically warm up dedicated IP addresses by limiting the amount of mail that can be sent through them per hour, with the limit determined by how long the IP address has been in warmup. See the [warmup schedule](https://sendgrid.com/docs/API_Reference/Web_API_v3/IP_Management/ip_warmup_schedule.html) for more details on how Twilio SendGrid limits your email traffic for IPs in warmup. + +For more general information about warming up IPs, please see our [Classroom](https://sendgrid.com/docs/Classroom/Deliver/Delivery_Introduction/warming_up_ips.html). + +### POST /ips/warmup + + +```php +$request_body = json_decode('{ + "ip": "0.0.0.0" +}'); +$response = $sg->client->ips()->warmup()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all IPs currently in warmup + +**This endpoint allows you to retrieve all of your IP addresses that are currently warming up.** + +Twilio SendGrid can automatically warm up dedicated IP addresses by limiting the amount of mail that can be sent through them per hour, with the limit determined by how long the IP address has been in warmup. See the [warmup schedule](https://sendgrid.com/docs/API_Reference/Web_API_v3/IP_Management/ip_warmup_schedule.html) for more details on how Twilio SendGrid limits your email traffic for IPs in warmup. + +For more general information about warming up IPs, please see our [Classroom](https://sendgrid.com/docs/Classroom/Deliver/Delivery_Introduction/warming_up_ips.html). + +### GET /ips/warmup + + +```php +$response = $sg->client->ips()->warmup()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve warmup status for a specific IP address + +**This endpoint allows you to retrieve the warmup status for a specific IP address.** + +Twilio SendGrid can automatically warm up dedicated IP addresses by limiting the amount of mail that can be sent through them per hour, with the limit determined by how long the IP address has been in warmup. See the [warmup schedule](https://sendgrid.com/docs/API_Reference/Web_API_v3/IP_Management/ip_warmup_schedule.html) for more details on how Twilio SendGrid limits your email traffic for IPs in warmup. + +For more general information about warming up IPs, please see our [Classroom](https://sendgrid.com/docs/Classroom/Deliver/Delivery_Introduction/warming_up_ips.html). + +### GET /ips/warmup/{ip_address} + + +```php +$ip_address = "test_url_param"; +$response = $sg->client->ips()->warmup()->_($ip_address)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Remove an IP from warmup + +**This endpoint allows you to remove an IP address from warmup mode.** + +Twilio SendGrid can automatically warm up dedicated IP addresses by limiting the amount of mail that can be sent through them per hour, with the limit determined by how long the IP address has been in warmup. See the [warmup schedule](https://sendgrid.com/docs/API_Reference/Web_API_v3/IP_Management/ip_warmup_schedule.html) for more details on how Twilio SendGrid limits your email traffic for IPs in warmup. + +For more general information about warming up IPs, please see our [Classroom](https://sendgrid.com/docs/Classroom/Deliver/Delivery_Introduction/warming_up_ips.html). + +### DELETE /ips/warmup/{ip_address} + + +```php +$ip_address = "test_url_param"; +$response = $sg->client->ips()->warmup()->_($ip_address)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all IP pools an IP address belongs to + +**This endpoint allows you to see which IP pools a particular IP address has been added to.** + +The same IP address can be added to multiple IP pools. + +A single IP address or a range of IP addresses may be dedicated to an account in order to send email for multiple domains. The reputation of this IP is based on the aggregate performance of all the senders who use it. + +### GET /ips/{ip_address} + + +```php +$ip_address = "test_url_param"; +$response = $sg->client->ips()->_($ip_address)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# MAIL + +## Create a batch ID + +**This endpoint allows you to generate a new batch ID. This batch ID can be associated with scheduled sends via the mail/send endpoint.** + +If you set the SMTPAPI header `batch_id`, it allows you to then associate multiple scheduled mail/send requests together with the same ID. Then at anytime up to 10 minutes before the scheduled date, you can cancel all of the mail/send requests that have this batch ID by calling the Cancel Scheduled Send endpoint. + +More Information: + +* [Scheduling Parameters > Batch ID](https://sendgrid.com/docs/API_Reference/SMTP_API/scheduling_parameters.html) + +### POST /mail/batch + + +```php +$response = $sg->client->mail()->batch()->post(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Validate batch ID + +**This endpoint allows you to validate a batch ID.** + +If you set the SMTPAPI header `batch_id`, it allows you to then associate multiple scheduled mail/send requests together with the same ID. Then at anytime up to 10 minutes before the scheduled date, you can cancel all of the mail/send requests that have this batch ID by calling the Cancel Scheduled Send endpoint. + +More Information: + +* [Scheduling Parameters > Batch ID](https://sendgrid.com/docs/API_Reference/SMTP_API/scheduling_parameters.html) + +### GET /mail/batch/{batch_id} + + +```php +$batch_id = "test_url_param"; +$response = $sg->client->mail()->batch()->_($batch_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## v3 Mail Send + +This endpoint allows you to send the email over Twilio SendGrid's v3 Web API, the most recent version of our API. If you are looking for documentation about the v2 Mail Send endpoint, please see our [v2 API Reference](https://sendgrid.com/docs/API_Reference/Web_API/mail.html). + +* Top level parameters are referred to as "global". +* Individual fields within the personalizations array will override any other global, or message level, parameters that are defined outside of personalizations. + +* Note: bypass_bounce_management, bypass_spam_management, and bypass_unsubscribe_management cannot +* be combined with bypass_list_management + +For an overview of the v3 Mail Send endpoint, please visit our [v3 API Reference](https://sendgrid.com/docs/API_Reference/Web_API_v3/Mail/index.html) + +For more detailed information about how to use the v3 Mail Send endpoint, please visit our [Classroom](https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/index.html). + +### POST /mail/send + +This endpoint has a helper, check it out [here](lib/helpers/mail/README.md). + +```php +$request_body = json_decode('{ + "asm": { + "group_id": 1, + "groups_to_display": [ + 1, + 2, + 3 + ] + }, + "attachments": [ + { + "content": "[BASE64 encoded content block here]", + "content_id": "ii_139db99fdb5c3704", + "disposition": "inline", + "filename": "file1.jpg", + "name": "file1", + "type": "jpg" + } + ], + "batch_id": "[YOUR BATCH ID GOES HERE]", + "categories": [ + "category1", + "category2" + ], + "content": [ + { + "type": "text/html", + "value": "

Hello, world!

" + } + ], + "custom_args": { + "New Argument 1": "New Value 1", + "activationAttempt": "1", + "customerAccountNumber": "[CUSTOMER ACCOUNT NUMBER GOES HERE]" + }, + "from": { + "email": "sam.smith@example.com", + "name": "Sam Smith" + }, + "headers": {}, + "ip_pool_name": "[YOUR POOL NAME GOES HERE]", + "mail_settings": { + "bcc": { + "email": "ben.doe@example.com", + "enable": true + }, + "bypass_bounce_management": { + "enable": true + }, + "bypass_list_management": { + "enable": true + }, + "bypass_spam_management": { + "enable": true + }, + "bypass_unsubscribe_management": { + "enable": true + }, + + "footer": { + "enable": true, + "html": "

Thanks
The Twilio SendGrid Team

", + "text": "Thanks,/n The Twilio SendGrid Team" + }, + "sandbox_mode": { + "enable": false + }, + "spam_check": { + "enable": true, + "post_to_url": "http://example.com/compliance", + "threshold": 3 + } + }, + "personalizations": [ + { + "bcc": [ + { + "email": "sam.doe@example.com", + "name": "Sam Doe" + } + ], + "cc": [ + { + "email": "jane.doe@example.com", + "name": "Jane Doe" + } + ], + "custom_args": { + "New Argument 1": "New Value 1", + "activationAttempt": "1", + "customerAccountNumber": "[CUSTOMER ACCOUNT NUMBER GOES HERE]" + }, + "headers": { + "X-Accept-Language": "en", + "X-Mailer": "MyApp" + }, + "send_at": 1409348513, + "subject": "Hello, World!", + "substitutions": { + "id": "substitutions", + "type": "object" + }, + "to": [ + { + "email": "john.doe@example.com", + "name": "John Doe" + } + ] + } + ], + "reply_to": { + "email": "sam.smith@example.com", + "name": "Sam Smith" + }, + "sections": { + "section": { + ":sectionName1": "section 1 text", + ":sectionName2": "section 2 text" + } + }, + "send_at": 1409348513, + "subject": "Hello, World!", + "template_id": "[YOUR TEMPLATE ID GOES HERE]", + "tracking_settings": { + "click_tracking": { + "enable": true, + "enable_text": true + }, + "ganalytics": { + "enable": true, + "utm_campaign": "[NAME OF YOUR REFERRER SOURCE]", + "utm_content": "[USE THIS SPACE TO DIFFERENTIATE YOUR EMAIL FROM ADS]", + "utm_medium": "[NAME OF YOUR MARKETING MEDIUM e.g. email]", + "utm_name": "[NAME OF YOUR CAMPAIGN]", + "utm_term": "[IDENTIFY PAID KEYWORDS HERE]" + }, + "open_tracking": { + "enable": true, + "substitution_tag": "%opentrack" + }, + "subscription_tracking": { + "enable": true, + "html": "If you would like to unsubscribe and stop receiving these emails <% clickhere %>.", + "substitution_tag": "<%click here%>", + "text": "If you would like to unsubscribe and stop receiving these emails <% click here %>." + } + } +}'); +$response = $sg->client->mail()->send()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# MAIL SETTINGS + +## Retrieve all mail settings + +**This endpoint allows you to retrieve a list of all mail settings.** + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### GET /mail_settings + + +```php +$query_params = json_decode('{"limit": 1, "offset": 1}'); +$response = $sg->client->mail_settings()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update address whitelist mail settings + +**This endpoint allows you to update your current email address whitelist settings.** + +The address whitelist setting whitelists a specified email address or domain for which mail should never be suppressed. For example, you own the domain example.com, and one or more of your recipients use email@example.com addresses, by placing example.com in the address whitelist setting, all bounces, blocks, and unsubscribes logged for that domain will be ignored and sent as if under normal sending conditions. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### PATCH /mail_settings/address_whitelist + + +```php +$request_body = json_decode('{ + "enabled": true, + "list": [ + "email1@example.com", + "example.com" + ] +}'); +$response = $sg->client->mail_settings()->address_whitelist()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve address whitelist mail settings + +**This endpoint allows you to retrieve your current email address whitelist settings.** + +The address whitelist setting whitelists a specified email address or domain for which mail should never be suppressed. For example, you own the domain example.com, and one or more of your recipients use email@example.com addresses, by placing example.com in the address whitelist setting, all bounces, blocks, and unsubscribes logged for that domain will be ignored and sent as if under normal sending conditions. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### GET /mail_settings/address_whitelist + + +```php +$response = $sg->client->mail_settings()->address_whitelist()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update BCC mail settings + +**This endpoint allows you to update your current BCC mail settings.** + +When the BCC mail setting is enabled, Twilio SendGrid will automatically send a blind carbon copy (BCC) to an address for every email sent without adding that address to the header. Please note that only one email address may be entered in this field. If you wish to distribute BCCs to multiple addresses you will need to create a distribution group or use forwarding rules. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### PATCH /mail_settings/bcc + + +```php +$request_body = json_decode('{ + "email": "email@example.com", + "enabled": false +}'); +$response = $sg->client->mail_settings()->bcc()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all BCC mail settings + +**This endpoint allows you to retrieve your current BCC mail settings.** + +When the BCC mail setting is enabled, Twilio SendGrid will automatically send a blind carbon copy (BCC) to an address for every email sent without adding that address to the header. Please note that only one email address may be entered in this field. If you wish to distribute BCCs to multiple addresses you will need to create a distribution group or use forwarding rules. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### GET /mail_settings/bcc + + +```php +$response = $sg->client->mail_settings()->bcc()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update bounce purge mail settings + +**This endpoint allows you to update your current bounce purge settings.** + +This setting allows you to set a schedule for Twilio SendGrid to automatically delete contacts from your soft and hard bounce suppression lists. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### PATCH /mail_settings/bounce_purge + + +```php +$request_body = json_decode('{ + "enabled": true, + "hard_bounces": 5, + "soft_bounces": 5 +}'); +$response = $sg->client->mail_settings()->bounce_purge()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve bounce purge mail settings + +**This endpoint allows you to retrieve your current bounce purge settings.** + +This setting allows you to set a schedule for Twilio SendGrid to automatically delete contacts from your soft and hard bounce suppression lists. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### GET /mail_settings/bounce_purge + + +```php +$response = $sg->client->mail_settings()->bounce_purge()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update footer mail settings + +**This endpoint allows you to update your current Footer mail settings.** + +The footer setting will insert a custom footer at the bottom of the text and HTML bodies. Use the embedded HTML editor and plain text entry fields to create the content of the footers to be inserted into your emails. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### PATCH /mail_settings/footer + + +```php +$request_body = json_decode('{ + "enabled": true, + "html_content": "...", + "plain_content": "..." +}'); +$response = $sg->client->mail_settings()->footer()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve footer mail settings + +**This endpoint allows you to retrieve your current Footer mail settings.** + +The footer setting will insert a custom footer at the bottom of the text and HTML bodies. Use the embedded HTML editor and plain text entry fields to create the content of the footers to be inserted into your emails. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### GET /mail_settings/footer + + +```php +$response = $sg->client->mail_settings()->footer()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update forward bounce mail settings + +**This endpoint allows you to update your current bounce forwarding mail settings.** + +Activating this setting allows you to specify an email address to which bounce reports are forwarded. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### PATCH /mail_settings/forward_bounce + + +```php +$request_body = json_decode('{ + "email": "example@example.com", + "enabled": true +}'); +$response = $sg->client->mail_settings()->forward_bounce()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve forward bounce mail settings + +**This endpoint allows you to retrieve your current bounce forwarding mail settings.** + +Activating this setting allows you to specify an email address to which bounce reports are forwarded. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### GET /mail_settings/forward_bounce + + +```php +$response = $sg->client->mail_settings()->forward_bounce()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update forward spam mail settings + +**This endpoint allows you to update your current Forward Spam mail settings.** + +Enabling the forward spam setting allows you to specify an email address to which spam reports will be forwarded. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### PATCH /mail_settings/forward_spam + + +```php +$request_body = json_decode('{ + "email": "", + "enabled": false +}'); +$response = $sg->client->mail_settings()->forward_spam()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve forward spam mail settings + +**This endpoint allows you to retrieve your current Forward Spam mail settings.** + +Enabling the forward spam setting allows you to specify an email address to which spam reports will be forwarded. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### GET /mail_settings/forward_spam + + +```php +$response = $sg->client->mail_settings()->forward_spam()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update plain content mail settings + +**This endpoint allows you to update your current Plain Content mail settings.** + +The plain content setting will automatically convert any plain text emails that you send to HTML before sending. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### PATCH /mail_settings/plain_content + + +```php +$request_body = json_decode('{ + "enabled": false +}'); +$response = $sg->client->mail_settings()->plain_content()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve plain content mail settings + +**This endpoint allows you to retrieve your current Plain Content mail settings.** + +The plain content setting will automatically convert any plain text emails that you send to HTML before sending. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrids' [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### GET /mail_settings/plain_content + + +```php +$response = $sg->client->mail_settings()->plain_content()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update spam check mail settings + +**This endpoint allows you to update your current spam checker mail settings.** + +The spam checker filter notifies you when emails are detected that exceed a predefined spam threshold. + +Mail settings allow you to tell Twilio Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### PATCH /mail_settings/spam_check + + +```php +$request_body = json_decode('{ + "enabled": true, + "max_score": 5, + "url": "url" +}'); +$response = $sg->client->mail_settings()->spam_check()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve spam check mail settings + +**This endpoint allows you to retrieve your current Spam Checker mail settings.** + +The spam checker filter notifies you when emails are detected that exceed a predefined spam threshold. + +Mail settings allow you to tell Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### GET /mail_settings/spam_check + + +```php +$response = $sg->client->mail_settings()->spam_check()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update template mail settings + +**This endpoint allows you to update your current legacy email template settings.** + +This setting refers to our original email templates. We currently support more fully featured [transactional templates](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + +The legacy email template setting wraps an HTML template around your email content. This can be useful for sending out marketing email and/or other HTML formatted messages. + +Mail settings allow you to tell Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### PATCH /mail_settings/template + + +```php +$request_body = json_decode('{ + "enabled": true, + "html_content": "<% body %>" +}'); +$response = $sg->client->mail_settings()->template()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve legacy template mail settings + +**This endpoint allows you to retrieve your current legacy email template settings.** + +This setting refers to our original email templates. We currently support more fully featured [transactional templates](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + +The legacy email template setting wraps an HTML template around your email content. This can be useful for sending out marketing email and/or other HTML formatted messages. + +Mail settings allow you to tell Twilio SendGrid specific things to do to every email that you send to your recipients over Twilio SendGrid's [Web API](https://sendgrid.com/docs/API_Reference/Web_API/mail.html) or [SMTP Relay](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html). + +### GET /mail_settings/template + + +```php +$response = $sg->client->mail_settings()->template()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# MAILBOX PROVIDERS + +## Retrieve email statistics by mailbox provider. + +**This endpoint allows you to retrieve your email statistics segmented by recipient mailbox provider.** + +**We only store up to 7 days of email activity in our database.** By default, 500 items will be returned per request via the Advanced Stats API endpoints. + +Advanced Stats provide a more in-depth view of your email statistics and the actions taken by your recipients. You can segment these statistics by geographic location, device type, client type, browser, and mailbox provider. For more information about statistics, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/index.html). + +### GET /mailbox_providers/stats + + +```php +$query_params = json_decode('{"end_date": "2016-04-01", "mailbox_providers": "test_string", "aggregated_by": "day", "limit": 1, "offset": 1, "start_date": "2016-01-01"}'); +$response = $sg->client->mailbox_providers()->stats()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# PARTNER SETTINGS + +## Returns a list of all partner settings. + +**This endpoint allows you to retrieve a list of all partner settings that you can enable.** + +Our partner settings allow you to integrate your Twilio SendGrid account with our partners to increase your Twilio SendGrid experience and functionality. For more information about our partners, and how you can begin integrating with them, please visit our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/partners.html). + +### GET /partner_settings + + +```php +$query_params = json_decode('{"limit": 1, "offset": 1}'); +$response = $sg->client->partner_settings()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Updates New Relic partner settings. + +**This endpoint allows you to update or change your New Relic partner settings.** + +Our partner settings allow you to integrate your Twilio SendGrid account with our partners to increase your Twilio SendGrid experience and functionality. For more information about our partners, and how you can begin integrating with them, please visit our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/partners.html). + +By integrating with New Relic, you can send your Twilio SendGrid email statistics to your New Relic Dashboard. If you enable this setting, your stats will be sent to New Relic every 5 minutes. You will need your New Relic License Key to enable this setting. For more information, please see our [Classroom](https://sendgrid.com/docs/Classroom/Track/Collecting_Data/new_relic.html). + +### PATCH /partner_settings/new_relic + + +```php +$request_body = json_decode('{ + "enable_subuser_statistics": true, + "enabled": true, + "license_key": "" +}'); +$response = $sg->client->partner_settings()->new_relic()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Returns all New Relic partner settings. + +**This endpoint allows you to retrieve your current New Relic partner settings.** + +Our partner settings allow you to integrate your Twilio SendGrid account with our partners to increase your Twilio SendGrid experience and functionality. For more information about our partners, and how you can begin integrating with them, please visit our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/partners.html). + +By integrating with New Relic, you can send your Twilio SendGrid email statistics to your New Relic Dashboard. If you enable this setting, your stats will be sent to New Relic every 5 minutes. You will need your New Relic License Key to enable this setting. For more information, please see our [Classroom](https://sendgrid.com/docs/Classroom/Track/Collecting_Data/new_relic.html). + +### GET /partner_settings/new_relic + + +```php +$response = $sg->client->partner_settings()->new_relic()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# SCOPES + +## Retrieve a list of scopes for which this user has access. + +**This endpoint returns a list of all scopes that this user has access to.** + +API Keys can be used to authenticate the use of [SendGrids v3 Web API](https://sendgrid.com/docs/API_Reference/Web_API_v3/index.html), or the [Mail API Endpoint](https://sendgrid.com/docs/API_Reference/Web_API/mail.html). API Keys may be assigned certain permissions, or scopes, that limit which API endpoints they are able to access. For a more detailed explanation of how you can use API Key permissions, please visit our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/api_keys.html#-API-Key-Permissions) or [Classroom](https://sendgrid.com/docs/Classroom/Basics/API/api_key_permissions.html). + +### GET /scopes + + +```php +$response = $sg->client->scopes()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# SENDERS + +## Create a Sender Identity + +**This endpoint allows you to create a new sender identity.** + +*You may create up to 100 unique sender identities.* + +Sender Identities are required to be verified before use. If your domain has been authenticated it will auto verify on creation. Otherwise, an email will be sent to the `from.email`. + +### POST /senders + + +```php +$request_body = json_decode('{ + "address": "123 Elm St.", + "address_2": "Apt. 456", + "city": "Denver", + "country": "United States", + "from": { + "email": "from@example.com", + "name": "Example INC" + }, + "nickname": "My Sender ID", + "reply_to": { + "email": "replyto@example.com", + "name": "Example INC" + }, + "state": "Colorado", + "zip": "80202" +}'); +$response = $sg->client->senders()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Get all Sender Identities + +**This endpoint allows you to retrieve a list of all sender identities that have been created for your account.** + +Sender Identities are required to be verified before use. If your domain has been authenticated it will auto verify on creation. Otherwise, an email will be sent to the `from.email`. + +### GET /senders + + +```php +$response = $sg->client->senders()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update a Sender Identity + +**This endpoint allows you to update a sender identity.** + +Updates to `from.email` require re-verification. If your domain has been authenticated it will auto verify on creation. Otherwise, an email will be sent to the `from.email`. + +Partial updates are allowed, but fields that are marked as "required" in the POST (create) endpoint must not be nil if that field is included in the PATCH request. + +### PATCH /senders/{sender_id} + + +```php +$request_body = json_decode('{ + "address": "123 Elm St.", + "address_2": "Apt. 456", + "city": "Denver", + "country": "United States", + "from": { + "email": "from@example.com", + "name": "Example INC" + }, + "nickname": "My Sender ID", + "reply_to": { + "email": "replyto@example.com", + "name": "Example INC" + }, + "state": "Colorado", + "zip": "80202" +}'); +$sender_id = "test_url_param"; +$response = $sg->client->senders()->_($sender_id)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## View a Sender Identity + +**This endpoint allows you to retrieve a specific sender identity.** + +Sender Identities are required to be verified before use. If your domain has been authenticated it will auto verify on creation. Otherwise, an email will be sent to the `from.email`. + +### GET /senders/{sender_id} + + +```php +$sender_id = "test_url_param"; +$response = $sg->client->senders()->_($sender_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a Sender Identity + +**This endpoint allows you to delete one of your sender identities.** + +Sender Identities are required to be verified before use. If your domain has been authenticated it will auto verify on creation. Otherwise, an email will be sent to the `from.email`. + +### DELETE /senders/{sender_id} + + +```php +$sender_id = "test_url_param"; +$response = $sg->client->senders()->_($sender_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Resend Sender Identity Verification + +**This endpoint allows you to resend a sender identity verification email.** + +Sender Identities are required to be verified before use. If your domain has been authenticated it will auto verify on creation. Otherwise, an email will be sent to the `from.email`. + +### POST /senders/{sender_id}/resend_verification + + +```php +$sender_id = "test_url_param"; +$response = $sg->client->senders()->_($sender_id)->resend_verification()->post(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# SENDER AUTHENTICATION + +## Create an authenticated domain. + +**This endpoint allows you to create a domain authentication for one of your domains.** + +If you are creating a domain authentication that you would like a subuser to use, you have two options: +1. Use the "username" parameter. This allows you to create am authenticated subuser. This means the subuser is able to see and modify the created authentication. +2. Use the Association workflow (see Associate Domain section). This allows you to assign a domain authentication created by the parent to a subuser. This means the subuser will default to the assigned domain authentication, but will not be able to see or modify that authentication. However, if the subuser creates their own domain authentication it will overwrite the assigned domain authentication. + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio SendGrid can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + +### POST /whitelabel/domains + + +```php +$request_body = json_decode('{ + "automatic_security": false, + "custom_spf": true, + "default": true, + "domain": "example.com", + "ips": [ + "192.168.1.1", + "192.168.1.2" + ], + "subdomain": "news", + "username": "john@example.com" +}'); +$response = $sg->client->whitelabel()->domains()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## List all domain authentications. + +**This endpoint allows you to retrieve a list of all domain authentications you have created.** + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio SendGrid can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + +### GET /whitelabel/domains + + +```php +$query_params = json_decode('{"username": "test_string", "domain": "test_string", "exclude_subusers": "true", "limit": 1, "offset": 1}'); +$response = $sg->client->whitelabel()->domains()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Get the default domain authentication. + +**This endpoint allows you to retrieve the default authentication for a domain.** + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio SendGrid can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + +## URI Parameters +| URI Parameter | Type | Description | +|---|---|---| +| domain | string |The domain to find a default domain whitelabel for. | + +### GET /whitelabel/domains/default + + +```php +$response = $sg->client->whitelabel()->domains()->default()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## List the domain authentication associated with the given user. + +**This endpoint allows you to retrieve all of the domain authentications that have been assigned to a specific subuser.** + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +Domain authentications can be associated with (i.e. assigned to) subusers from a parent account. This functionality allows subusers to send mail using their parent's authenticated domains. To associate a domain authentication with a subuser, the parent account must first create the domain authentication and validate it. The parent may then associate the domain authentication via the subuser management tools. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + +## URI Parameters +| URI Parameter | Type | Description | +|---|---|---| +| username | string | Username of the subuser to find associated whitelabels for. | + +### GET /whitelabel/domains/subuser + + +```php +$response = $sg->client->whitelabel()->domains()->subuser()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Disassociate a domain authentication from a given user. + +**This endpoint allows you to disassociate a specific domain authentication from a subuser.** + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio SendGrid can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +Domain authentications can be associated with (i.e. assigned to) subusers from a parent account. This functionality allows subusers to send mail using their parent's authenticated domains. To associate a domain authentication with a subuser, the parent account must first create the domain authentication and validate it. The parent may then associate the domain authentication via the subuser management tools. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + +## URI Parameters +| URI Parameter | Type | Required? | Description | +|---|---|---|---| +| username | string | required | Username for the subuser to find associated whitelabels for. | + +### DELETE /whitelabel/domains/subuser + + +```php +$response = $sg->client->whitelabel()->domains()->subuser()->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update a domain authentication. + +**This endpoint allows you to update the settings for a domain authentication.** + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio SendGrid can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + +### PATCH /whitelabel/domains/{domain_id} + + +```php +$request_body = json_decode('{ + "custom_spf": true, + "default": false +}'); +$domain_id = "test_url_param"; +$response = $sg->client->whitelabel()->domains()->_($domain_id)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a domain authentication. + +**This endpoint allows you to retrieve a specific domain authentication.** + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio SendGrid can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + + +### GET /whitelabel/domains/{domain_id} + + +```php +$domain_id = "test_url_param"; +$response = $sg->client->whitelabel()->domains()->_($domain_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a domain authentication. + +**This endpoint allows you to delete a domain authentication.** + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio SendGrid can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + +### DELETE /whitelabel/domains/{domain_id} + + +```php +$domain_id = "test_url_param"; +$response = $sg->client->whitelabel()->domains()->_($domain_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Associate a domain authentication with a given user. + +**This endpoint allows you to associate a specific domain authentication with a subuser.** + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio SendGrid can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +Domain authentications can be associated with (i.e. assigned to) subusers from a parent account. This functionality allows subusers to send mail using their parent's authenticated domains. To associate a domain authentication with a subuser, the parent account must first create the domain authentication and validate it. The parent may then associate the domain authentication via the subuser management tools. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + +## URI Parameters +| URI Parameter | Type | Description | +|---|---|---| +| domain_id | integer | ID of the domain whitelabel to associate with the subuser. | + +### POST /whitelabel/domains/{domain_id}/subuser + + +```php +$request_body = json_decode('{ + "username": "jane@example.com" +}'); +$domain_id = "test_url_param"; +$response = $sg->client->whitelabel()->domains()->_($domain_id)->subuser()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Add an IP to a domain authentication. + +**This endpoint allows you to add an IP address to a domain authentication.** + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio SendGrid can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + +## URI Parameters +| URI Parameter | Type | Description | +|---|---|---| +| id | integer | ID of the domain to which you are adding an IP | + +### POST /whitelabel/domains/{id}/ips + + +```php +$request_body = json_decode('{ + "ip": "192.168.0.1" +}'); +$id = "test_url_param"; +$response = $sg->client->whitelabel()->domains()->_($id)->ips()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Remove an IP from a domain authentication. + +**This endpoint allows you to remove a domain's IP address from that domain's authentication.** + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio SendGrid can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + +## URI Parameters +| URI Parameter | Type | Description | +|---|---|---| +| id | integer | ID of the domain whitelabel to delete the IP from. | +| ip | string | IP to remove from the domain whitelabel. | + +### DELETE /whitelabel/domains/{id}/ips/{ip} + + +```php +$id = "test_url_param"; +$ip = "test_url_param"; +$response = $sg->client->whitelabel()->domains()->_($id)->ips()->_($ip)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Validate a domain authentication. + +**This endpoint allows you to validate a domain authentication. If it fails, it will return an error message describing why the domain could not be validated.** + +A domain authentication allows you to remove the via or sent on behalf of message that your recipients see when they read your emails. Authenticating a domain allows you to replace sendgrid.net with your personal sending domain. You will be required to create a subdomain so that Twilio SendGrid can generate the DNS records which you must give to your host provider. If you choose to use Automated Security, Twilio SendGrid will provide you with 3 CNAME records. If you turn Automated Security off, you will be given 2 TXT records and 1 MX record. + +For more information on domain authentication, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) + +## URI Parameters +| URI Parameter | Type | Description | +|---|---|---| +| id | integer |ID of the domain whitelabel to validate. | + +### POST /whitelabel/domains/{id}/validate + + +```php +$id = "test_url_param"; +$response = $sg->client->whitelabel()->domains()->_($id)->validate()->post(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Create a reverse DNS record + +**This endpoint allows you to create a reverse DNS record.** + +When creating a reverse DNS record, you should use the same subdomain that you used when you created a domain authentication. + +Reverse DNS consists of a subdomain and domain that will be used to generate a record for a given IP. Once Twilio SendGrid has verified that the appropriate A record for the IP has been created, the appropriate reverse DNS record for the IP is generated. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-reverse-dns/). + +### POST /whitelabel/ips + + +```php +$request_body = json_decode('{ + "domain": "example.com", + "ip": "192.168.1.1", + "subdomain": "email" +}'); +$response = $sg->client->whitelabel()->ips()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all reverse DNS records + +**This endpoint allows you to retrieve all of the reverse DNS records that have been created by this account.** + +You may include a search key by using the "ip" parameter. This enables you to perform a prefix search for a given IP segment (e.g. "192."). + +Reverse DNS consists of a subdomain and domain that will be used to generate a record for a given IP. Once Twilio SendGrid has verified that the appropriate A record for the IP has been created, the appropriate reverse DNS record for the IP is generated. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-reverse-dns/). + +### GET /whitelabel/ips + + +```php +$query_params = json_decode('{"ip": "test_string", "limit": 1, "offset": 1}'); +$response = $sg->client->whitelabel()->ips()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve an reverse DNS record + +**This endpoint allows you to retrieve a reverse DNS record.** + +Reverse DNS consists of a subdomain and domain that will be used to generate a record for a given IP. Once Twilio SendGrid has verified that the appropriate A record for the IP has been created, the appropriate reverse DNS record for the IP is generated. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-reverse-dns/). + +### GET /whitelabel/ips/{id} + + +```php +$id = "test_url_param"; +$response = $sg->client->whitelabel()->ips()->_($id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete an reverse DNS record + +**This endpoint allows you to delete a reverse DNS record.** + +Reverse DNS consists of a subdomain and domain that will be used to generate a record for a given IP. Once Twilio SendGrid has verified that the appropriate A record for the IP has been created, the appropriate reverse DNS record for the IP is generated. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-reverse-dns/). + +### DELETE /whitelabel/ips/{id} + + +```php +$id = "test_url_param"; +$response = $sg->client->whitelabel()->ips()->_($id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Validate a reverse DNS record + +**This endpoint allows you to validate a reverse DNS record.** + +Reverse DNS consists of a subdomain and domain that will be used to generate a record for a given IP. Once Twilio SendGrid has verified that the appropriate A record for the IP has been created, the appropriate reverse DNS record for the IP is generated. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-reverse-dns/). + +### POST /whitelabel/ips/{id}/validate + + +```php +$id = "test_url_param"; +$response = $sg->client->whitelabel()->ips()->_($id)->validate()->post(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Create a Branded Link + +**This endpoint allows you to create a new link branding.** + +Email link branding allow all of the click-tracked links you send in your emails to include the URL of your domain instead of sendgrid.net. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-link-branding/). + +### POST /whitelabel/links + + +```php +$request_body = json_decode('{ + "default": true, + "domain": "example.com", + "subdomain": "mail" +}'); +$query_params = json_decode('{"limit": 1, "offset": 1}'); +$response = $sg->client->whitelabel()->links()->post($request_body, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all link brandings + +**This endpoint allows you to retrieve all link brandings.** + +Email link branding allow all of the click-tracked links you send in your emails to include the URL of your domain instead of sendgrid.net. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-link-branding/). + +### GET /whitelabel/links + + +```php +$query_params = json_decode('{"limit": 1}'); +$response = $sg->client->whitelabel()->links()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a Default Link Branding + +**This endpoint allows you to retrieve the default link branding.** + +Default link branding is the actual link branding to be used when sending messages. If there are multiple link brandings, the default is determined by the following order: +
    +
  • Validated link branding marked as "default"
  • +
  • Legacy link brands (migrated from the whitelabel wizard)
  • +
  • Default Twilio SendGrid link whitelabel (i.e. 100.ct.sendgrid.net)
  • +
+ +Email link branding allow all of the click-tracked links you send in your emails to include the URL of your domain instead of sendgrid.net. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-link-branding/). + +### GET /whitelabel/links/default + + +```php +$query_params = json_decode('{"domain": "test_string"}'); +$response = $sg->client->whitelabel()->links()->default()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve Associated Link Branding + +**This endpoint allows you to retrieve the associated link branding for a subuser.** + +Link branding can be associated with subusers from the parent account. This functionality allows +subusers to send mail using their parent's link brands. To associate a link branding, the parent account +must first create a branded link and validate it. The parent may then associate that branded link with a subuser via the API or the Subuser Management page in the user interface. + +Email link branding allow all of the click-tracked links you send in your emails to include the URL of your domain instead of sendgrid.net. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-link-branding/). + +### GET /whitelabel/links/subuser + + +```php +$query_params = json_decode('{"username": "test_string"}'); +$response = $sg->client->whitelabel()->links()->subuser()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Disassociate a Link Branding + +**This endpoint allows you to disassociate a link branding from a subuser.** + +Link branding can be associated with subusers from the parent account. This functionality allows +subusers to send mail using their parent's link brands. To associate a link branding, the parent account +must first create a branded link and validate it. The parent may then associate that branded link with a subuser via the API or the Subuser Management page in the user interface. + +Email link branding allow all of the click-tracked links you send in your emails to include the URL of your domain instead of sendgrid.net. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-link-branding/). + +### DELETE /whitelabel/links/subuser + + +```php +$query_params = json_decode('{"username": "test_string"}'); +$response = $sg->client->whitelabel()->links()->subuser()->delete(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update a Link Branding + +**This endpoint allows you to update a specific link branding. You can use this endpoint to change a branded link's default status.** + +Email link branding allow all of the click-tracked links you send in your emails to include the URL of your domain instead of sendgrid.net. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-link-branding/). + +### PATCH /whitelabel/links/{id} + + +```php +$request_body = json_decode('{ + "default": true +}'); +$id = "test_url_param"; +$response = $sg->client->whitelabel()->links()->_($id)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a Link Branding + +**This endpoint allows you to retrieve a specific link branding.** + +Email link branding allow all of the click-tracked links you send in your emails to include the URL of your domain instead of sendgrid.net. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-link-branding/). + +### GET /whitelabel/links/{id} + + +```php +$id = "test_url_param"; +$response = $sg->client->whitelabel()->links()->_($id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a Link Branding + +**This endpoint allows you to delete a link branding.** + +Email link branding allow all of the click-tracked links you send in your emails to include the URL of your domain instead of sendgrid.net. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-link-branding/). + +### DELETE /whitelabel/links/{id} + + +```php +$id = "test_url_param"; +$response = $sg->client->whitelabel()->links()->_($id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Validate a Link Branding + +**This endpoint allows you to validate a link branding.** + +Email link branding allow all of the click-tracked links you send in your emails to include the URL of your domain instead of sendgrid.net. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-link-branding/). + +### POST /whitelabel/links/{id}/validate + + +```php +$id = "test_url_param"; +$response = $sg->client->whitelabel()->links()->_($id)->validate()->post(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Associate a Link Branding + +**This endpoint allows you to associate a link branding with a subuser account.** + +Link branding can be associated with subusers from the parent account. This functionality allows +subusers to send mail using their parent's link brands. To associate a link branding, the parent account +must first create a branded link and validate it. The parent may then associate that branded link with a subuser via the API or the Subuser Management page in the user interface. + +Email link branding allow all of the click-tracked links you send in your emails to include the URL of your domain instead of sendgrid.net. + +For more information, please see our [User Guide](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-link-branding/). + +### POST /whitelabel/links/{link_id}/subuser + + +```php +$request_body = json_decode('{ + "username": "jane@example.com" +}'); +$link_id = "test_url_param"; +$response = $sg->client->whitelabel()->links()->_($link_id)->subuser()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + + +# STATS + +## Retrieve global email statistics + +**This endpoint allows you to retrieve all of your global email statistics between a given date range.** + +Parent accounts will see aggregated stats for their account and all subuser accounts. Subuser accounts will only see their own stats. + +### GET /stats + + +```php +$query_params = json_decode('{"aggregated_by": "day", "limit": 1, "start_date": "2016-01-01", "end_date": "2016-04-01", "offset": 1}'); +$response = $sg->client->stats()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# SUBUSERS + +## Create Subuser + +This endpoint allows you to retrieve a list of all of your subusers. You can choose to retrieve specific subusers as well as limit the results that come back from the API. + +For more information about Subusers: + +* [User Guide > Subusers](https://sendgrid.com/docs/User_Guide/Settings/Subusers/index.html) +* [Classroom > How do I add more subusers to my account?](https://sendgrid.com/docs/Classroom/Basics/Account/how_do_i_add_more_subusers_to_my_account.html) + +### POST /subusers + + +```php +$request_body = json_decode('{ + "email": "John@example.com", + "ips": [ + "1.1.1.1", + "2.2.2.2" + ], + "password": "johns_password", + "username": "John@example.com" +}'); +$response = $sg->client->subusers()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## List all Subusers + +This endpoint allows you to retrieve a list of all of your subusers. You can choose to retrieve specific subusers as well as limit the results that come back from the API. + +For more information about Subusers: + +* [User Guide > Subusers](https://sendgrid.com/docs/User_Guide/Settings/Subusers/index.html) +* [Classroom > How do I add more subusers to my account?](https://sendgrid.com/docs/Classroom/Basics/Account/how_do_i_add_more_subusers_to_my_account.html) + +### GET /subusers + + +```php +$query_params = json_decode('{"username": "test_string", "limit": 1, "offset": 1}'); +$response = $sg->client->subusers()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve Subuser Reputations + +Subuser sender reputations give a good idea how well a sender is doing with regards to how recipients and recipient servers react to the mail that is being received. When a bounce, spam report, or other negative action happens on a sent email, it will effect your sender rating. + +This endpoint allows you to request the reputations for your subusers. + +### GET /subusers/reputations + + +```php +$query_params = json_decode('{"usernames": "test_string"}'); +$response = $sg->client->subusers()->reputations()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve email statistics for your subusers. + +**This endpoint allows you to retrieve the email statistics for the given subusers.** + +You may retrieve statistics for up to 10 different subusers by including an additional _subusers_ parameter for each additional subuser. + +While you can always view the statistics for all email activity on your account, subuser statistics enable you to view specific segments of your stats. Emails sent, bounces, and spam reports are always tracked for subusers. Unsubscribes, clicks, and opens are tracked if you have enabled the required settings. + +For more information, see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/subuser.html). + +### GET /subusers/stats + + +```php +$query_params = json_decode('{"end_date": "2016-04-01", "aggregated_by": "day", "limit": 1, "offset": 1, "start_date": "2016-01-01", "subusers": "test_string"}'); +$response = $sg->client->subusers()->stats()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve monthly stats for all subusers + +**This endpoint allows you to retrieve the monthly email statistics for all subusers over the given date range.** + +While you can always view the statistics for all email activity on your account, subuser statistics enable you to view specific segments of your stats for your subusers. Emails sent, bounces, and spam reports are always tracked for subusers. Unsubscribes, clicks, and opens are tracked if you have enabled the required settings. + +When using the `sort_by_metric` to sort your stats by a specific metric, you can not sort by the following metrics: +`bounce_drops`, `deferred`, `invalid_emails`, `processed`, `spam_report_drops`, `spam_reports`, or `unsubscribe_drops`. + +For more information, see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/subuser.html). + +### GET /subusers/stats/monthly + + +```php +$query_params = json_decode('{"subuser": "test_string", "limit": 1, "sort_by_metric": "test_string", "offset": 1, "date": "test_string", "sort_by_direction": "asc"}'); +$response = $sg->client->subusers()->stats()->monthly()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve the totals for each email statistic metric for all subusers. + +**This endpoint allows you to retrieve the total sums of each email statistic metric for all subusers over the given date range.** + + +While you can always view the statistics for all email activity on your account, subuser statistics enable you to view specific segments of your stats. Emails sent, bounces, and spam reports are always tracked for subusers. Unsubscribes, clicks, and opens are tracked if you have enabled the required settings. + +For more information, see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/subuser.html). + +### GET /subusers/stats/sums + + +```php +$query_params = json_decode('{"end_date": "2016-04-01", "aggregated_by": "day", "limit": 1, "sort_by_metric": "test_string", "offset": 1, "start_date": "2016-01-01", "sort_by_direction": "asc"}'); +$response = $sg->client->subusers()->stats()->sums()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Enable/disable a subuser + +This endpoint allows you to enable or disable a subuser. + +For more information about Subusers: + +* [User Guide > Subusers](https://sendgrid.com/docs/User_Guide/Settings/Subusers/index.html) +* [Classroom > How do I add more subusers to my account?](https://sendgrid.com/docs/Classroom/Basics/Account/how_do_i_add_more_subusers_to_my_account.html) + +### PATCH /subusers/{subuser_name} + + +```php +$request_body = json_decode('{ + "disabled": false +}'); +$subuser_name = "test_url_param"; +$response = $sg->client->subusers()->_($subuser_name)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a subuser + +This endpoint allows you to delete a subuser. This is a permanent action, once deleted a subuser cannot be retrieved. + +For more information about Subusers: + +* [User Guide > Subusers](https://sendgrid.com/docs/User_Guide/Settings/Subusers/index.html) +* [Classroom > How do I add more subusers to my account?](https://sendgrid.com/docs/Classroom/Basics/Account/how_do_i_add_more_subusers_to_my_account.html) + +### DELETE /subusers/{subuser_name} + + +```php +$subuser_name = "test_url_param"; +$response = $sg->client->subusers()->_($subuser_name)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update IPs assigned to a subuser + +Each subuser should be assigned to an IP address, from which all of this subuser's mail will be sent. Often, this is the same IP as the parent account, but each subuser can have their own, or multiple, IP addresses as well. + +More information: + +* [How to request more IPs](https://sendgrid.com/docs/Classroom/Basics/Account/adding_an_additional_dedicated_ip_to_your_account.html) +* [How to set up reverse DNS](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-reverse-dns/) + +### PUT /subusers/{subuser_name}/ips + + +```php +$request_body = json_decode('[ + "127.0.0.1" +]'); +$subuser_name = "test_url_param"; +$response = $sg->client->subusers()->_($subuser_name)->ips()->put($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update Monitor Settings for a subuser + +Subuser monitor settings allow you to receive a sample of an outgoing message by a specific customer at a specific frequency of emails. + +### PUT /subusers/{subuser_name}/monitor + + +```php +$request_body = json_decode('{ + "email": "example@example.com", + "frequency": 500 +}'); +$subuser_name = "test_url_param"; +$response = $sg->client->subusers()->_($subuser_name)->monitor()->put($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Create monitor settings + +Subuser monitor settings allow you to receive a sample of an outgoing message by a specific customer at a specific frequency of emails. + +### POST /subusers/{subuser_name}/monitor + + +```php +$request_body = json_decode('{ + "email": "example@example.com", + "frequency": 50000 +}'); +$subuser_name = "test_url_param"; +$response = $sg->client->subusers()->_($subuser_name)->monitor()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve monitor settings for a subuser + +Subuser monitor settings allow you to receive a sample of an outgoing message by a specific customer at a specific frequency of emails. + +### GET /subusers/{subuser_name}/monitor + + +```php +$subuser_name = "test_url_param"; +$response = $sg->client->subusers()->_($subuser_name)->monitor()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete monitor settings + +Subuser monitor settings allow you to receive a sample of an outgoing message by a specific customer at a specific frequency of emails. + +### DELETE /subusers/{subuser_name}/monitor + + +```php +$subuser_name = "test_url_param"; +$response = $sg->client->subusers()->_($subuser_name)->monitor()->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve the monthly email statistics for a single subuser + +**This endpoint allows you to retrieve the monthly email statistics for a specific subuser.** + +While you can always view the statistics for all email activity on your account, subuser statistics enable you to view specific segments of your stats for your subusers. Emails sent, bounces, and spam reports are always tracked for subusers. Unsubscribes, clicks, and opens are tracked if you have enabled the required settings. + +When using the `sort_by_metric` to sort your stats by a specific metric, you can not sort by the following metrics: +`bounce_drops`, `deferred`, `invalid_emails`, `processed`, `spam_report_drops`, `spam_reports`, or `unsubscribe_drops`. + +For more information, see our [User Guide](https://sendgrid.com/docs/User_Guide/Statistics/subuser.html). + +### GET /subusers/{subuser_name}/stats/monthly + + +```php +$query_params = json_decode('{"date": "test_string", "sort_by_direction": "asc", "limit": 1, "sort_by_metric": "test_string", "offset": 1}'); +$subuser_name = "test_url_param"; +$response = $sg->client->subusers()->_($subuser_name)->stats()->monthly()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# SUPPRESSION + +## Retrieve all blocks + +**This endpoint allows you to retrieve a list of all email addresses that are currently on your blocks list.** + +[Blocks](https://sendgrid.com/docs/Glossary/blocks.html) happen when your message was rejected for a reason related to the message, not the recipient address. This can happen when your mail server IP address has been added to a blacklist or blocked by an ISP, or if the message content is flagged by a filter on the receiving server. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/blocks.html). + +### GET /suppression/blocks + + +```php +$query_params = json_decode('{"start_time": 1, "limit": 1, "end_time": 1, "offset": 1}'); +$response = $sg->client->suppression()->blocks()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete blocks + +**This endpoint allows you to delete all email addresses on your blocks list.** + +There are two options for deleting blocked emails: + +1. You can delete all blocked emails by setting `delete_all` to true in the request body. +2. You can delete some blocked emails by specifying the email addresses in an array in the request body. + +[Blocks](https://sendgrid.com/docs/Glossary/blocks.html) happen when your message was rejected for a reason related to the message, not the recipient address. This can happen when your mail server IP address has been added to a blacklist or blocked by an ISP, or if the message content is flagged by a filter on the receiving server. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/blocks.html). + +### DELETE /suppression/blocks + + +```php +$request_body = json_decode('{ + "delete_all": false, + "emails": [ + "example1@example.com", + "example2@example.com" + ] +}'); +$response = $sg->client->suppression()->blocks()->delete($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a specific block + +**This endpoint allows you to retrieve a specific email address from your blocks list.** + +[Blocks](https://sendgrid.com/docs/Glossary/blocks.html) happen when your message was rejected for a reason related to the message, not the recipient address. This can happen when your mail server IP address has been added to a blacklist or blocked by an ISP, or if the message content is flagged by a filter on the receiving server. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/blocks.html). + +### GET /suppression/blocks/{email} + + +```php +$email = "test_url_param"; +$response = $sg->client->suppression()->blocks()->_($email)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a specific block + +**This endpoint allows you to delete a specific email address from your blocks list.** + +[Blocks](https://sendgrid.com/docs/Glossary/blocks.html) happen when your message was rejected for a reason related to the message, not the recipient address. This can happen when your mail server IP address has been added to a blacklist or blocked by an ISP, or if the message content is flagged by a filter on the receiving server. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/blocks.html). + +### DELETE /suppression/blocks/{email} + + +```php +$email = "test_url_param"; +$response = $sg->client->suppression()->blocks()->_($email)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all bounces + +**This endpoint allows you to retrieve all of your bounces.** + +Bounces are messages that are returned to the server that sent it. + +For more information see: + +* [User Guide > Bounces](https://sendgrid.com/docs/User_Guide/Suppressions/bounces.html) for more information +* [Glossary > Bounces](https://sendgrid.com/docs/Glossary/Bounces.html) + +### GET /suppression/bounces + + +```php +$query_params = json_decode('{"start_time": 1, "end_time": 1}'); +$response = $sg->client->suppression()->bounces()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete bounces + +**This endpoint allows you to delete all of your bounces. You can also use this endpoint to remove a specific email address from your bounce list.** + +Bounces are messages that are returned to the server that sent it. + +For more information see: + +* [User Guide > Bounces](https://sendgrid.com/docs/User_Guide/Suppressions/bounces.html) for more information +* [Glossary > Bounces](https://sendgrid.com/docs/Glossary/Bounces.html) +* [Classroom > List Scrubbing Guide](https://sendgrid.com/docs/Classroom/Deliver/list_scrubbing.html) + +Note: the `delete_all` and `emails` parameters should be used independently of each other as they have different purposes. + +### DELETE /suppression/bounces + + +```php +$request_body = json_decode('{ + "delete_all": true, + "emails": [ + "example@example.com", + "example2@example.com" + ] +}'); +$response = $sg->client->suppression()->bounces()->delete($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a Bounce + +**This endpoint allows you to retrieve a specific bounce for a given email address.** + +Bounces are messages that are returned to the server that sent it. + +For more information see: + +* [User Guide > Bounces](https://sendgrid.com/docs/User_Guide/Suppressions/bounces.html) for more information +* [Glossary > Bounces](https://sendgrid.com/docs/Glossary/Bounces.html) +* [Classroom > List Scrubbing Guide](https://sendgrid.com/docs/Classroom/Deliver/list_scrubbing.html) + +### GET /suppression/bounces/{email} + + +```php +$email = "test_url_param"; +$response = $sg->client->suppression()->bounces()->_($email)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a bounce + +**This endpoint allows you to remove an email address from your bounce list.** + +Bounces are messages that are returned to the server that sent it. This endpoint allows you to delete a single email addresses from your bounce list. + +For more information see: + +* [User Guide > Bounces](https://sendgrid.com/docs/User_Guide/Suppressions/bounces.html) for more information +* [Glossary > Bounces](https://sendgrid.com/docs/Glossary/Bounces.html) +* [Classroom > List Scrubbing Guide](https://sendgrid.com/docs/Classroom/Deliver/list_scrubbing.html) + +### DELETE /suppression/bounces/{email} + + +```php +$query_params = json_decode('{"email_address": "example@example.com"}'); +$email = "test_url_param"; +$response = $sg->client->suppression()->bounces()->_($email)->delete(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all invalid emails + +**This endpoint allows you to retrieve a list of all invalid email addresses.** + +An invalid email occurs when you attempt to send email to an address that is formatted in a manner that does not meet internet email format standards or the email does not exist at the recipients mail server. + +Examples include addresses without the @ sign or addresses that include certain special characters and/or spaces. This response can come from our own server or the recipient mail server. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/invalid_emails.html). + +### GET /suppression/invalid_emails + + +```php +$query_params = json_decode('{"start_time": 1, "limit": 1, "end_time": 1, "offset": 1}'); +$response = $sg->client->suppression()->invalid_emails()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete invalid emails + +**This endpoint allows you to remove email addresses from your invalid email address list.** + +There are two options for deleting invalid email addresses: + +1) You can delete all invalid email addresses by setting `delete_all` to true in the request body. +2) You can delete some invalid email addresses by specifying certain addresses in an array in the request body. + +An invalid email occurs when you attempt to send email to an address that is formatted in a manner that does not meet internet email format standards or the email does not exist at the recipients mail server. + +Examples include addresses without the @ sign or addresses that include certain special characters and/or spaces. This response can come from our own server or the recipient mail server. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/invalid_emails.html). + +### DELETE /suppression/invalid_emails + + +```php +$request_body = json_decode('{ + "delete_all": false, + "emails": [ + "example1@example.com", + "example2@example.com" + ] +}'); +$response = $sg->client->suppression()->invalid_emails()->delete($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a specific invalid email + +**This endpoint allows you to retrieve a specific invalid email addresses.** + +An invalid email occurs when you attempt to send email to an address that is formatted in a manner that does not meet internet email format standards or the email does not exist at the recipients mail server. + +Examples include addresses without the @ sign or addresses that include certain special characters and/or spaces. This response can come from our own server or the recipient mail server. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/invalid_emails.html). + +### GET /suppression/invalid_emails/{email} + + +```php +$email = "test_url_param"; +$response = $sg->client->suppression()->invalid_emails()->_($email)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a specific invalid email + +**This endpoint allows you to remove a specific email address from the invalid email address list.** + +An invalid email occurs when you attempt to send email to an address that is formatted in a manner that does not meet internet email format standards or the email does not exist at the recipients mail server. + +Examples include addresses without the @ sign or addresses that include certain special characters and/or spaces. This response can come from our own server or the recipient mail server. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/invalid_emails.html). + +### DELETE /suppression/invalid_emails/{email} + + +```php +$email = "test_url_param"; +$response = $sg->client->suppression()->invalid_emails()->_($email)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a specific spam report + +**This endpoint allows you to retrieve a specific spam report.** + +[Spam reports](https://sendgrid.com/docs/Glossary/spam_reports.html) happen when a recipient indicates that they think your email is [spam](https://sendgrid.com/docs/Glossary/spam.html) and then their email provider reports this to Twilio SendGrid. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/spam_reports.html). + +### GET /suppression/spam_report/{email} + + +```php +$email = "test_url_param"; +$response = $sg->client->suppression()->spam_reports()->_($email)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a specific spam report + +**This endpoint allows you to delete a specific spam report.** + +[Spam reports](https://sendgrid.com/docs/Glossary/spam_reports.html) happen when a recipient indicates that they think your email is [spam](https://sendgrid.com/docs/Glossary/spam.html) and then their email provider reports this to Twilio SendGrid. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/spam_reports.html). + +### DELETE /suppression/spam_report/{email} + + +```php +$email = "test_url_param"; +$response = $sg->client->suppression()->spam_reports()->_($email)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all spam reports + +**This endpoint allows you to retrieve all spam reports.** + +[Spam reports](https://sendgrid.com/docs/Glossary/spam_reports.html) happen when a recipient indicates that they think your email is [spam](https://sendgrid.com/docs/Glossary/spam.html) and then their email provider reports this to Twilio SendGrid. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/spam_reports.html). + +### GET /suppression/spam_reports + + +```php +$query_params = json_decode('{"start_time": 1, "limit": 1, "end_time": 1, "offset": 1}'); +$response = $sg->client->suppression()->spam_reports()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete spam reports + +**This endpoint allows you to delete your spam reports.** + +There are two options for deleting spam reports: + +1) You can delete all spam reports by setting "delete_all" to true in the request body. +2) You can delete some spam reports by specifying the email addresses in an array in the request body. + +[Spam reports](https://sendgrid.com/docs/Glossary/spam_reports.html) happen when a recipient indicates that they think your email is [spam](https://sendgrid.com/docs/Glossary/spam.html) and then their email provider reports this to Twilio SendGrid. + +For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/spam_reports.html). + +### DELETE /suppression/spam_reports + + +```php +$request_body = json_decode('{ + "delete_all": false, + "emails": [ + "example1@example.com", + "example2@example.com" + ] +}'); +$response = $sg->client->suppression()->spam_reports()->delete($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all global suppressions + +**This endpoint allows you to retrieve a list of all email address that are globally suppressed.** + +A global suppression (or global unsubscribe) is an email address of a recipient who does not want to receive any of your messages. A globally suppressed recipient will be removed from any email you send. For more information, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Suppressions/global_unsubscribes.html). + +### GET /suppression/unsubscribes + + +```php +$query_params = json_decode('{"start_time": 1, "limit": 1, "end_time": 1, "offset": 1}'); +$response = $sg->client->suppression()->unsubscribes()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# TEMPLATES + +## Create a transactional template. + +**This endpoint allows you to create a transactional template.** + +Each user can create up to 300 different transactional templates. Transactional templates are specific to accounts and subusers. Templates created on a parent account will not be accessible from the subuser accounts. + +Transactional templates are templates created specifically for transactional email and are not to be confused with [Marketing Campaigns templates](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/templates.html). For more information about transactional templates, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + +### POST /templates + + +```php +$request_body = json_decode('{ + "name": "example_name" +}'); +$response = $sg->client->templates()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all transactional templates (legacy & dynamic). + +**This endpoint allows you to retrieve all transactional templates.** + +Each user can create up to 300 different transactional templates. Transactional templates are specific to accounts and subusers. Templates created on a parent account will not be accessible from the subuser accounts. + +Transactional templates are templates created specifically for transactional email and are not to be confused with [Marketing Campaigns templates](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/templates.html). For more information about transactional templates, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + +### GET /templates + + +```php +$query_params = json_decode('{"generations": "legacy,dynamic"}'); +$response = $sg->client->templates()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Edit a transactional template. + +**This endpoint allows you to edit a transactional template.** + +Each user can create up to 300 different transactional templates. Transactional templates are specific to accounts and subusers. Templates created on a parent account will not be accessible from the subuser accounts. + +Transactional templates are templates created specifically for transactional email and are not to be confused with [Marketing Campaigns templates](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/templates.html). For more information about transactional templates, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + + +### PATCH /templates/{template_id} + + +```php +$request_body = json_decode('{ + "name": "new_example_name" +}'); +$template_id = "test_url_param"; +$response = $sg->client->templates()->_($template_id)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a single transactional template. + +**This endpoint allows you to retrieve a single transactional template.** + +Each user can create up to 300 different transactional templates. Transactional templates are specific to accounts and subusers. Templates created on a parent account will not be accessible from the subuser accounts. + +Transactional templates are templates created specifically for transactional email and are not to be confused with [Marketing Campaigns templates](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/templates.html). For more information about transactional templates, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + + +### GET /templates/{template_id} + + +```php +$template_id = "test_url_param"; +$response = $sg->client->templates()->_($template_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a template. + +**This endpoint allows you to delete a transactional template.** + +Each user can create up to 300 different transactional templates. Transactional templates are specific to accounts and subusers. Templates created on a parent account will not be accessible from the subuser accounts. + +Transactional templates are templates created specifically for transactional email and are not to be confused with [Marketing Campaigns templates](https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/templates.html). For more information about transactional templates, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + + +### DELETE /templates/{template_id} + + +```php +$template_id = "test_url_param"; +$response = $sg->client->templates()->_($template_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Create a new transactional template version. + +**This endpoint allows you to create a new version of a template.** + +Each transactional template can have multiple versions, each version with its own subject and content. Each user can have up to 300 versions across all templates. + +For more information about transactional templates, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + + +### POST /templates/{template_id}/versions + + +```php +$request_body = json_decode('{ + "active": 1, + "html_content": "<%body%>", + "name": "example_version_name", + "plain_content": "<%body%>", + "subject": "<%subject%>", + "template_id": "ddb96bbc-9b92-425e-8979-99464621b543" +}'); +$template_id = "test_url_param"; +$response = $sg->client->templates()->_($template_id)->versions()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Edit a transactional template version. + +**This endpoint allows you to edit a version of one of your transactional templates.** + +Each transactional template can have multiple versions, each version with its own subject and content. Each user can have up to 300 versions across all templates. + +For more information about transactional templates, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + +## URI Parameters +| URI Parameter | Type | Description | +|---|---|---| +| template_id | string | The ID of the original template | +| version_id | string | The ID of the template version | + +### PATCH /templates/{template_id}/versions/{version_id} + + +```php +$request_body = json_decode('{ + "active": 1, + "html_content": "<%body%>", + "name": "updated_example_name", + "plain_content": "<%body%>", + "subject": "<%subject%>" +}'); +$template_id = "test_url_param"; +$version_id = "test_url_param"; +$response = $sg->client->templates()->_($template_id)->versions()->_($version_id)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a specific transactional template version. + +**This endpoint allows you to retrieve a specific version of a template.** + +Each transactional template can have multiple versions, each version with its own subject and content. Each user can have up to 300 versions across all templates. + +For more information about transactional templates, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + +## URI Parameters +| URI Parameter | Type | Description | +|---|---|---| +| template_id | string | The ID of the original template | +| version_id | string | The ID of the template version | + +### GET /templates/{template_id}/versions/{version_id} + + +```php +$template_id = "test_url_param"; +$version_id = "test_url_param"; +$response = $sg->client->templates()->_($template_id)->versions()->_($version_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a transactional template version. + +**This endpoint allows you to delete one of your transactional template versions.** + +Each transactional template can have multiple versions, each version with its own subject and content. Each user can have up to 300 versions across all templates. + +For more information about transactional templates, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + +## URI Parameters +| URI Parameter | Type | Description | +|---|---|---| +| template_id | string | The ID of the original template | +| version_id | string | The ID of the template version | + +### DELETE /templates/{template_id}/versions/{version_id} + + +```php +$template_id = "test_url_param"; +$version_id = "test_url_param"; +$response = $sg->client->templates()->_($template_id)->versions()->_($version_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Activate a transactional template version. + +**This endpoint allows you to activate a version of one of your templates.** + +Each transactional template can have multiple versions, each version with its own subject and content. Each user can have up to 300 versions across all templates. + + +For more information about transactional templates, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html). + +## URI Parameters +| URI Parameter | Type | Description | +|---|---|---| +| template_id | string | The ID of the original template | +| version_id | string | The ID of the template version | + +### POST /templates/{template_id}/versions/{version_id}/activate + + +```php +$template_id = "test_url_param"; +$version_id = "test_url_param"; +$response = $sg->client->templates()->_($template_id)->versions()->_($version_id)->activate()->post(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# TRACKING SETTINGS + +## Retrieve Tracking Settings + +**This endpoint allows you to retrieve a list of all tracking settings that you can enable on your account.** + +You can track a variety of the actions your recipients may take when interacting with your emails including opening your emails, clicking on links in your emails, and subscribing to (or unsubscribing from) your emails. + +For more information about tracking, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/tracking.html). + +### GET /tracking_settings + + +```php +$query_params = json_decode('{"limit": 1, "offset": 1}'); +$response = $sg->client->tracking_settings()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update Click Tracking Settings + +**This endpoint allows you to change your current click tracking setting. You can enable, or disable, click tracking using this endpoint.** + +You can track a variety of the actions your recipients may take when interacting with your emails including opening your emails, clicking on links in your emails, and subscribing to (or unsubscribing from) your emails. + +For more information about tracking, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/tracking.html). + +### PATCH /tracking_settings/click + + +```php +$request_body = json_decode('{ + "enabled": true +}'); +$response = $sg->client->tracking_settings()->click()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve Click Track Settings + +**This endpoint allows you to retrieve your current click tracking setting.** + +You can track a variety of the actions your recipients may take when interacting with your emails including opening your emails, clicking on links in your emails, and subscribing to (or unsubscribing from) your emails. + +For more information about tracking, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/tracking.html). + +### GET /tracking_settings/click + + +```php +$response = $sg->client->tracking_settings()->click()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update Google Analytics Settings + +**This endpoint allows you to update your current setting for Google Analytics.** + +For more information about using Google Analytics, please refer to [Googles URL Builder](https://support.google.com/analytics/answer/1033867?hl=en) and their article on ["Best Practices for Campaign Building"](https://support.google.com/analytics/answer/1037445). + +We default the settings to Googles recommendations. For more information, see [Google Analytics Demystified](https://sendgrid.com/docs/Classroom/Track/Collecting_Data/google_analytics_demystified_ga_statistics_vs_sg_statistics.html). + +You can track a variety of the actions your recipients may take when interacting with your emails including opening your emails, clicking on links in your emails, and subscribing to (or unsubscribing from) your emails. + +For more information about tracking, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/tracking.html). + +### PATCH /tracking_settings/google_analytics + + +```php +$request_body = json_decode('{ + "enabled": true, + "utm_campaign": "website", + "utm_content": "", + "utm_medium": "email", + "utm_source": "sendgrid.com", + "utm_term": "" +}'); +$response = $sg->client->tracking_settings()->google_analytics()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve Google Analytics Settings + +**This endpoint allows you to retrieve your current setting for Google Analytics.** + +For more information about using Google Analytics, please refer to [Googles URL Builder](https://support.google.com/analytics/answer/1033867?hl=en) and their article on ["Best Practices for Campaign Building"](https://support.google.com/analytics/answer/1037445). + +We default the settings to Googles recommendations. For more information, see [Google Analytics Demystified](https://sendgrid.com/docs/Classroom/Track/Collecting_Data/google_analytics_demystified_ga_statistics_vs_sg_statistics.html). + +You can track a variety of the actions your recipients may take when interacting with your emails including opening your emails, clicking on links in your emails, and subscribing to (or unsubscribing from) your emails. + +For more information about tracking, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/tracking.html). + +### GET /tracking_settings/google_analytics + + +```php +$response = $sg->client->tracking_settings()->google_analytics()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update Open Tracking Settings + +**This endpoint allows you to update your current settings for open tracking.** + +Open Tracking adds an invisible image at the end of the email which can track email opens. If the email recipient has images enabled on their email client, a request to Twilio SendGrid's server for the invisible image is executed and an open event is logged. These events are logged in the Statistics portal, Email Activity interface, and are reported by the Event Webhook. + +You can track a variety of the actions your recipients may take when interacting with your emails including opening your emails, clicking on links in your emails, and subscribing to (or unsubscribing from) your emails. + +For more information about tracking, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/tracking.html). + +### PATCH /tracking_settings/open + + +```php +$request_body = json_decode('{ + "enabled": true +}'); +$response = $sg->client->tracking_settings()->open()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Get Open Tracking Settings + +**This endpoint allows you to retrieve your current settings for open tracking.** + +Open Tracking adds an invisible image at the end of the email which can track email opens. If the email recipient has images enabled on their email client, a request to Twilio SendGrid's server for the invisible image is executed and an open event is logged. These events are logged in the Statistics portal, Email Activity interface, and are reported by the Event Webhook. + +You can track a variety of the actions your recipients may take when interacting with your emails including opening your emails, clicking on links in your emails, and subscribing to (or unsubscribing from) your emails. + +For more information about tracking, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/tracking.html). + +### GET /tracking_settings/open + + +```php +$response = $sg->client->tracking_settings()->open()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update Subscription Tracking Settings + +**This endpoint allows you to update your current settings for subscription tracking.** + +Subscription tracking adds links to the bottom of your emails that allows your recipients to subscribe to, or unsubscribe from, your emails. + +You can track a variety of the actions your recipients may take when interacting with your emails including opening your emails, clicking on links in your emails, and subscribing to (or unsubscribing from) your emails. + +For more information about tracking, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/tracking.html). + +### PATCH /tracking_settings/subscription + + +```php +$request_body = json_decode('{ + "enabled": true, + "html_content": "html content", + "landing": "landing page html", + "plain_content": "text content", + "replace": "replacement tag", + "url": "url" +}'); +$response = $sg->client->tracking_settings()->subscription()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve Subscription Tracking Settings + +**This endpoint allows you to retrieve your current settings for subscription tracking.** + +Subscription tracking adds links to the bottom of your emails that allows your recipients to subscribe to, or unsubscribe from, your emails. + +You can track a variety of the actions your recipients may take when interacting with your emails including opening your emails, clicking on links in your emails, and subscribing to (or unsubscribing from) your emails. + +For more information about tracking, please see our [User Guide](https://sendgrid.com/docs/User_Guide/Settings/tracking.html). + +### GET /tracking_settings/subscription + + +```php +$response = $sg->client->tracking_settings()->subscription()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` + +# USER + +## Get a user's account information. + +**This endpoint allows you to retrieve your user account details.** + +Your user's account information includes the user's account type and reputation. + +Keeping your user profile up to date is important. This will help Twilio SendGrid to verify who you are as well as contact you should we need to. + +For more information about your user profile: + +* [Twilio SendGrid Account Settings](https://sendgrid.com/docs/User_Guide/Settings/account.html) + +### GET /user/account + + +```php +$response = $sg->client->user()->account()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve your credit balance + +**This endpoint allows you to retrieve the current credit balance for your account.** + +Your monthly credit allotment limits the number of emails you may send before incurring overage charges. For more information about credits and billing, please visit our [Classroom](https://sendgrid.com/docs/Classroom/Basics/Billing/billing_info_and_faqs.html). + +### GET /user/credits + + +```php +$response = $sg->client->user()->credits()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update your account email address + +**This endpoint allows you to update the email address currently on file for your account.** + +Keeping your user profile up to date is important. This will help Twilio SendGrid to verify who you are as well as contact you should we need to. + +For more information about your user profile: + +* [Twilio SendGrid Account Settings](https://sendgrid.com/docs/User_Guide/Settings/account.html) + +### PUT /user/email + + +```php +$request_body = json_decode('{ + "email": "example@example.com" +}'); +$response = $sg->client->user()->email()->put($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve your account email address + +**This endpoint allows you to retrieve the email address currently on file for your account.** + +Keeping your user profile up to date is important. This will help Twilio SendGrid to verify who you are as well as contact you should we need to. + +For more information about your user profile: + +* [Twilio SendGrid Account Settings](https://sendgrid.com/docs/User_Guide/Settings/account.html) + +### GET /user/email + + +```php +$response = $sg->client->user()->email()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update your password + +**This endpoint allows you to update your password.** + +Keeping your user profile up to date is important. This will help Twilio SendGrid to verify who you are as well as contact you should we need to. + +For more information about your user profile: + +* [Twilio SendGrid Account Settings](https://sendgrid.com/docs/User_Guide/Settings/account.html) + +### PUT /user/password + + +```php +$request_body = json_decode('{ + "new_password": "new_password", + "old_password": "old_password" +}'); +$response = $sg->client->user()->password()->put($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update a user's profile + +**This endpoint allows you to update your current profile details.** + +Keeping your user profile up to date is important. This will help Twilio SendGrid to verify who you are as well as contact you should we need to. + +For more information about your user profile: + +* [Twilio SendGrid Account Settings](https://sendgrid.com/docs/User_Guide/Settings/account.html) + +It should be noted that any one or more of the parameters can be updated via the PATCH /user/profile endpoint. The only requirement is that you include at least one when you PATCH. + +### PATCH /user/profile + + +```php +$request_body = json_decode('{ + "city": "Orange", + "first_name": "Example", + "last_name": "User" +}'); +$response = $sg->client->user()->profile()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Get a user's profile + +Keeping your user profile up to date is important. This will help Twilio SendGrid to verify who you are as well as contact you should we need to. + +For more information about your user profile: + +* [Twilio SendGrid Account Settings](https://sendgrid.com/docs/User_Guide/Settings/account.html) + +### GET /user/profile + + +```php +$response = $sg->client->user()->profile()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Cancel or pause a scheduled send + +**This endpoint allows you to cancel or pause an email that has been scheduled to be sent.** + +If the maximum number of cancellations/pauses are added, HTTP 400 will +be returned. + +The Cancel Scheduled Sends feature allows the customer to cancel a scheduled send based on a Batch ID included in the SMTPAPI header.Scheduled sends cancelled less than 10 minutes before the scheduled time are not guaranteed to be cancelled. + +### POST /user/scheduled_sends + + +```php +$request_body = json_decode('{ + "batch_id": "YOUR_BATCH_ID", + "status": "pause" +}'); +$response = $sg->client->user()->scheduled_sends()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all scheduled sends + +**This endpoint allows you to retrieve all cancel/paused scheduled send information.** + +The Cancel Scheduled Sends feature allows the customer to cancel a scheduled send based on a Batch ID included in the SMTPAPI header.Scheduled sends cancelled less than 10 minutes before the scheduled time are not guaranteed to be cancelled. + +### GET /user/scheduled_sends + + +```php +$response = $sg->client->user()->scheduled_sends()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update user scheduled send information + +**This endpoint allows you to update the status of a scheduled send for the given `batch_id`.** + +The Cancel Scheduled Sends feature allows the customer to cancel a scheduled send based on a Batch ID included in the SMTPAPI header.Scheduled sends cancelled less than 10 minutes before the scheduled time are not guaranteed to be cancelled. + +### PATCH /user/scheduled_sends/{batch_id} + + +```php +$request_body = json_decode('{ + "status": "pause" +}'); +$batch_id = "test_url_param"; +$response = $sg->client->user()->scheduled_sends()->_($batch_id)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve scheduled send + +**This endpoint allows you to retrieve the cancel/paused scheduled send information for a specific `batch_id`.** + +The Cancel Scheduled Sends feature allows the customer to cancel a scheduled send based on a Batch ID included in the SMTPAPI header.Scheduled sends cancelled less than 10 minutes before the scheduled time are not guaranteed to be cancelled. + +### GET /user/scheduled_sends/{batch_id} + + +```php +$batch_id = "test_url_param"; +$response = $sg->client->user()->scheduled_sends()->_($batch_id)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a cancellation or pause of a scheduled send + +**This endpoint allows you to delete the cancellation/pause of a scheduled send.** + +The Cancel Scheduled Sends feature allows the customer to cancel a scheduled send based on a Batch ID included in the SMTPAPI header.Scheduled sends cancelled less than 10 minutes before the scheduled time are not guaranteed to be cancelled. + +### DELETE /user/scheduled_sends/{batch_id} + + +```php +$batch_id = "test_url_param"; +$response = $sg->client->user()->scheduled_sends()->_($batch_id)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update Enforced TLS settings + +**This endpoint allows you to update your current Enforced TLS settings.** + +The Enforced TLS settings specify whether or not the recipient is required to support TLS or have a valid certificate. See the [SMTP Ports User Guide](https://sendgrid.com/docs/Classroom/Basics/Email_Infrastructure/smtp_ports.html) for more information on opportunistic TLS. + +**Note:** If either setting is enabled and the recipient does not support TLS or have a valid certificate, we drop the message and send a block event with TLS required but not supported as the description. + +### PATCH /user/settings/enforced_tls + + +```php +$request_body = json_decode('{ + "require_tls": true, + "require_valid_cert": false +}'); +$response = $sg->client->user()->settings()->enforced_tls()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve current Enforced TLS settings. + +**This endpoint allows you to retrieve your current Enforced TLS settings.** + +The Enforced TLS settings specify whether or not the recipient is required to support TLS or have a valid certificate. See the [SMTP Ports User Guide](https://sendgrid.com/docs/Classroom/Basics/Email_Infrastructure/smtp_ports.html) for more information on opportunistic TLS. + +**Note:** If either setting is enabled and the recipient does not support TLS or have a valid certificate, we drop the message and send a block event with TLS required but not supported as the description. + +### GET /user/settings/enforced_tls + + +```php +$response = $sg->client->user()->settings()->enforced_tls()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update your username + +**This endpoint allows you to update the username for your account.** + +Keeping your user profile up to date is important. This will help Twilio SendGrid to verify who you are as well as contact you should we need to. + +For more information about your user profile: + +* [Twilio SendGrid Account Settings](https://sendgrid.com/docs/User_Guide/Settings/account.html) + +### PUT /user/username + + +```php +$request_body = json_decode('{ + "username": "test_username" +}'); +$response = $sg->client->user()->username()->put($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve your username + +**This endpoint allows you to retrieve your current account username.** + +Keeping your user profile up to date is important. This will help Twilio SendGrid to verify who you are as well as contact you should we need to. + +For more information about your user profile: + +* [Twilio SendGrid Account Settings](https://sendgrid.com/docs/User_Guide/Settings/account.html) + +### GET /user/username + + +```php +$response = $sg->client->user()->username()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update Event Notification Settings + +**This endpoint allows you to update your current event webhook settings.** + +If an event type is marked as `true`, then the event webhook will include information about that event. + +Twilio SendGrid's Event Webhook will notify a URL of your choice via HTTP POST with information about events that occur as Twilio SendGrid processes your email. + +Common uses of this data are to remove unsubscribes, react to spam reports, determine unengaged recipients, identify bounced email addresses, or create advanced analytics of your email program. + +### PATCH /user/webhooks/event/settings + + +```php +$request_body = json_decode('{ + "bounce": true, + "click": true, + "deferred": true, + "delivered": true, + "dropped": true, + "enabled": true, + "group_resubscribe": true, + "group_unsubscribe": true, + "open": true, + "processed": true, + "spam_report": true, + "unsubscribe": true, + "url": "url" +}'); +$response = $sg->client->user()->webhooks()->event()->settings()->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve Event Webhook settings + +**This endpoint allows you to retrieve your current event webhook settings.** + +If an event type is marked as `true`, then the event webhook will include information about that event. + +Twilio SendGrid's Event Webhook will notify a URL of your choice via HTTP POST with information about events that occur as Twilio SendGrid processes your email. + +Common uses of this data are to remove unsubscribes, react to spam reports, determine unengaged recipients, identify bounced email addresses, or create advanced analytics of your email program. + +### GET /user/webhooks/event/settings + + +```php +$response = $sg->client->user()->webhooks()->event()->settings()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Test Event Notification Settings + +**This endpoint allows you to test your event webhook by sending a fake event notification post to the provided URL.** + +Twilio SendGrid's Event Webhook will notify a URL of your choice via HTTP POST with information about events that occur as Twilio SendGrid processes your email. + +Common uses of this data are to remove unsubscribes, react to spam reports, determine unengaged recipients, identify bounced email addresses, or create advanced analytics of your email program. + +### POST /user/webhooks/event/test + + +```php +$request_body = json_decode('{ + "url": "url" +}'); +$response = $sg->client->user()->webhooks()->event()->test()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Create a parse setting + +**This endpoint allows you to create a new inbound parse setting.** + +The inbound parse webhook allows you to have incoming emails parsed, extracting some or all of the content, and then have that content POSTed by Twilio SendGrid to a URL of your choosing. For more information, please see our [User Guide](https://sendgrid.com/docs/API_Reference/Webhooks/parse.html). + +### POST /user/webhooks/parse/settings + + +```php +$request_body = json_decode('{ + "hostname": "myhostname.com", + "send_raw": false, + "spam_check": true, + "url": "http://email.myhosthame.com" +}'); +$response = $sg->client->user()->webhooks()->parse()->settings()->post($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve all parse settings + +**This endpoint allows you to retrieve all of your current inbound parse settings.** + +The inbound parse webhook allows you to have incoming emails parsed, extracting some or all of the content, and then have that content POSTed by Twilio SendGrid to a URL of your choosing. For more information, please see our [User Guide](https://sendgrid.com/docs/API_Reference/Webhooks/parse.html). + +### GET /user/webhooks/parse/settings + + +```php +$response = $sg->client->user()->webhooks()->parse()->settings()->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Update a parse setting + +**This endpoint allows you to update a specific inbound parse setting.** + +The inbound parse webhook allows you to have incoming emails parsed, extracting some or all of the content, and then have that content POSTed by Twilio SendGrid to a URL of your choosing. For more information, please see our [User Guide](https://sendgrid.com/docs/API_Reference/Webhooks/parse.html). + +### PATCH /user/webhooks/parse/settings/{hostname} + + +```php +$request_body = json_decode('{ + "send_raw": true, + "spam_check": false, + "url": "http://newdomain.com/parse" +}'); +$hostname = "test_url_param"; +$response = $sg->client->user()->webhooks()->parse()->settings()->_($hostname)->patch($request_body); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieve a specific parse setting + +**This endpoint allows you to retrieve a specific inbound parse setting.** + +The inbound parse webhook allows you to have incoming emails parsed, extracting some or all of the content, and then have that content POSTed by Twilio SendGrid to a URL of your choosing. For more information, please see our [User Guide](https://sendgrid.com/docs/API_Reference/Webhooks/parse.html). + +### GET /user/webhooks/parse/settings/{hostname} + + +```php +$hostname = "test_url_param"; +$response = $sg->client->user()->webhooks()->parse()->settings()->_($hostname)->get(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Delete a parse setting + +**This endpoint allows you to delete a specific inbound parse setting.** + +The inbound parse webhook allows you to have incoming emails parsed, extracting some or all of the content, and then have that content POSTed by Twilio SendGrid to a URL of your choosing. For more information, please see our [User Guide](https://sendgrid.com/docs/API_Reference/Webhooks/parse.html). + +### DELETE /user/webhooks/parse/settings/{hostname} + + +```php +$hostname = "test_url_param"; +$response = $sg->client->user()->webhooks()->parse()->settings()->_($hostname)->delete(); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` +## Retrieves Inbound Parse Webhook statistics. + +**This endpoint allows you to retrieve the statistics for your Parse Webhook usage.** + +Twilio SendGrid's Inbound Parse Webhook allows you to parse the contents and attachments of incoming emails. The Parse API can then POST the parsed emails to a URL that you specify. The Inbound Parse Webhook cannot parse messages greater than 20MB in size, including all attachments. + +There are a number of pre-made integrations for the Twilio SendGrid Parse Webhook which make processing events easy. You can find these integrations in the [Library Index](https://sendgrid.com/docs/Integrate/libraries.html#-Webhook-Libraries). + +### GET /user/webhooks/parse/stats + + +```php +$query_params = json_decode('{"aggregated_by": "day", "limit": "test_string", "start_date": "2016-01-01", "end_date": "2016-04-01", "offset": "test_string"}'); +$response = $sg->client->user()->webhooks()->parse()->stats()->get(null, $query_params); +print $response->statusCode() . "\n"; +print $response->body() . "\n"; +print_r($response->headers()); +``` diff --git a/lib/sendgrid-php/USE_CASES.md b/lib/sendgrid-php/USE_CASES.md new file mode 100644 index 000000000..d103bf221 --- /dev/null +++ b/lib/sendgrid-php/USE_CASES.md @@ -0,0 +1,1627 @@ +This documentation provides examples for specific use cases. Please [open an issue](https://github.com/sendgrid/sendgrid-php/issues) or make a pull request for any use cases you would like us to document here. Thank you! + +# Table of Contents +- [Table of Contents](#table-of-contents) +- [Attachments](#attachments) +- [Attaching a File from Box](#attaching-a-file-from-box) +- [Attaching a File from S3](#attaching-a-file-from-s3) +- [Kitchen Sink - an example with all settings used](#kitchen-sink) +- [Send an Email to a Single Recipient](#send-an-email-to-a-single-recipient) +- [Send an Email to Multiple Recipients](#send-an-email-to-multiple-recipients) +- [Send Multiple Emails to Multiple Recipients](#send-multiple-emails-to-multiple-recipients) +- [Send Multiple Emails with Personalizations](#multiple-email-personalizations) +- [Set Region](#set-region) +- [Transactional Templates](#transactional-templates) +- [Legacy Templates](#legacy-templates) +- [Send an Email With Twilio Email (Pilot)](#send-an-email-with-twilio-email-pilot) +- [Send an SMS Message](#send-an-sms-message) +- [How to Set up a Domain Authentication](#how-to-set-up-a-domain-authentication) +- [How to View Email Statistics](#how-to-view-email-statistics) +- [How to Use the Email Activity Feed](#how-to-use-the-email-activity-feed-api) +- [Deploying to Heroku](#deploying-to-heroku) +- [Google App Engine Installation](#google-app-engine-installation) + + +# Attachments + +Here is an example of attaching a text file to your email, assuming that text file `my_file.txt` is located in the same directory. You can use the `addAttachments` method to add an array attachments. + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\Mail; + +$email = new Mail(); +$email->setFrom("test@example.com", "Example User"); +$email->setSubject("Sending with Twilio SendGrid is Fun"); +$email->addTo("test@example.com", "Example User"); +$email->addContent("text/plain", "and easy to do anywhere, even with PHP"); +$email->addContent( + "text/html", "and easy to do anywhere, even with PHP" +); + +$file_encoded = base64_encode(file_get_contents('my_file.txt')); +$email->addAttachment( + $file_encoded, + "application/text", + "my_file.txt", + "attachment" +); + +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + + +# Attaching a File from Box + +You can attach a file from [Box](https://www.box.com) to your emails. +Because there is no official Box SDK for PHP, this example requires +[firebase/php-jwt](https://github.com/firebase/php-jwt) to generate a +[JSON Web Token](https://jwt.io) assertion. Before using this code, you should +set up a JWT application on [Box](https://developer.box.com/docs/setting-up-a-jwt-app). +For more information about authenticating with JWT, see +[this page](https://developer.box.com/docs/construct-jwt-claim-manually). + +After completing the setup tutorial, you will want to make sure your app’s +configuration settings have at least the following options enabled: + +**Application Access** +* Enterprise + +**Application Scopes** +* Read all files and folders stored in Box +* Read and write all files and folders stored in Box +* Manage users + +**Advanced Features** +* Perform Actions as Users + +Remember to reauthorize your app +[here](https://app.box.com/master/settings/openbox) after making any changes to +your app’s JWT scopes. + +```php + 'RS256', + 'typ' => 'JWT', + 'kid' => $boxConfig->boxAppSettings->appAuth->publicKeyID +); +$claims = array( + 'iss' => $boxConfig->boxAppSettings->clientID, + 'sub' => $boxConfig->enterpriseID, + 'box_sub_type' => 'enterprise', + 'aud' => 'https://api.box.com/oauth2/token', + 'jti' => bin2hex(openssl_random_pseudo_bytes(16)), + 'exp' => time() + 50 +); + +$privateKey = openssl_get_privatekey( + $boxConfig->boxAppSettings->appAuth->privateKey, + $boxConfig->boxAppSettings->appAuth->passphrase +); + +$assertion = JWT::encode($claims, $privateKey, 'RS256', null, $header); + +// Get access token +$url = 'https://api.box.com/oauth2/token'; +$data = array( + 'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', + 'client_id' => $boxConfig->boxAppSettings->clientID, + 'client_secret' => $boxConfig->boxAppSettings->clientSecret, + 'assertion' => $assertion +); +$ch = curl_init($url); +curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); +curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); +$result = json_decode(curl_exec($ch)); +curl_close($ch); +$accessToken = $result->access_token; + +// Get user ID +$url = 'https://api.box.com/2.0/users'; +$ch = curl_init($url); +curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); +curl_setopt($ch, CURLOPT_HTTPHEADER, array( + 'Authorization: Bearer '.$accessToken +)); +$result = json_decode(curl_exec($ch)); +curl_close($ch); +foreach ($result->entries as $entry){ + if ($entry->login === $fileOwner){ + $userId = $entry->id; + } +} + +// Get file ID +$url = 'https://api.box.com/2.0/search'; +$data = array( + 'query' => urlencode(end($path)) +); +$urlEncoded = http_build_query($data); +$ch = curl_init($url.'?'.$urlEncoded); +curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); +curl_setopt($ch, CURLOPT_HTTPHEADER, array( + 'Authorization: Bearer '.$accessToken, + 'As-User: '.$userId +)); +$result = json_decode(curl_exec($ch)); +curl_close($ch); +foreach ($result->entries as $entry){ + if (count($entry->path_collection->entries) === count($path) -1){ + if (count($path) > 2){ + // File is located in a subdirectory. + for ($i = 1; $i < (count($path) - 1); $i++){ + if ($path[$i] === $entry->path_collection->entries[$i]->name){ + $fileId = $entry->id; + } + } + } else { + // File is located in default directory. + $fileId = $entry->id; + } + } +} + +if (isset($fileId) && isset($userId)){ + // Get file data + $url = 'https://api.box.com/2.0/files/'.$fileId.'/content'; + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, array( + 'Authorization: Bearer '.$accessToken, + 'As-User: '.$userId + )); + $result = curl_exec($ch); + $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); + curl_close($ch); + + $attachmentFilename = end($path); + $attachmentContent = base64_encode($result); + $attachmentContentType = $contentType; + + $email = new \SendGrid\Mail\Mail(); + $email->setFrom("test@example.com", "Example User"); + $email->setSubject("Attaching a File from Box"); + $email->addTo("test@example.com", "Example User"); + $email->addContent("text/plain", "See attached file from Box."); + $email->addContent( + "text/html", "See attached file from Box." + ); + + $attachment = new \SendGrid\Mail\Attachment(); + $attachment->setContent($attachmentContent); + $attachment->setType($attachmentContentType); + $attachment->setFilename($attachmentFilename); + $attachment->setDisposition("attachment"); + $attachment->setContentId($attachmentFilename); // Only used if disposition is set to inline + $email->addAttachment($attachment); + + $sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); + try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; + } catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage() ."\n"; + } +} else { + echo "Error: file or owner could not be located\n"; +} +``` + + +# Attaching a File from S3 + +You can attach a file from [Amazon S3 storage](https://aws.amazon.com/s3/) to your emails. In addition to sendgrid-php, this requires the [AWS SDK for PHP](https://aws.amazon.com/sdk-for-php/). Please follow the [Getting Started](https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/getting-started_index.html) tutorial at Amazon if you haven’t set up your AWS SDK installation yet. + +```php + 'latest', + 'region' => 'us-east-1' // This should match the region of your S3 bucket. +]); +try { + // Get the object. + // See https://docs.aws.amazon.com/AmazonS3/latest/dev/RetrieveObjSingleOpPHP.html + $result = $s3->getObject([ + 'Bucket' => $bucket, + 'Key' => $keyname + ]); + $keyExplode = explode('/',$keyname); + $attachmentFilename = end($keyExplode); + $attachmentContent = base64_encode($result['Body']); + $attachmentContentType = $result['ContentType']; + + $email = new \SendGrid\Mail\Mail(); + $email->setFrom("test@example.com", "Example User"); + $email->setSubject("Attaching a File from S3"); + $email->addTo("test@example.com", "Example User"); + $email->addContent("text/plain", "See attached file from S3."); + $email->addContent( + "text/html", "See attached file from S3." + ); + + $attachment = new \SendGrid\Mail\Attachment(); + $attachment->setContent($attachmentContent); + $attachment->setType($attachmentContentType); + $attachment->setFilename($attachmentFilename); + $attachment->setDisposition("attachment"); + $attachment->setContentId($attachmentFilename); //Only used if disposition is set to inline + $email->addAttachment($attachment); + $sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); + try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; + } catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage() ."\n"; + } +} catch (S3Exception $e) { + echo 'Caught exception: '. $e->getMessage() . "\n"; +} +``` + + +# Kitchen Sink - an example with all settings used + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\Mail; + +$email = new Mail(); + +// For a detailed description of each of these settings, +// please see the +// [documentation](https://sendgrid.com/docs/API_Reference/api_v3.html). +$email->setSubject("Sending with Twilio SendGrid is Fun 2"); + +$email->addTo("test@example.com", "Example User"); +$email->addTo("test+1@example.com", "Example User1"); +$toEmails = [ + "test+2@example.com" => "Example User2", + "test+3@example.com" => "Example User3" +]; +$email->addTos($toEmails); + +$email->addCc("test+4@example.com", "Example User4"); +$ccEmails = [ + "test+5@example.com" => "Example User5", + "test+6@example.com" => "Example User6" +]; +$email->addCcs($ccEmails); + +$email->addBcc("test+7@example.com", "Example User7"); +$bccEmails = [ + "test+8@example.com" => "Example User8", + "test+9@example.com" => "Example User9" +]; +$email->addBccs($bccEmails); + +$email->addHeader("X-Test1", "Test1"); +$email->addHeader("X-Test2", "Test2"); +$headers = [ + "X-Test3" => "Test3", + "X-Test4" => "Test4", +]; +$email->addHeaders($headers); + +$email->addDynamicTemplateData("subject1", "Example Subject 1"); +$email->addDynamicTemplateData("name1", "Example Name 1"); +$email->addDynamicTemplateData("city1", "Denver"); +$substitutions = [ + "subject2" => "Example Subject 2", + "name2" => "Example Name 2", + "city2" => "Orange" +]; +$email->addDynamicTemplateDatas($substitutions); + +$email->addCustomArg("marketing1", "false"); +$email->addCustomArg("transactional1", "true"); +$email->addCustomArg("category", "name"); +$customArgs = [ + "marketing2" => "true", + "transactional2" => "false", + "category" => "name" +]; +$email->addCustomArgs($customArgs); + +$email->setSendAt(1461775051); + +// You can add a personalization index or personalization parameter to the above +// methods to add and update multiple personalizations. You can learn more about +// personalizations [here](https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/personalizations.html). + +// The values below this comment are global to an entire message + +$email->setFrom("test@example.com", "Twilio SendGrid"); + +$email->setGlobalSubject("Sending with Twilio SendGrid is Fun and Global 2"); + +$email->addContent( + "text/plain", + "and easy to do anywhere, even with PHP" +); +$email->addContent( + "text/html", + "and easy to do anywhere, even with PHP" +); +$contents = [ + "text/calendar" => "Party Time!!", + "text/calendar2" => "Party Time 2!!" +]; +$email->addContents($contents); + +$email->addAttachment( + "base64 encoded content1", + "image/png", + "banner.png", + "inline", + "Banner" +); +$attachments = [ + [ + "base64 encoded content2", + "image/jpeg", + "banner2.jpeg", + "attachment", + "Banner 3" + ], + [ + "base64 encoded content3", + "image/gif", + "banner3.gif", + "inline", + "Banner 3" + ] +]; +$email->addAttachments($attachments); + +$email->setTemplateId("d-13b8f94fbcae4ec6b75270d6cb59f932"); + +$email->addGlobalHeader("X-Day", "Monday"); +$globalHeaders = [ + "X-Month" => "January", + "X-Year" => "2017" +]; +$email->addGlobalHeaders($globalHeaders); + +$email->addSection("%section1%", "Substitution for Section 1 Tag"); +$sections = [ + "%section3%" => "Substitution for Section 3 Tag", + "%section4%" => "Substitution for Section 4 Tag" +]; +$email->addSections($sections); + +$email->addCategory("Category 1"); +$categories = [ + "Category 2", + "Category 3" +]; +$email->addCategories($categories); + +$email->setBatchId( + "MWQxZmIyODYtNjE1Ni0xMWU1LWI3ZTUtMDgwMDI3OGJkMmY2LWEzMmViMjYxMw" +); + +$email->setReplyTo("dx+replyto2@example.com", "DX Team Reply To 2"); + +$email->setAsm(1, [1, 2, 3, 4]); + +$email->setIpPoolName("23"); + +// Mail Settings +$email->setBccSettings(true, "bcc@example.com"); +// Note: Bypass Spam, Bounce, and Unsubscribe management cannot +// be combined with Bypass List Management +$email->enableBypassBounceManagement(); +$email->enableBypassListManagement(); +$email->enableBypassSpamManagement(); +$email->enableBypassUnsubscribeManagement(); +//$email->disableBypassListManagement(); +$email->setFooter(true, "Footer", "Footer"); +$email->enableSandBoxMode(); +//$email->disableSandBoxMode(); +$email->setSpamCheck(true, 1, "http://mydomain.com"); + +// Tracking Settings +$email->setClickTracking(true, true); +$email->setOpenTracking(true, "--sub--"); +$email->setSubscriptionTracking( + true, + "subscribe", + "subscribe", + "%%sub%%" +); +$email->setGanalytics( + true, + "utm_source", + "utm_medium", + "utm_term", + "utm_content", + "utm_campaign" +); + +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + +OR + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\Asm; +use SendGrid\Mail\Attachment; +use SendGrid\Mail\BatchId; +use SendGrid\Mail\Bcc; +use SendGrid\Mail\BccSettings; +use SendGrid\Mail\BypassBounceManagement; +use SendGrid\Mail\BypassListManagement; +use SendGrid\Mail\BypassSpamManagement; +use SendGrid\Mail\BypassUnsubscribeManagement; +use SendGrid\Mail\Category; +use SendGrid\Mail\Cc; +use SendGrid\Mail\ClickTracking; +use SendGrid\Mail\Content; +use SendGrid\Mail\CustomArg; +use SendGrid\Mail\Footer; +use SendGrid\Mail\From; +use SendGrid\Mail\Ganalytics; +use SendGrid\Mail\GroupId; +use SendGrid\Mail\GroupsToDisplay; +use SendGrid\Mail\Header; +use SendGrid\Mail\HtmlContent; +use SendGrid\Mail\IpPoolName; +use SendGrid\Mail\Mail; +use SendGrid\Mail\MailSettings; +use SendGrid\Mail\OpenTracking; +use SendGrid\Mail\PlainTextContent; +use SendGrid\Mail\ReplyTo; +use SendGrid\Mail\SandBoxMode; +use SendGrid\Mail\Section; +use SendGrid\Mail\SendAt; +use SendGrid\Mail\SpamCheck; +use SendGrid\Mail\Subject; +use SendGrid\Mail\SubscriptionTracking; +use SendGrid\Mail\Substitution; +use SendGrid\Mail\TemplateId; +use SendGrid\Mail\To; +use SendGrid\Mail\TrackingSettings; + +$email = new Mail(); + +// For a detailed description of each of these settings, +// please see the +// [documentation](https://sendgrid.com/docs/API_Reference/api_v3.html). +$email->setSubject( + new Subject("Sending with Twilio SendGrid is Fun 2") +); + +$email->addTo(new To("test@example.com", "Example User")); +$email->addTo(new To("test+1@example.com", "Example User1")); +$toEmails = [ + new To("test+2@example.com", "Example User2"), + new To("test+3@example.com", "Example User3") +]; +$email->addTos($toEmails); + +$email->addCc(new Cc("test+4@example.com", "Example User4")); +$ccEmails = [ + new Cc("test+5@example.com", "Example User5"), + new Cc("test+6@example.com", "Example User6") +]; +$email->addCcs($ccEmails); + +$email->addBcc( + new Bcc("test+7@example.com", "Example User7") +); +$bccEmails = [ + new Bcc("test+8@example.com", "Example User8"), + new Bcc("test+9@example.com", "Example User9") +]; +$email->addBccs($bccEmails); + +$email->addHeader(new Header("X-Test1", "Test1")); +$email->addHeader(new Header("X-Test2", "Test2")); +$headers = [ + new Header("X-Test3", "Test3"), + new Header("X-Test4", "Test4") +]; +$email->addHeaders($headers); + +$email->addDynamicTemplateData( + new Substitution("subject1", "Example Subject 1") +); +$email->addDynamicTemplateData( + new Substitution("name", "Example Name 1") +); +$email->addDynamicTemplateData( + new Substitution("city1", "Denver") +); +$substitutions = [ + new Substitution("subject2", "Example Subject 2"), + new Substitution("name2", "Example Name 2"), + new Substitution("city2", "Orange") +]; +$email->addDynamicTemplateDatas($substitutions); + +$email->addCustomArg(new CustomArg("marketing1", "false")); +$email->addCustomArg(new CustomArg("transactional1", "true")); +$email->addCustomArg(new CustomArg("category", "name")); +$customArgs = [ + new CustomArg("marketing2", "true"), + new CustomArg("transactional2", "false"), + new CustomArg("category", "name") +]; +$email->addCustomArgs($customArgs); + +$email->setSendAt(new SendAt(1461775051)); + +// You can add a personalization index or personalization parameter to the above +// methods to add and update multiple personalizations. You can learn more about +// personalizations [here](https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/personalizations.html). + +// The values below this comment are global to an entire message + +$email->setFrom(new From("test@example.com", "Twilio SendGrid")); + +$email->setGlobalSubject( + new Subject("Sending with Twilio SendGrid is Fun and Global 2") +); + +$plainTextContent = new PlainTextContent( + "and easy to do anywhere, even with PHP" +); +$htmlContent = new HtmlContent( + "and easy to do anywhere, even with PHP" +); +$email->addContent($plainTextContent); +$email->addContent($htmlContent); +$contents = [ + new Content("text/calendar", "Party Time!!"), + new Content("text/calendar2", "Party Time 2!!") +]; +$email->addContents($contents); + +$email->addAttachment( + new Attachment( + "base64 encoded content1", + "image/png", + "banner.png", + "inline", + "Banner" + ) +); +$attachments = [ + new Attachment( + "base64 encoded content2", + "banner2.jpeg", + "image/jpeg", + "attachment", + "Banner 3" + ), + new Attachment( + "base64 encoded content3", + "banner3.gif", + "image/gif", + "inline", + "Banner 3" + ) +]; +$email->addAttachments($attachments); + +$email->setTemplateId( + new TemplateId("d-13b8f94fbcae4ec6b75270d6cb59f932") +); + +$email->addGlobalHeader(new Header("X-Day", "Monday")); +$globalHeaders = [ + new Header("X-Month", "January"), + new Header("X-Year", "2017") +]; +$email->addGlobalHeaders($globalHeaders); + +$email->addSection( + new Section( + "%section1%", + "Substitution for Section 1 Tag" + ) +); + +$sections = [ + new Section( + "%section3%", + "Substitution for Section 3 Tag" + ), + new Section( + "%section4%", + "Substitution for Section 4 Tag" + ) +]; +$email->addSections($sections); + +$email->addCategory(new Category("Category 1")); +$categories = [ + new Category("Category 2"), + new Category("Category 3") +]; +$email->addCategories($categories); + +$email->setBatchId( + new BatchId( + "MWQxZmIyODYtNjE1Ni0xMWU1LWI3ZTUtMDgwMDI3OGJkMmY2LWEzMmViMjYxMw" + ) +); + +$email->setReplyTo( + new ReplyTo( + "dx+replyto2@example.com", + "DX Team Reply To 2" + ) +); + +$asm = new Asm( + new GroupId(1), + new GroupsToDisplay([1,2,3,4]) +); +$email->setAsm($asm); + +$email->setIpPoolName(new IpPoolName("23")); + +$mail_settings = new MailSettings(); +$mail_settings->setBccSettings( + new BccSettings(true, "bcc@example.com") +); + +// Note: Bypass Spam, Bounce, and Unsubscribe management cannot +// be combined with Bypass List Management +$mail_settings->setBypassBounceManagement( + new BypassBounceManagement(true) +); +$mail_settings->setBypassSpamManagement( + new BypassSpamManagement(true) +); +$mail_settings->setBypassUnsubscribeManagement( + new BypassUnsubscribeManagement(true) +); + +// OR +$mail_settings->setBypassListManagement( + new BypassListManagement(true) +); + +$mail_settings->setFooter( + new Footer(true, "Footer", "Footer") +); +$mail_settings->setSandBoxMode(new SandBoxMode(true)); +$mail_settings->setSpamCheck( + new SpamCheck(true, 1, "http://mydomain.com") +); +$email->setMailSettings($mail_settings); + +$tracking_settings = new TrackingSettings(); +$tracking_settings->setClickTracking( + new ClickTracking(true, true) +); +$tracking_settings->setOpenTracking( + new OpenTracking(true, "--sub--") +); +$tracking_settings->setSubscriptionTracking( + new SubscriptionTracking( + true, + "subscribe", + "subscribe", + "%%sub%%" + ) +); +$tracking_settings->setGanalytics( + new Ganalytics( + true, + "utm_source", + "utm_medium", + "utm_term", + "utm_content", + "utm_campaign" + ) +); +$email->setTrackingSettings($tracking_settings); + +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + + +# Send an Email to a Single Recipient + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\Mail; + +$email = new Mail(); +$email->setFrom("test@example.com", "Example User"); +$email->setSubject("Sending with Twilio SendGrid is Fun"); +$email->addTo("test@example.com", "Example User"); +$email->addContent("text/plain", "and easy to do anywhere, even with PHP"); +$email->addContent( + "text/html", "and easy to do anywhere, even with PHP" +); +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + +OR + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\From; +use SendGrid\Mail\HtmlContent; +use SendGrid\Mail\Mail; +use SendGrid\Mail\PlainTextContent; +use SendGrid\Mail\Subject; +use SendGrid\Mail\To; + +$from = new From("test@example.com", "Example User"); +$subject = new Subject("Sending with Twilio SendGrid is Fun"); +$to = new To("test@example.com", "Example User"); +$plainTextContent = new PlainTextContent( + "and easy to do anywhere, even with PHP" +); +$htmlContent = new HtmlContent( + "and easy to do anywhere, even with PHP" +); +$email = new Mail( + $from, + $to, + $subject, + $plainTextContent, + $htmlContent +); +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + + +# Send an Email to Multiple Recipients + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\Mail; + +$email = new Mail(); +$email->setFrom("test@example.com", "Example User"); +$tos = [ + "test+test1@example.com" => "Example User1", + "test+test2@example.com" => "Example User2", + "test+test3@example.com" => "Example User3" +]; +$email->addTos($tos); +$email->setSubject("Sending with Twilio SendGrid is Fun"); +$email->addContent("text/plain", "and easy to do anywhere, even with PHP"); +$email->addContent( + "text/html", "and easy to do anywhere, even with PHP" +); +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + +OR + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\From; +use SendGrid\Mail\HtmlContent; +use SendGrid\Mail\Mail; +use SendGrid\Mail\PlainTextContent; +use SendGrid\Mail\Subject; +use SendGrid\Mail\To; + +$from = new From("test@example.com", "Example User"); +$tos = [ + new To("test+test1@example.com", "Example User1"), + new To("test+test2@example.com", "Example User2"), + new To("test+test3@example.com", "Example User3") +]; +$subject = new Subject("Sending with Twilio SendGrid is Fun"); +$plainTextContent = new PlainTextContent( + "and easy to do anywhere, even with PHP" +); +$htmlContent = new HtmlContent( + "and easy to do anywhere, even with PHP" +); +$email = new Mail( + $from, + $tos, + $subject, + $plainTextContent, + $htmlContent +); + +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + + +# Send Multiple Emails to Multiple Recipients + +Note that [transactional templates](#transactional-templates) may be a better option for this use case, especially for more complex uses. + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\From; +use SendGrid\Mail\HtmlContent; +use SendGrid\Mail\Mail; +use SendGrid\Mail\PlainTextContent; +use SendGrid\Mail\Subject; +use SendGrid\Mail\To; + +$from = new From("test@example.com", "Example User"); +$tos = [ + new To( + "test+test1@example.com", + "Example User1", + [ + '-name-' => 'Example User 1', + '-github-' => 'http://github.com/example_user1' + ], + "Subject 1 -name-" + ), + new To( + "test+test2@example.com", + "Example User2", + [ + '-name-' => 'Example User 2', + '-github-' => 'http://github.com/example_user2' + ], + "Subject 2 -name-" + ), + new To( + "test+test3@example.com", + "Example User3", + [ + '-name-' => 'Example User 3', + '-github-' => 'http://github.com/example_user3' + ] + ) +]; +$subject = new Subject("Hi -name-!"); // default subject +$globalSubstitutions = [ + '-time-' => "2018-05-03 23:10:29" +]; +$plainTextContent = new PlainTextContent( + "Hello -name-, your github is -github- sent at -time-" +); +$htmlContent = new HtmlContent( + "Hello -name-, your github is here sent at -time-" +); +$email = new Mail( + $from, + $tos, + $subject, // or array of subjects, these take precedence + $plainTextContent, + $htmlContent, + $globalSubstitutions +); + +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + +OR + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\From; +use SendGrid\Mail\HtmlContent; +use SendGrid\Mail\Mail; +use SendGrid\Mail\PlainTextContent; +use SendGrid\Mail\To; + +$from = new From("test@example.com", "Example User"); +$tos = [ + new To( + "test+test1@example.com", + "Example User1", + [ + '-name-' => 'Example User 1', + '-github-' => 'http://github.com/example_user1' + ], + "Example User1 -name-" + ), + new To( + "test+test2@example.com", + "Example User2", + [ + '-name-' => 'Example User 2', + '-github-' => 'http://github.com/example_user2' + ], + "Example User2 -name-" + ), + new To( + "test+test3@example.com", + "Example User3", + [ + '-name-' => 'Example User 3', + '-github-' => 'http://github.com/example_user3' + ] + ) +]; +$subject = [ + "Subject 1 -name-", + "Subject 2 -name-" +]; +$globalSubstitutions = [ + '-time-' => "2018-05-03 23:10:29" +]; +$plainTextContent = new PlainTextContent( + "Hello -name-, your github is -github- sent at -time-" +); +$htmlContent = new HtmlContent( + "Hello -name-, your github is here sent at -time-" +); +$email = new Mail( + $from, + $tos, + $subject, // or array of subjects, these take precedence + $plainTextContent, + $htmlContent, + $globalSubstitutions +); + +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + + +# Send Multiple Emails with Personalizations + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\From; +use SendGrid\Mail\Mail; +use SendGrid\Mail\Personalization; +use SendGrid\Mail\Subject; +use SendGrid\Mail\To; + +$from = new From("test@example.com", "Twilio Sendgrid"); +$to = new To( + "test+test1@example.com", + "Example User1", + [ + '-name-' => 'Example User 1' + ], + "Example User1 -name-" + ); +$subject = new Subject("Hello from Twilio Sendgrid!"); +$plainTextContent = new PlainTextContent( + "How's it going -name-?" +); +$htmlContent = new HtmlContent( + "How's it going -name-?" +); +$email = new Mail($from, $to, $subject, $plainTextContent, $htmlContent); + +$personalization0 = new Personalization(); +$personalization0->addTo(new To( + "test+test2@example.com", + "Example User2", + [ + '-name-' => 'Example User 2' + ], + "Example User2 -name-" +)); +$personalization0->addTo(new To( + "test+test3@example.com", + "Example User3", + [ + '-name-' => 'Example User 3' + ], + "Example User3 -name-" +)); +$personalization0->setSubject(new Subject("Hello from Twilio Sendgrid!")); +$email->addPersonalization($personalization0); + +$personalization1 = new Personalization(); +$personalization1->addTo(new To( + "test+test3@example.com", + "Example User4", + [ + '-name-' => 'Example User 4' + ], + "Example User4 -name-" +)); +$personalization1->addTo(new To( + "test+test4@example.com", + "Example User5", + [ + '-name-' => 'Example User 5' + ], + "Example User5 -name-" +)); +$personalization1->addFrom(new From( + "test5@example.com" => "Twilio" +)) +$personalization1->setSubject(new Subject("Hello from Twilio!")); +$mail->addPersonalization($personalization1); + +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + + +# Set Region +The SendGrid object can also be used to set the region to "eu", which will send the request to https://api.eu.sendgrid.com/. By default, it is set to https://api.sendgrid.com/, e.g. + +```php +/sendgrid-php.php'; + +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +$sendgrid->setDataResidency("eu"); + +OR + +$sendgrid->setDataResidency("global"); +``` + + +# Transactional Templates + +For this example, we assume you have created a [dynamic transactional template](https://sendgrid.com/docs/ui/sending-email/how-to-send-an-email-with-dynamic-transactional-templates/) in the UI or via the API. Following is the template content we used for testing. + +Template ID (replace with your own): + +```text +d-13b8f94fbcae4ec6b75270d6cb59f932 +``` + +Email Subject: + +```text +{{ subject }} +``` + +Template Body: + +```html + + + + + +Hello {{ name }}, +

+I'm glad you are trying out the template feature! +

+I hope you are having a great day in {{ city }} :) +

+ + +``` + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\From; +use SendGrid\Mail\To; +use SendGrid\Mail\Mail; + +$from = new From("test@example.com", "Example User"); +$tos = [ + new To( + "test+test1@example.com", + "Example User1", + [ + 'subject' => 'Subject 1', + 'name' => 'Example User 1', + 'city' => 'Denver' + ] + ), + new To( + "test+test2@example.com", + "Example User2", + [ + 'subject' => 'Subject 2', + 'name' => 'Example User 2', + 'city' => 'Irvine' + ] + ), + new To( + "test+test3@example.com", + "Example User3", + [ + 'subject' => 'Subject 3', + 'name' => 'Example User 3', + 'city' => 'Redwood City' + ] + ) +]; +$email = new Mail( + $from, + $tos +); +$email->setTemplateId("d-13b8f94fbcae4ec6b75270d6cb59f932"); +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + +OR + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\Mail; + +$email = new Mail(); +$email->setFrom("test@sendgrid.com", "Example User"); +$email->setSubject("I'm replacing the subject tag"); +$email->addTo( + "test+test1@example.com", + "Example User1", + [ + "subject" => "Subject 1", + "name" => "Example User 1", + "city" => "Denver" + ], + 0 +); +$email->addTo( + "test+test2@example.com", + "Example User2", + [ + "subject" => "Subject 2", + "name" => "Example User 2", + "city" => "Denver" + ], + 1 +); +$email->addTo( + "test+test3@example.com", + "Example User3", + [ + "subject" => "Subject 3", + "name" => "Example User 3", + "city" => "Redwood City" + ], + 2 +); +$email->setTemplateId("d-13b8f94fbcae4ec6b75270d6cb59f932"); +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + + +# Legacy Templates + +For this example, we assume you have created a [legacy transactional template](https://sendgrid.com/docs/User_Guide/Transactional_Templates/index.html) in the UI or via the API. Following is the template content we used for testing. + +Template ID (replace with your own): + +```text +13b8f94f-bcae-4ec6-b752-70d6cb59f932 +``` + +Email Subject: + +```text +<%subject%> +``` + +Template Body: + +```html + + + + + +Hello -name-, +

+I'm glad you are trying out the template feature! +

+<%body%> +

+I hope you are having a great day in -city- :) +

+ + +``` + +```php +/sendgrid-php.php'; + +use \SendGrid\Mail\From; +use \SendGrid\Mail\To; +use \SendGrid\Mail\Subject; +use \SendGrid\Mail\PlainTextContent; +use \SendGrid\Mail\HtmlContent; +use \SendGrid\Mail\Mail; + +$from = new From("test@example.com", "Example User"); +$tos = [ + new To( + "test+test1@example.com", + "Example User1", + [ + '-name-' => 'Example User 1', + '-city-' => 'Denver' + ] + ), + new To( + "test+test2@example.com", + "Example User2", + [ + '-name-' => 'Example User 2', + '-city-' => 'Irvine' + ] + ), + new To( + "test+test3@example.com", + "Example User3", + [ + '-name-' => 'Example User 3', + '-city-' => 'Redwood City' + ] + ) +]; +$subject = new Subject("I'm replacing the subject tag"); +$plainTextContent = new PlainTextContent( + "I'm replacing the **body tag**" +); +$htmlContent = new HtmlContent( + "I'm replacing the body tag" +); +$email = new Mail( + $from, + $tos, + $subject, + $plainTextContent, + $htmlContent +); +$email->setTemplateId("13b8f94f-bcae-4ec6-b752-70d6cb59f932"); +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + +OR + +```php +/sendgrid-php.php'; + +use SendGrid\Mail\Mail; + +$email = new Mail(); +$email->setFrom("test@sendgrid.com", "Example User"); +$email->setSubject("I'm replacing the subject tag"); +$email->addTo( + "test+test1@example.com", + "Example User1", + [ + "-name-" => "Example User 1", + "-city-" => "Denver" + ], + 0 +); +$email->addTo( + "test+test2@example.com", + "Example User2", + [ + "-name-" => "Example User 2", + "-city-" => "Denver" + ], + 1 +); +$email->addTo( + "test+test3@example.com", + "Example User3", + [ + "-name-" => "Example User 3", + "-city-" => "Redwood City" + ], + 2 +); +$email->addContent("text/plain", "I'm replacing the **body tag**"); +$email->addContent("text/html", "I'm replacing the body tag"); +$email->setTemplateId("13b8f94f-bcae-4ec6-b752-70d6cb59f932"); +$sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid->send($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: '. $e->getMessage(). "\n"; +} +``` + +# Send an Email With Twilio Email (Pilot) + +### 1. Obtain a Free Twilio Account + +Sign up for a free Twilio account [here](https://www.twilio.com/try-twilio?source=sendgrid-php). + +### 2. Set Up Your Environment Variables + +The Twilio API allows for authentication using with either an API key/secret or your Account SID/Auth Token. You can create an API key [here](https://twil.io/get-api-key) or obtain your Account SID and Auth Token [here](https://twil.io/console). + +Once you have those, follow the steps below based on your operating system. + +#### Linux/Mac + +```bash +echo "export TWILIO_API_KEY='YOUR_TWILIO_API_KEY'" > twilio.env +echo "export TWILIO_API_SECRET='YOUR_TWILIO_API_SECRET'" >> twilio.env + +# or + +echo "export TWILIO_ACCOUNT_SID='YOUR_TWILIO_ACCOUNT_SID'" > twilio.env +echo "export TWILIO_AUTH_TOKEN='YOUR_TWILIO_AUTH_TOKEN'" >> twilio.env +``` + +Then: + +```bash +echo "twilio.env" >> .gitignore +source ./twilio.env +``` + +#### Windows + +Temporarily set the environment variable (accessible only during the current CLI session): + +```bash +set TWILIO_API_KEY=YOUR_TWILIO_API_KEY +set TWILIO_API_SECRET=YOUR_TWILIO_API_SECRET + +: or + +set TWILIO_ACCOUNT_SID=YOUR_TWILIO_ACCOUNT_SID +set TWILIO_AUTH_TOKEN=YOUR_TWILIO_AUTH_TOKEN +``` + +Or permanently set the environment variable (accessible in all subsequent CLI sessions): + +```bash +setx TWILIO_API_KEY "YOUR_TWILIO_API_KEY" +setx TWILIO_API_SECRET "YOUR_TWILIO_API_SECRET" + +: or + +setx TWILIO_ACCOUNT_SID "YOUR_TWILIO_ACCOUNT_SID" +setx TWILIO_AUTH_TOKEN "YOUR_TWILIO_AUTH_TOKEN" +``` + +### 3. Initialize the Twilio Email Client + +```php +$twilioEmail = new \TwilioEmail(\getenv('TWILIO_API_KEY'), \getenv('TWILIO_API_SECRET')); + +// or + +$twilioEmail = new \TwilioEmail(\getenv('TWILIO_ACCOUNT_SID'), \getenv('TWILIO_AUTH_TOKEN')); +``` + +This client has the same interface as the `SendGrid` client. + +# Send an SMS Message + +First, follow the above steps for creating a Twilio account and setting up environment variables with the proper credentials. + +Then, install the Twilio Helper Library. + +```bash +composer require twilio/sdk +``` + +Finally, send a message. + +```php +messages->create( + '8881231234', // Text this number + [ + 'from' => '9991231234', // From a valid Twilio number + 'body' => 'Hello from Twilio!' + ] +); +``` + +For more information, please visit the [Twilio SMS PHP documentation](https://www.twilio.com/docs/sms/quickstart/php). + + +# How to Set up a Domain Authentication + +You can find documentation for how to setup a domain authentication via the UI [here](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/) and via API [here](USAGE.md#sender-authentication). + +Find more information about all of Twilio SendGrid's authentication related documentation [here](https://sendgrid.com/docs/ui/account-and-settings/). + + +# How to View Email Statistics + +You can find documentation for how to view your email statistics via the UI [here](https://app.sendgrid.com/statistics) and via API [here](USAGE.md#stats). + +Alternatively, we can post events to a URL of your choice via our [Event Webhook](https://sendgrid.com/docs/for-developers/tracking-events/event/) about events that occur as Twilio SendGrid processes your email. + + +# How to Use the Email Activity Feed API + +In order to gain access to the Email Activity Feed API, you must [purchase](https://app.sendgrid.com/settings/billing/addons/email_activity) additional email activity history. To learn about Sendgrid's API to download information from the Email Activity feed, get started [here](https://sendgrid.com/docs/API_Reference/Web_API_v3/Tutorials/getting_started_email_activity_api.html). + + +# Deploying to Heroku + +Use the button below to instantly setup your own Simple instance for sending email using SendGrid on Heroku. + + + Deploy + + + +# Google App Engine Installation + +Google App Engine installations with Composer require the creation of file `php.ini` in the base folder(the same directory as the `app.yaml` file). You can read more about this file [here](https://cloud.google.com/appengine/docs/standard/php/config/php_ini). + +The file `php.ini` should contain: + +```ini +google_app_engine.enable_curl_lite = 1 +``` diff --git a/lib/sendgrid-php/composer.lock b/lib/sendgrid-php/composer.lock new file mode 100644 index 000000000..e45db3dbd --- /dev/null +++ b/lib/sendgrid-php/composer.lock @@ -0,0 +1,2052 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "b8cdbfd36fe8771e1ec1488c63934b81", + "packages": [ + { + "name": "sendgrid/php-http-client", + "version": "4.1.1", + "source": { + "type": "git", + "url": "https://github.com/sendgrid/php-http-client.git", + "reference": "ec09bcfccabeb21d69e245a1e1c0e51f2813fc35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sendgrid/php-http-client/zipball/ec09bcfccabeb21d69e245a1e1c0e51f2813fc35", + "reference": "ec09bcfccabeb21d69e245a1e1c0e51f2813fc35", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": ">=7.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "~2.0" + }, + "suggest": { + "composer/ca-bundle": "Including this library will ensure that a valid CA bundle is available for secure connections" + }, + "type": "library", + "autoload": { + "psr-4": { + "SendGrid\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Bernier", + "email": "mbernier@twilio.com" + }, + { + "name": "Elmer Thomas", + "email": "ethomas@twilio.com" + } + ], + "description": "HTTP REST client, simplified for PHP", + "homepage": "http://github.com/sendgrid/php-http-client", + "keywords": [ + "api", + "fluent", + "http", + "rest", + "sendgrid" + ], + "support": { + "source": "https://github.com/sendgrid/php-http-client/tree/4.1.1" + }, + "time": "2023-12-14T08:50:59+00:00" + }, + { + "name": "starkbank/ecdsa", + "version": "0.0.5", + "source": { + "type": "git", + "url": "https://github.com/starkbank/ecdsa-php.git", + "reference": "484bedac47bac4012dc73df91da221f0a66845cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/starkbank/ecdsa-php/zipball/484bedac47bac4012dc73df91da221f0a66845cb", + "reference": "484bedac47bac4012dc73df91da221f0a66845cb", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/ellipticcurve.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "StarkBank", + "email": "developers@starkbank.com", + "homepage": "https://starkbank.com", + "role": "Developer" + } + ], + "description": "fast openSSL-compatible implementation of the Elliptic Curve Digital Signature Algorithm (ECDSA)", + "homepage": "https://github.com/starkbank/ecdsa-php", + "support": { + "issues": "https://github.com/starkbank/ecdsa-php/issues", + "source": "https://github.com/starkbank/ecdsa-php/tree/v0.0.5" + }, + "time": "2021-06-06T22:24:49+00:00" + } + ], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.0.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + }, + "time": "2024-03-05T20:51:40+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.10.67", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "16ddbe776f10da6a95ebd25de7c1dbed397dc493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/16ddbe776f10da6a95ebd25de7c1dbed397dc493", + "reference": "16ddbe776f10da6a95ebd25de7c1dbed397dc493", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2024-04-16T07:22:02+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.31", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:37:42+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.19", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", + "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.28", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2024-04-05T04:35:58+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:27:43+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:30:58+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:33:00+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T06:35:11+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-14T16:00:52+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.9.1", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "267a4405fff1d9c847134db3a3c92f1ab7f77909" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/267a4405fff1d9c847134db3a3c92f1ab7f77909", + "reference": "267a4405fff1d9c847134db3a3c92f1ab7f77909", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2024-03-31T21:03:09+00:00" + }, + { + "name": "swaggest/json-diff", + "version": "v3.10.5", + "source": { + "type": "git", + "url": "https://github.com/swaggest/json-diff.git", + "reference": "17bfc66b330f46e12a7e574133497a290cd79ba5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/swaggest/json-diff/zipball/17bfc66b330f46e12a7e574133497a290cd79ba5", + "reference": "17bfc66b330f46e12a7e574133497a290cd79ba5", + "shasum": "" + }, + "require": { + "ext-json": "*" + }, + "require-dev": { + "phperf/phpunit": "4.8.37" + }, + "type": "library", + "autoload": { + "psr-4": { + "Swaggest\\JsonDiff\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Viacheslav Poturaev", + "email": "vearutop@gmail.com" + } + ], + "description": "JSON diff/rearrange/patch/pointer library for PHP", + "support": { + "issues": "https://github.com/swaggest/json-diff/issues", + "source": "https://github.com/swaggest/json-diff/tree/v3.10.5" + }, + "time": "2023-11-17T11:12:46+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.3", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:36:25+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.3", + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/lib/sendgrid-php/examples/accesssettings/accesssettings.php b/lib/sendgrid-php/examples/accesssettings/accesssettings.php new file mode 100644 index 000000000..a409fe33e --- /dev/null +++ b/lib/sendgrid-php/examples/accesssettings/accesssettings.php @@ -0,0 +1,114 @@ +client->access_settings()->activity()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Add one or more IPs to the whitelist # +// POST /access_settings/whitelist # + +$request_body = json_decode('{ + "ips": [ + { + "ip": "192.168.1.1" + }, + { + "ip": "192.*.*.*" + }, + { + "ip": "192.168.1.3/32" + } + ] +}'); + +try { + $response = $sg->client->access_settings()->whitelist()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a list of currently whitelisted IPs # +// GET /access_settings/whitelist # + +try { + $response = $sg->client->access_settings()->whitelist()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Remove one or more IPs from the whitelist # +// DELETE /access_settings/whitelist # + +$request_body = json_decode('{ + "ids": [ + 1, + 2, + 3 + ] +}'); + +try { + $response = $sg->client->access_settings()->whitelist()->delete($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a specific whitelisted IP # +// GET /access_settings/whitelist/{rule_id} # + +$rule_id = "test_url_param"; + +try { + $response = $sg->client->access_settings()->whitelist()->_($rule_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Remove a specific IP from the whitelist # +// DELETE /access_settings/whitelist/{rule_id} # + +$rule_id = "test_url_param"; + +try { + $response = $sg->client->access_settings()->whitelist()->_($rule_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/alerts/alerts.php b/lib/sendgrid-php/examples/alerts/alerts.php new file mode 100644 index 000000000..b9a3a1954 --- /dev/null +++ b/lib/sendgrid-php/examples/alerts/alerts.php @@ -0,0 +1,88 @@ +client->alerts()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all alerts # +// GET /alerts # + +try { + $response = $sg->client->alerts()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update an alert # +// PATCH /alerts/{alert_id} # + +$request_body = json_decode('{ + "email_to": "example@example.com" +}'); +$alert_id = "test_url_param"; + +try { + $response = $sg->client->alerts()->_($alert_id)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a specific alert # +// GET /alerts/{alert_id} # + +$alert_id = "test_url_param"; + +try { + $response = $sg->client->alerts()->_($alert_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete an alert # +// DELETE /alerts/{alert_id} # + +$alert_id = "test_url_param"; + +try { + $response = $sg->client->alerts()->_($alert_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/apikeys/apikeys.php b/lib/sendgrid-php/examples/apikeys/apikeys.php new file mode 100644 index 000000000..2f33d0fbc --- /dev/null +++ b/lib/sendgrid-php/examples/apikeys/apikeys.php @@ -0,0 +1,116 @@ +client->api_keys()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all API Keys belonging to the authenticated user # +// GET /api_keys # + +$query_params = json_decode('{"limit": 1}'); + +try { + $response = $sg->client->api_keys()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update the name & scopes of an API Key # +// PUT /api_keys/{api_key_id} # + +$request_body = json_decode('{ + "name": "A New Hope", + "scopes": [ + "user.profile.read", + "user.profile.update" + ] +}'); +$api_key_id = "test_url_param"; + +try { + $response = $sg->client->api_keys()->_($api_key_id)->put($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update API keys # +// PATCH /api_keys/{api_key_id} # + +$request_body = json_decode('{ + "name": "A New Hope" +}'); +$api_key_id = "test_url_param"; + +try { + $response = $sg->client->api_keys()->_($api_key_id)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve an existing API Key # +// GET /api_keys/{api_key_id} # + +$api_key_id = "test_url_param"; + +try { + $response = $sg->client->api_keys()->_($api_key_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete API keys # +// DELETE /api_keys/{api_key_id} # + +$api_key_id = "test_url_param"; + +try { + $response = $sg->client->api_keys()->_($api_key_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/asm/asm.php b/lib/sendgrid-php/examples/asm/asm.php new file mode 100644 index 000000000..273dafd87 --- /dev/null +++ b/lib/sendgrid-php/examples/asm/asm.php @@ -0,0 +1,245 @@ +client->asm()->groups()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve information about multiple suppression groups # +// GET /asm/groups # + +$query_params = json_decode('{"id": 1}'); + +try { + $response = $sg->client->asm()->groups()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update a suppression group. # +// PATCH /asm/groups/{group_id} # + +$request_body = json_decode('{ + "description": "Suggestions for items our users might like.", + "id": 103, + "name": "Item Suggestions" +}'); +$group_id = "test_url_param"; + +try { + $response = $sg->client->asm()->groups()->_($group_id)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Get information on a single suppression group. # +// GET /asm/groups/{group_id} # + +$group_id = "test_url_param"; + +try { + $response = $sg->client->asm()->groups()->_($group_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a suppression group. # +// DELETE /asm/groups/{group_id} # + +$group_id = "test_url_param"; + +try { + $response = $sg->client->asm()->groups()->_($group_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Add suppressions to a suppression group # +// POST /asm/groups/{group_id}/suppressions # + +$request_body = json_decode('{ + "recipient_emails": [ + "test1@example.com", + "test2@example.com" + ] +}'); +$group_id = "test_url_param"; + +try { + $response = $sg->client->asm()->groups()->_($group_id)->suppressions()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all suppressions for a suppression group # +// GET /asm/groups/{group_id}/suppressions # + +$group_id = "test_url_param"; + +try { + $response = $sg->client->asm()->groups()->_($group_id)->suppressions()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Search for suppressions within a group # +// POST /asm/groups/{group_id}/suppressions/search # + +$request_body = json_decode('{ + "recipient_emails": [ + "exists1@example.com", + "exists2@example.com", + "doesnotexists@example.com" + ] +}'); +$group_id = "test_url_param"; + +try { + $response = $sg->client->asm()->groups()->_($group_id)->suppressions()->search()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a suppression from a suppression group # +// DELETE /asm/groups/{group_id}/suppressions/{email} # + +$group_id = "test_url_param"; +$email = "test_url_param"; + +try { + $response = $sg->client->asm()->groups()->_($group_id)->suppressions()->_($email)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all suppressions # +// GET /asm/suppressions # + + +try { + $response = $sg->client->asm()->suppressions()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Add recipient addresses to the global suppression group. # +// POST /asm/suppressions/global # + +$request_body = json_decode('{ + "recipient_emails": [ + "test1@example.com", + "test2@example.com" + ] +}'); + +try { + $response = $sg->client->asm()->suppressions()->global()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a Global Suppression # +// GET /asm/suppressions/global/{email} # + +$email = "test_url_param"; + +try { + $response = $sg->client->asm()->suppressions()->global()->_($email)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a Global Suppression # +// DELETE /asm/suppressions/global/{email} # + +$email = "test_url_param"; + +try { + $response = $sg->client->asm()->suppressions()->global()->_($email)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all suppression groups for an email address # +// GET /asm/suppressions/{email} # + +$email = "test_url_param"; + +try { + $response = $sg->client->asm()->suppressions()->_($email)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/browsers/browsers.php b/lib/sendgrid-php/examples/browsers/browsers.php new file mode 100644 index 000000000..9e50a994f --- /dev/null +++ b/lib/sendgrid-php/examples/browsers/browsers.php @@ -0,0 +1,23 @@ +client->browsers()->stats()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/campaigns/campaigns.php b/lib/sendgrid-php/examples/campaigns/campaigns.php new file mode 100644 index 000000000..f6768b13f --- /dev/null +++ b/lib/sendgrid-php/examples/campaigns/campaigns.php @@ -0,0 +1,210 @@ +

Check out our spring line!

", + "ip_pool": "marketing", + "list_ids": [ + 110, + 124 + ], + "plain_content": "Check out our spring line!", + "segment_ids": [ + 110 + ], + "sender_id": 124451, + "subject": "New Products for Spring!", + "suppression_group_id": 42, + "title": "March Newsletter" +}'); + +try { + $response = $sg->client->campaigns()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all Campaigns # +// GET /campaigns # + +$query_params = json_decode('{"limit": 1, "offset": 1}'); + +try { + $response = $sg->client->campaigns()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update a Campaign # +// PATCH /campaigns/{campaign_id} # + +$request_body = json_decode('{ + "categories": [ + "summer line" + ], + "html_content": "

Check out our summer line!

", + "plain_content": "Check out our summer line!", + "subject": "New Products for Summer!", + "title": "May Newsletter" +}'); +$campaign_id = "test_url_param"; + +try { + $response = $sg->client->campaigns()->_($campaign_id)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a single campaign # +// GET /campaigns/{campaign_id} # + +$campaign_id = "test_url_param"; + +try { + $response = $sg->client->campaigns()->_($campaign_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a Campaign # +// DELETE /campaigns/{campaign_id} # + +$campaign_id = "test_url_param"; + +try { + $response = $sg->client->campaigns()->_($campaign_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update a Scheduled Campaign # +// PATCH /campaigns/{campaign_id}/schedules # + +$request_body = json_decode('{ + "send_at": 1489451436 +}'); +$campaign_id = "test_url_param"; + +try { + $response = $sg->client->campaigns()->_($campaign_id)->schedules()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Schedule a Campaign # +// POST /campaigns/{campaign_id}/schedules # + +$request_body = json_decode('{ + "send_at": 1489771528 +}'); +$campaign_id = "test_url_param"; + +try { + $response = $sg->client->campaigns()->_($campaign_id)->schedules()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// View Scheduled Time of a Campaign # +// GET /campaigns/{campaign_id}/schedules # + +$campaign_id = "test_url_param"; + +try { + $response = $sg->client->campaigns()->_($campaign_id)->schedules()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Unschedule a Scheduled Campaign # +// DELETE /campaigns/{campaign_id}/schedules # + +$campaign_id = "test_url_param"; + +try { + $response = $sg->client->campaigns()->_($campaign_id)->schedules()->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Send a Campaign # +// POST /campaigns/{campaign_id}/schedules/now # + +$campaign_id = "test_url_param"; + +try { + $response = $sg->client->campaigns()->_($campaign_id)->schedules()->now()->post(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Send a Test Campaign # +// POST /campaigns/{campaign_id}/schedules/test # + +$request_body = json_decode('{ + "to": "your.email@example.com" +}'); +$campaign_id = "test_url_param"; + +try { + $response = $sg->client->campaigns()->_($campaign_id)->schedules()->test()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/categories/categories.php b/lib/sendgrid-php/examples/categories/categories.php new file mode 100644 index 000000000..7510026aa --- /dev/null +++ b/lib/sendgrid-php/examples/categories/categories.php @@ -0,0 +1,53 @@ +client->categories()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve Email Statistics for Categories # +// GET /categories/stats # + +$query_params = json_decode('{"end_date": "2016-04-01", "aggregated_by": "day", "limit": 1, "offset": 1, "start_date": "2016-01-01", "categories": "test_string"}'); + +try { + $response = $sg->client->categories()->stats()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve sums of email stats for each category [Needs: Stats object defined, has category ID?] # +// GET /categories/stats/sums # + +$query_params = json_decode('{"end_date": "2016-04-01", "aggregated_by": "day", "limit": 1, "sort_by_metric": "test_string", "offset": 1, "start_date": "2016-01-01", "sort_by_direction": "asc"}'); + +try { + $response = $sg->client->categories()->stats()->sums()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/clients/clients.php b/lib/sendgrid-php/examples/clients/clients.php new file mode 100644 index 000000000..a4d85c9a5 --- /dev/null +++ b/lib/sendgrid-php/examples/clients/clients.php @@ -0,0 +1,39 @@ +client->clients()->stats()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve stats by a specific client type. # +// GET /clients/{client_type}/stats # + +$query_params = json_decode('{"aggregated_by": "day", "start_date": "2016-01-01", "end_date": "2016-04-01"}'); +$client_type = "test_url_param"; + +try { + $response = $sg->client->clients()->_($client_type)->stats()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/contactdb/contactdb.php b/lib/sendgrid-php/examples/contactdb/contactdb.php new file mode 100644 index 000000000..9d0387dab --- /dev/null +++ b/lib/sendgrid-php/examples/contactdb/contactdb.php @@ -0,0 +1,546 @@ +client->contactdb()->custom_fields()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all custom fields # +// GET /contactdb/custom_fields # + +try { + $response = $sg->client->contactdb()->custom_fields()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a Custom Field # +// GET /contactdb/custom_fields/{custom_field_id} # + +$custom_field_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->custom_fields()->_($custom_field_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a Custom Field # +// DELETE /contactdb/custom_fields/{custom_field_id} # + +$custom_field_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->custom_fields()->_($custom_field_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Create a List # +// POST /contactdb/lists # + +$request_body = json_decode('{ + "name": "your list name" +}'); + +try { + $response = $sg->client->contactdb()->lists()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all lists # +// GET /contactdb/lists # + +try { + $response = $sg->client->contactdb()->lists()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete Multiple lists # +// DELETE /contactdb/lists # + +$request_body = json_decode('[ + 1, + 2, + 3, + 4 +]'); + +try { + $response = $sg->client->contactdb()->lists()->delete($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update a List # +// PATCH /contactdb/lists/{list_id} # + +$request_body = json_decode('{ + "name": "newlistname" +}'); +$query_params = json_decode('{"list_id": 1}'); +$list_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->lists()->_($list_id)->patch($request_body, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a single list # +// GET /contactdb/lists/{list_id} # + +$query_params = json_decode('{"list_id": 1}'); +$list_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->lists()->_($list_id)->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a List # +// DELETE /contactdb/lists/{list_id} # + +$query_params = json_decode('{"delete_contacts": "true"}'); +$list_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->lists()->_($list_id)->delete(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Add Multiple Recipients to a List # +// POST /contactdb/lists/{list_id}/recipients # + +$request_body = json_decode('[ + "recipient_id1", + "recipient_id2" +]'); +$list_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->lists()->_($list_id)->recipients()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all recipients on a List # +// GET /contactdb/lists/{list_id}/recipients # + +$query_params = json_decode('{"page": 1, "page_size": 1, "list_id": 1}'); +$list_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->lists()->_($list_id)->recipients()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Add a Single Recipient to a List # +// POST /contactdb/lists/{list_id}/recipients/{recipient_id} # + +$list_id = "test_url_param"; +$recipient_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->lists()->_($list_id)->recipients()->_($recipient_id)->post(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a Single Recipient from a Single List # +// DELETE /contactdb/lists/{list_id}/recipients/{recipient_id} # + +$query_params = json_decode('{"recipient_id": 1, "list_id": 1}'); +$list_id = "test_url_param"; +$recipient_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->lists()->_($list_id)->recipients()->_($recipient_id)->delete(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update Recipient # +// PATCH /contactdb/recipients # + +$request_body = json_decode('[ + { + "email": "jones@example.com", + "first_name": "Guy", + "last_name": "Jones" + } +]'); + +try { + $response = $sg->client->contactdb()->recipients()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Add recipients # +// POST /contactdb/recipients # + +$request_body = json_decode('[ + { + "age": 25, + "email": "example@example.com", + "first_name": "", + "last_name": "User" + }, + { + "age": 25, + "email": "example2@example.com", + "first_name": "Example", + "last_name": "User" + } +]'); + +try { + $response = $sg->client->contactdb()->recipients()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve recipients # +// GET /contactdb/recipients # + +$query_params = json_decode('{"page": 1, "page_size": 1}'); + +try { + $response = $sg->client->contactdb()->recipients()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete Recipient # +// DELETE /contactdb/recipients # + +$request_body = json_decode('[ + "recipient_id1", + "recipient_id2" +]'); + +try { + $response = $sg->client->contactdb()->recipients()->delete($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve the count of billable recipients # +// GET /contactdb/recipients/billable_count # + +try { + $response = $sg->client->contactdb()->recipients()->billable_count()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a Count of Recipients # +// GET /contactdb/recipients/count # + +try { + $response = $sg->client->contactdb()->recipients()->count()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve recipients matching search criteria # +// GET /contactdb/recipients/search # + +$query_params = json_decode('{"{field_name}": "test_string"}'); + +try { + $response = $sg->client->contactdb()->recipients()->search()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a single recipient # +// GET /contactdb/recipients/{recipient_id} # + +$recipient_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->recipients()->_($recipient_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a Recipient # +// DELETE /contactdb/recipients/{recipient_id} # + +$recipient_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->recipients()->_($recipient_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve the lists that a recipient is on # +// GET /contactdb/recipients/{recipient_id}/lists # + +$recipient_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->recipients()->_($recipient_id)->lists()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve reserved fields # +// GET /contactdb/reserved_fields # + +try { + $response = $sg->client->contactdb()->reserved_fields()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Create a Segment # +// POST /contactdb/segments # + +$request_body = json_decode('{ + "conditions": [ + { + "and_or": "", + "field": "last_name", + "operator": "eq", + "value": "Miller" + }, + { + "and_or": "and", + "field": "last_clicked", + "operator": "gt", + "value": "01/02/2015" + }, + { + "and_or": "or", + "field": "clicks.campaign_identifier", + "operator": "eq", + "value": "513" + } + ], + "list_id": 4, + "name": "Last Name Miller" +}'); + +try { + $response = $sg->client->contactdb()->segments()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all segments # +// GET /contactdb/segments # + +try { + $response = $sg->client->contactdb()->segments()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update a segment # +// PATCH /contactdb/segments/{segment_id} # + +$request_body = json_decode('{ + "conditions": [ + { + "and_or": "", + "field": "last_name", + "operator": "eq", + "value": "Miller" + } + ], + "list_id": 5, + "name": "The Millers" +}'); +$query_params = json_decode('{"segment_id": "test_string"}'); +$segment_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->segments()->_($segment_id)->patch($request_body, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a segment # +// GET /contactdb/segments/{segment_id} # + +$query_params = json_decode('{"segment_id": 1}'); +$segment_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->segments()->_($segment_id)->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a segment # +// DELETE /contactdb/segments/{segment_id} # + +$query_params = json_decode('{"delete_contacts": "true"}'); +$segment_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->segments()->_($segment_id)->delete(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve recipients on a segment # +// GET /contactdb/segments/{segment_id}/recipients # + +$query_params = json_decode('{"page": 1, "page_size": 1}'); +$segment_id = "test_url_param"; + +try { + $response = $sg->client->contactdb()->segments()->_($segment_id)->recipients()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/dataresidency/setregion.php b/lib/sendgrid-php/examples/dataresidency/setregion.php new file mode 100644 index 000000000..b51f17e70 --- /dev/null +++ b/lib/sendgrid-php/examples/dataresidency/setregion.php @@ -0,0 +1,75 @@ +client->mail()->send()->post($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo $exceptionMessage, $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// sending to EU data residency + +$sendgrid_eu = buildSendgridObject("eu"); + +try { + $response = $sendgrid_eu->client->mail()->send()->post($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo $exceptionMessage, $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// not configuring any region defaults to global +$sendgrid_default = new \SendGrid(getenv('SENDGRID_API_KEY')); +try { + $response = $sendgrid_default->client->mail()->send()->post($email); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo $exceptionMessage, $e->getMessage(), "\n"; +} + +function buildHelloEmail(): Mail +{ + $email = new Mail(); + $email->setFrom("test@example.com", "test"); + $email->setSubject("Sending with Twilio SendGrid is Fun"); + $email->addTo("test@example.co", "test"); + $email->addContent("text/plain", "and easy to do anywhere, even with PHP"); + $email->addContent( + "text/html", "and easy to do anywhere, even with PHP" + ); + $objPersonalization = new Personalization(); + + $objTo = new To('foo@bar.com', 'foo bar'); + $objPersonalization->addTo($objTo); + $email->addPersonalization($objPersonalization); + return $email; +} + +function buildSendgridObject($region): SendGrid +{ + $sendgrid = new \SendGrid(getenv('SENDGRID_API_KEY')); + $sendgrid->setDataResidency($region); + return $sendgrid; +} diff --git a/lib/sendgrid-php/examples/devices/devices.php b/lib/sendgrid-php/examples/devices/devices.php new file mode 100644 index 000000000..ffe0878f0 --- /dev/null +++ b/lib/sendgrid-php/examples/devices/devices.php @@ -0,0 +1,23 @@ +client->devices()->stats()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/geo/geo.php b/lib/sendgrid-php/examples/geo/geo.php new file mode 100644 index 000000000..a97146ab7 --- /dev/null +++ b/lib/sendgrid-php/examples/geo/geo.php @@ -0,0 +1,23 @@ +client->geo()->stats()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/helpers/contacts/recipients.php b/lib/sendgrid-php/examples/helpers/contacts/recipients.php new file mode 100644 index 000000000..dd5f8daf7 --- /dev/null +++ b/lib/sendgrid-php/examples/helpers/contacts/recipients.php @@ -0,0 +1,55 @@ + 'Test', + 'last-name' => 'Tester', + 'email' => 'test@test.com' + ); + + $firstName = $post_body['first-name']; + $lastName = $post_body['last-name']; + $email = $post_body['email']; + $recipient = new \SendGrid\Contacts\Recipient($firstName, $lastName, $email); + // $request_body = json_encode(array($recipient)); + $request_body = json_decode( + '[ + { + "email": "' . $recipient->getEmail() . '", + "first_name": "' . $recipient->getFirstName() . '", + "last_name": "' . $recipient->getLastName() . '" + } + ]' + ); + + try { + $response = $sg->client->contactdb()->recipients()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; + } catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; + } +} + +buildRecipientForm(); // This will build and output an HTML form +recipientFormSubmit(); // This will simulate a form submission and will output the response. diff --git a/lib/sendgrid-php/examples/helpers/eventwebhook/example.php b/lib/sendgrid-php/examples/helpers/eventwebhook/example.php new file mode 100644 index 000000000..62e674ff2 --- /dev/null +++ b/lib/sendgrid-php/examples/helpers/eventwebhook/example.php @@ -0,0 +1,20 @@ +convertPublicKeyToECDSA($publicKey); + + return $eventWebhook->verifySignature( + $ecPublicKey, + $request->getContent(), + $request->header(EventWebhookHeader::SIGNATURE), + $request->header(EventWebhookHeader::TIMESTAMP) + ); +} diff --git a/lib/sendgrid-php/examples/helpers/mail/example.php b/lib/sendgrid-php/examples/helpers/mail/example.php new file mode 100644 index 000000000..be3ead25f --- /dev/null +++ b/lib/sendgrid-php/examples/helpers/mail/example.php @@ -0,0 +1,272 @@ +addTo(new To("test2@example.com")); + $personalization->addFrom(new From("test3@example.com", "Twilio Sendgrid")); + $mail->addPersonalization($personalization); + + //echo json_encode($mail, JSON_PRETTY_PRINT), "\n"; + return $mail; + } catch (\Exception $e) { + echo $e->getMessage(); + } + + return null; +} + +function kitchenSink() +{ + try { + $from = new From("test@example.com", "Twilio SendGrid"); + $subject = "Hello World from the Twilio SendGrid PHP Library"; + $to = new To("test1@example.com", "Example User"); + $content = new Content("text/plain", "some text here"); + + $mail = new Mail($from, $to, $subject, $content); + + $personalization0 = new Personalization(); + $personalization0->addTo(new To("test2@example.com", "Example User")); + $personalization0->addFrom(new From("test3@example.com", "Twilio SendGrid")); + $personalization0->addCc(new Cc("test4@example.com", "Example User")); + $personalization0->addCc(new Cc("test5@example.com", "Example User")); + $personalization0->addBcc(new Bcc("test6@example.com", "Example User")); + $personalization0->addBcc(new Bcc("test7@example.com", "Example User")); + $personalization0->setSubject(new Subject("Hello World from the Twilio SendGrid PHP Library")); + $personalization0->addHeader(new Header("X-Test", "test")); + $personalization0->addHeader(new Header("X-Mock", "true")); + $personalization0->addSubstitution("%name%", "Example User"); + $personalization0->addSubstitution("%city%", "Denver"); + $personalization0->addSubstitution("%sec1%", "%section1%"); + $personalization0->addCustomArg(new CustomArg("user_id", "343")); + $personalization0->addCustomArg(new CustomArg("type", "marketing")); + $personalization0->setSendAt(new SendAt(1443636843)); + $mail->addPersonalization($personalization0); + + $personalization1 = new Personalization(); + $personalization1->addTo(new To("test8@example.com", "Example User")); + $personalization1->addTo(new To("test9@example.com", "Example User")); + $personalization1->addFrom(new From("test10@example.com", "Twilio SendGrid")); + $personalization1->addCc(new Cc("test11@example.com", "Example User")); + $personalization1->addCc(new Cc("test12@example.com", "Example User")); + $personalization1->addBcc(new Bcc("test13@example.com", "Example User")); + $personalization1->addBcc(new Bcc("test14@example.com", "Example User")); + $personalization1->setSubject(new Subject("Hello World from the Twilio SendGrid PHP Library")); + $personalization1->addHeader(new Header("X-Test", "test")); + $personalization1->addHeader(new Header("X-Mock", "true")); + $personalization1->addSubstitution("%name%", "Example User"); + $personalization1->addSubstitution("%city%", "Denver"); + $personalization1->addSubstitution("%sec2%", "%section2%"); + $personalization1->addCustomArg(new CustomArg("user_id", "343")); + $personalization1->addCustomArg(new CustomArg("type", "marketing")); + $personalization1->setSendAt(new SendAt(1443636843)); + $mail->addPersonalization($personalization1); + + // Examples of adding personalization by specifying personalization indexes + $mail->addCc("test15@example.com", "Example User", null, 0); + $mail->addBcc("test16@example.com", "Example User", null, 1); + + $content = new Content("text/html", "some text here"); + $mail->addContent($content); + + $attachment = new Attachment(); + $attachment->setContent("TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gQ3JhcyBwdW12"); + $attachment->setType("application/pdf"); + $attachment->setFilename("balance_001.pdf"); + $attachment->setDisposition("attachment"); + $attachment->setContentId("Balance Sheet"); + $mail->addAttachment($attachment); + + $attachment2 = new Attachment(); + $attachment2->setContent("BwdW"); + $attachment2->setType("image/png"); + $attachment2->setFilename("banner.png"); + $attachment2->setDisposition("inline"); + $attachment2->setContentId("Banner"); + $mail->addAttachment($attachment2); + + $mail->setTemplateId("439b6d66-4408-4ead-83de-5c83c2ee313a"); + + # This must be a valid [batch ID](https://sendgrid.com/docs/API_Reference/SMTP_API/scheduling_parameters.html) to work + # $mail->setBatchID("sendgrid_batch_id"); + + $mail->addSection("%section1%", "Substitution Text for Section 1"); + $mail->addSection("%section2%", "Substitution Text for Section 2"); + + $mail->addHeader("X-Test1", "1"); + $mail->addHeader("X-Test2", "2"); + + $mail->addCategory("May"); + $mail->addCategory("2016"); + + $mail->addCustomArg("campaign", "welcome"); + $mail->addCustomArg("weekday", "morning"); + + $mail->setSendAt(1443636842); + + $asm = new ASM(); + $asm->setGroupId(99); + $asm->setGroupsToDisplay([4,5,6,7,8]); + $mail->setASM($asm); + + $mail->setIpPoolName("23"); + + $mail_settings = new MailSettings(); + $bcc_settings = new BccSettings(); + $bcc_settings->setEnable(true); + $bcc_settings->setEmail("test@example.com"); + $mail_settings->setBccSettings($bcc_settings); + $sandbox_mode = new SandBoxMode(); + $sandbox_mode->setEnable(true); + $mail_settings->setSandboxMode($sandbox_mode); + // Note: Bypass Spam, Bounce, and Unsubscribe management cannot + // be combined with Bypass List Management + $bypass_bounce_management = new BypassBounceManagement(); + $bypass_bounce_management->setEnable(true); + $mail_settings->setBypassBounceManagement($bypass_bounce_management); + $bypass_list_management = new BypassListManagement(); + $bypass_list_management->setEnable(true); + $mail_settings->setBypassListManagement($bypass_list_management); + $bypass_spam_management = new BypassSpamManagement(); + $bypass_spam_management->setEnable(true); + $mail_settings->setBypassSpamManagement($bypass_spam_management); + $bypass_unsubscribe_management = new BypassUnsubscribeManagement(); + $bypass_unsubscribe_management->setEnable(true); + $mail_settings->setBypassUnsubscribeManagement($bypass_unsubscribe_management); + $footer = new Footer(); + $footer->setEnable(true); + $footer->setText("Footer Text"); + $footer->setHtml("Footer Text"); + $mail_settings->setFooter($footer); + $spam_check = new SpamCheck(); + $spam_check->setEnable(true); + $spam_check->setThreshold(1); + $spam_check->setPostToUrl("https://spamcatcher.sendgrid.com"); + $mail_settings->setSpamCheck($spam_check); + $mail->setMailSettings($mail_settings); + + $tracking_settings = new TrackingSettings(); + $click_tracking = new ClickTracking(); + $click_tracking->setEnable(true); + $click_tracking->setEnableText(true); + $tracking_settings->setClickTracking($click_tracking); + $open_tracking = new OpenTracking(); + $open_tracking->setEnable(true); + $open_tracking->setSubstitutionTag("Optional tag to replace with the open image in the body of the message"); + $tracking_settings->setOpenTracking($open_tracking); + $subscription_tracking = new SubscriptionTracking(); + $subscription_tracking->setEnable(true); + $subscription_tracking->setText("text to insert into the text/plain portion of the message"); + $subscription_tracking->setHtml("html to insert into the text/html portion of the message"); + $subscription_tracking->setSubstitutionTag("Optional tag to replace with the open image in the body of the message"); + $tracking_settings->setSubscriptionTracking($subscription_tracking); + $ganalytics = new Ganalytics(); + $ganalytics->setEnable(true); + $ganalytics->setCampaignSource("some source"); + $ganalytics->setCampaignTerm("some term"); + $ganalytics->setCampaignContent("some content"); + $ganalytics->setCampaignName("some name"); + $ganalytics->setCampaignMedium("some medium"); + $tracking_settings->setGanalytics($ganalytics); + $mail->setTrackingSettings($tracking_settings); + + $reply_to = new ReplyTo("test@example.com", "Optional Name"); + $mail->setReplyTo($reply_to); + + //echo json_encode($mail, JSON_PRETTY_PRINT), "\n"; + return $mail; + } catch (\Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; + } + + return null; +} + +function sendHelloEmail() +{ + $apiKey = getenv('SENDGRID_API_KEY'); + $sg = new \SendGrid($apiKey); + + $request_body = helloEmail(); + + if (!($request_body instanceof Mail)) { + echo 'Invalid request_body to send HelloEmail', "\n"; + return; + } + + try { + $response = $sg->client->mail()->send()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; + } catch (\Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; + } +} + +function sendKitchenSink() +{ + $apiKey = getenv('SENDGRID_API_KEY'); + $sg = new \SendGrid($apiKey); + + $request_body = kitchenSink(); + + if (!($request_body instanceof Mail)) { + echo 'Invalid request_body to send KitchenSink', "\n"; + return; + } + + try { + $response = $sg->client->mail()->send()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; + } catch (\Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; + } +} + +sendHelloEmail(); // this will actually send an email +sendKitchenSink(); // this will only send an email if you set SandBox Mode to false diff --git a/lib/sendgrid-php/examples/helpers/stats/example.php b/lib/sendgrid-php/examples/helpers/stats/example.php new file mode 100644 index 000000000..b56228ff1 --- /dev/null +++ b/lib/sendgrid-php/examples/helpers/stats/example.php @@ -0,0 +1,29 @@ +client->categories()->post(['category' => 'cat2']); +//$response = $sg->client->categories()->get(null, $query_params); +$globalResponse = $sg->client->stats()->get(null, $stats->getGlobal()); + +$categoryResponse = $sg->client->categories()->stats()->get(null, $stats->getCategory(['category1', 'category2'])); +$categorySumResponse = $sg->client->categories()->stats()->sums()->get(null, $stats->getSum()); + +$subuserResponse = $sg->client->subusers()->stats()->get(null, $stats->getSubuser(['user1', 'user2'])); +$subuserSumResponse = $sg->client->subusers()->stats()->sums()->get(null, $stats->getSum()); +$subuserMonthlyResponse = $sg->client->subusers()->stats()->monthly()->get(null, $stats->getSubuserMonthly()); + +var_dump($globalResponse); +var_dump($categoryResponse); +var_dump($categoryResponse); +var_dump($subuserResponse); +var_dump($subuserSumResponse); +var_dump($subuserMonthlyResponse); diff --git a/lib/sendgrid-php/examples/ips/ips.php b/lib/sendgrid-php/examples/ips/ips.php new file mode 100644 index 000000000..33973e02f --- /dev/null +++ b/lib/sendgrid-php/examples/ips/ips.php @@ -0,0 +1,223 @@ +client->ips()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all assigned IPs # +// GET /ips/assigned # + +try { + $response = $sg->client->ips()->assigned()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Create an IP pool. # +// POST /ips/pools # + +$request_body = json_decode('{ + "name": "marketing" +}'); + +try { + $response = $sg->client->ips()->pools()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all IP pools. # +// GET /ips/pools # + +try { + $response = $sg->client->ips()->pools()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update an IP pools name. # +// PUT /ips/pools/{pool_name} # + +$request_body = json_decode('{ + "name": "new_pool_name" +}'); +$pool_name = "test_url_param"; + +try { + $response = $sg->client->ips()->pools()->_($pool_name)->put($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all IPs in a specified pool. # +// GET /ips/pools/{pool_name} # + +$pool_name = "test_url_param"; + +try { + $response = $sg->client->ips()->pools()->_($pool_name)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete an IP pool. # +// DELETE /ips/pools/{pool_name} # + +$pool_name = "test_url_param"; + +try { + $response = $sg->client->ips()->pools()->_($pool_name)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Add an IP address to a pool # +// POST /ips/pools/{pool_name}/ips # + +$request_body = json_decode('{ + "ip": "0.0.0.0" +}'); +$pool_name = "test_url_param"; + +try { + $response = $sg->client->ips()->pools()->_($pool_name)->ips()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Remove an IP address from a pool. # +// DELETE /ips/pools/{pool_name}/ips/{ip} # + +$pool_name = "test_url_param"; +$ip = "test_url_param"; + +try { + $response = $sg->client->ips()->pools()->_($pool_name)->ips()->_($ip)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Add an IP to warmup # +// POST /ips/warmup # + +$request_body = json_decode('{ + "ip": "0.0.0.0" +}'); + +try { + $response = $sg->client->ips()->warmup()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all IPs currently in warmup # +// GET /ips/warmup # + +try { + $response = $sg->client->ips()->warmup()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve warmup status for a specific IP address # +// GET /ips/warmup/{ip_address} # + +$ip_address = "test_url_param"; + +try { + $response = $sg->client->ips()->warmup()->_($ip_address)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Remove an IP from warmup # +// DELETE /ips/warmup/{ip_address} # + +$ip_address = "test_url_param"; + +try { + $response = $sg->client->ips()->warmup()->_($ip_address)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all IP pools an IP address belongs to # +// GET /ips/{ip_address} # + +$ip_address = "test_url_param"; + +try { + $response = $sg->client->ips()->_($ip_address)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/mail/mail.php b/lib/sendgrid-php/examples/mail/mail.php new file mode 100644 index 000000000..84463f40a --- /dev/null +++ b/lib/sendgrid-php/examples/mail/mail.php @@ -0,0 +1,198 @@ +client->mail()->batch()->post(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Validate batch ID # +// GET /mail/batch/{batch_id} # + +$batch_id = "test_url_param"; + +try { + $response = $sg->client->mail()->batch()->_($batch_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// v3 Mail Send # +// POST /mail/send # +// This endpoint has a helper, check it out [here](https://github.com/sendgrid/sendgrid-php/blob/HEAD/lib/mail/README.md). + +$request_body = json_decode('{ + "asm": { + "group_id": 1, + "groups_to_display": [ + 1, + 2, + 3 + ] + }, + "attachments": [ + { + "content": "[BASE64 encoded content block here]", + "content_id": "ii_139db99fdb5c3704", + "disposition": "inline", + "filename": "file1.jpg", + "name": "file1", + "type": "image/jpeg" + } + ], + "batch_id": "[YOUR BATCH ID GOES HERE]", + "categories": [ + "category1", + "category2" + ], + "content": [ + { + "type": "text/html", + "value": "

Hello, world!

" + } + ], + "custom_args": { + "New Argument 1": "New Value 1", + "activationAttempt": "1", + "customerAccountNumber": "[CUSTOMER ACCOUNT NUMBER GOES HERE]" + }, + "from": { + "email": "sam.smith@example.com", + "name": "Sam Smith" + }, + "headers": {}, + "ip_pool_name": "[YOUR POOL NAME GOES HERE]", + "mail_settings": { + "bcc": { + "email": "ben.doe@example.com", + "enable": true + }, + "bypass_bounce_management": { + "enable": true + }, + "bypass_list_management": { + "enable": true + }, + "bypass_spam_management": { + "enable": true + }, + "bypass_unsubscribe_management": { + "enable": true + }, + "footer": { + "enable": true, + "html": "

Thanks
The Twilio SendGrid Team

", + "text": "Thanks,/n The Twilio SendGrid Team" + }, + "sandbox_mode": { + "enable": false + }, + "spam_check": { + "enable": true, + "post_to_url": "http://example.com/compliance", + "threshold": 3 + } + }, + "personalizations": [ + { + "bcc": [ + { + "email": "sam.doe@example.com", + "name": "Sam Doe" + } + ], + "cc": [ + { + "email": "jane.doe@example.com", + "name": "Jane Doe" + } + ], + "custom_args": { + "New Argument 1": "New Value 1", + "activationAttempt": "1", + "customerAccountNumber": "[CUSTOMER ACCOUNT NUMBER GOES HERE]" + }, + "headers": { + "X-Accept-Language": "en", + "X-Mailer": "MyApp" + }, + "send_at": 1409348513, + "subject": "Hello, World!", + "substitutions": { + "id": "substitutions", + "type": "object" + }, + "to": [ + { + "email": "john.doe@example.com", + "name": "John Doe" + } + ] + } + ], + "reply_to": { + "email": "sam.smith@example.com", + "name": "Sam Smith" + }, + "sections": { + "section": { + ":sectionName1": "section 1 text", + ":sectionName2": "section 2 text" + } + }, + "send_at": 1409348513, + "subject": "Hello, World!", + "template_id": "[YOUR TEMPLATE ID GOES HERE]", + "tracking_settings": { + "click_tracking": { + "enable": true, + "enable_text": true + }, + "ganalytics": { + "enable": true, + "utm_campaign": "[NAME OF YOUR REFERRER SOURCE]", + "utm_content": "[USE THIS SPACE TO DIFFERENTIATE YOUR EMAIL FROM ADS]", + "utm_medium": "[NAME OF YOUR MARKETING MEDIUM e.g. email]", + "utm_name": "[NAME OF YOUR CAMPAIGN]", + "utm_term": "[IDENTIFY PAID KEYWORDS HERE]" + }, + "open_tracking": { + "enable": true, + "substitution_tag": "%opentrack" + }, + "subscription_tracking": { + "enable": true, + "html": "If you would like to unsubscribe and stop receiving these emails <% clickhere %>.", + "substitution_tag": "<%click here%>", + "text": "If you would like to unsubscribe and stop receiving these emails <% click here %>." + } + } +}'); + +try { + $response = $sg->client->mail()->send()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/mailboxproviders/mailboxproviders.php b/lib/sendgrid-php/examples/mailboxproviders/mailboxproviders.php new file mode 100644 index 000000000..d0fd56716 --- /dev/null +++ b/lib/sendgrid-php/examples/mailboxproviders/mailboxproviders.php @@ -0,0 +1,23 @@ +client->mailbox_providers()->stats()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/mailsettings/mailsettings.php b/lib/sendgrid-php/examples/mailsettings/mailsettings.php new file mode 100644 index 000000000..62dca3772 --- /dev/null +++ b/lib/sendgrid-php/examples/mailsettings/mailsettings.php @@ -0,0 +1,307 @@ +client->mail_settings()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update address whitelist mail settings # +// PATCH /mail_settings/address_whitelist # + +$request_body = json_decode('{ + "enabled": true, + "list": [ + "email1@example.com", + "example.com" + ] +}'); + +try { + $response = $sg->client->mail_settings()->address_whitelist()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve address whitelist mail settings # +// GET /mail_settings/address_whitelist # + +try { + $response = $sg->client->mail_settings()->address_whitelist()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update BCC mail settings # +// PATCH /mail_settings/bcc # + +$request_body = json_decode('{ + "email": "email@example.com", + "enabled": false +}'); + +try { + $response = $sg->client->mail_settings()->bcc()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all BCC mail settings # +// GET /mail_settings/bcc # + +try { + $response = $sg->client->mail_settings()->bcc()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update bounce purge mail settings # +// PATCH /mail_settings/bounce_purge # + +$request_body = json_decode('{ + "enabled": true, + "hard_bounces": 5, + "soft_bounces": 5 +}'); + +try { + $response = $sg->client->mail_settings()->bounce_purge()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve bounce purge mail settings # +// GET /mail_settings/bounce_purge # + +try { + $response = $sg->client->mail_settings()->bounce_purge()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update footer mail settings # +// PATCH /mail_settings/footer # + +$request_body = json_decode('{ + "enabled": true, + "html_content": "...", + "plain_content": "..." +}'); + +try { + $response = $sg->client->mail_settings()->footer()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve footer mail settings # +// GET /mail_settings/footer # + +try { + $response = $sg->client->mail_settings()->footer()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update forward bounce mail settings # +// PATCH /mail_settings/forward_bounce # + +$request_body = json_decode('{ + "email": "example@example.com", + "enabled": true +}'); + +try { + $response = $sg->client->mail_settings()->forward_bounce()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve forward bounce mail settings # +// GET /mail_settings/forward_bounce # + +try { + $response = $sg->client->mail_settings()->forward_bounce()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update forward spam mail settings # +// PATCH /mail_settings/forward_spam # + +$request_body = json_decode('{ + "email": "", + "enabled": false +}'); + +try { + $response = $sg->client->mail_settings()->forward_spam()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve forward spam mail settings # +// GET /mail_settings/forward_spam # + +try { + $response = $sg->client->mail_settings()->forward_spam()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update plain content mail settings # +// PATCH /mail_settings/plain_content # + +$request_body = json_decode('{ + "enabled": false +}'); + +try { + $response = $sg->client->mail_settings()->plain_content()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve plain content mail settings # +// GET /mail_settings/plain_content # + +try { + $response = $sg->client->mail_settings()->plain_content()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update spam check mail settings # +// PATCH /mail_settings/spam_check # + +$request_body = json_decode('{ + "enabled": true, + "max_score": 5, + "url": "url" +}'); + +try { + $response = $sg->client->mail_settings()->spam_check()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve spam check mail settings # +// GET /mail_settings/spam_check # + +try { + $response = $sg->client->mail_settings()->spam_check()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update template mail settings # +// PATCH /mail_settings/template # + +$request_body = json_decode('{ + "enabled": true, + "html_content": "<% body %>" +}'); + +try { + $response = $sg->client->mail_settings()->template()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve legacy template mail settings # +// GET /mail_settings/template # + +try { + $response = $sg->client->mail_settings()->template()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/partnersettings/partnersettings.php b/lib/sendgrid-php/examples/partnersettings/partnersettings.php new file mode 100644 index 000000000..fde977dfb --- /dev/null +++ b/lib/sendgrid-php/examples/partnersettings/partnersettings.php @@ -0,0 +1,56 @@ +client->partner_settings()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Updates New Relic partner settings. # +// PATCH /partner_settings/new_relic # + +$request_body = json_decode('{ + "enable_subuser_statistics": true, + "enabled": true, + "license_key": "" +}'); + +try { + $response = $sg->client->partner_settings()->new_relic()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Returns all New Relic partner settings. # +// GET /partner_settings/new_relic # + + +try { + $response = $sg->client->partner_settings()->new_relic()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/scopes/scopes.php b/lib/sendgrid-php/examples/scopes/scopes.php new file mode 100644 index 000000000..43814ed83 --- /dev/null +++ b/lib/sendgrid-php/examples/scopes/scopes.php @@ -0,0 +1,21 @@ +client->scopes()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/senderauthentication/senderauthentication.php b/lib/sendgrid-php/examples/senderauthentication/senderauthentication.php new file mode 100644 index 000000000..333074c47 --- /dev/null +++ b/lib/sendgrid-php/examples/senderauthentication/senderauthentication.php @@ -0,0 +1,444 @@ +client->whitelabel()->domains()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// List all domain authentications. # +// GET /whitelabel/domains # + +$query_params = json_decode('{"username": "test_string", "domain": "test_string", "exclude_subusers": "true", "limit": 1, "offset": 1}'); + +try { + $response = $sg->client->whitelabel()->domains()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Get the default domain authentication. # +// GET /whitelabel/domains/default # + +try { + $response = $sg->client->whitelabel()->domains()->default()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// List the domain authentication associated with the given user. # +// GET /whitelabel/domains/subuser # + +try { + $response = $sg->client->whitelabel()->domains()->subuser()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Disassociate a domain authentication from a given user. # +// DELETE /whitelabel/domains/subuser # + +try { + $response = $sg->client->whitelabel()->domains()->subuser()->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update a domain authentication. # +// PATCH /whitelabel/domains/{domain_id} # + +$request_body = json_decode('{ + "custom_spf": true, + "default": false +}'); +$domain_id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->domains()->_($domain_id)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a domain authentication. # +// GET /whitelabel/domains/{domain_id} # + +$domain_id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->domains()->_($domain_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a domain authentication. # +// DELETE /whitelabel/domains/{domain_id} # + +$domain_id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->domains()->_($domain_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Associate a domain authentication with a given user. # +// POST /whitelabel/domains/{domain_id}/subuser # + +$request_body = json_decode('{ + "username": "jane@example.com" +}'); +$domain_id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->domains()->_($domain_id)->subuser()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Add an IP to a domain authentication. # +// POST /whitelabel/domains/{id}/ips # + +$request_body = json_decode('{ + "ip": "192.168.0.1" +}'); +$id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->domains()->_($id)->ips()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Remove an IP from a domain authentication. # +// DELETE /whitelabel/domains/{id}/ips/{ip} # + +$id = "test_url_param"; +$ip = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->domains()->_($id)->ips()->_($ip)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Validate a domain authentication. # +// POST /whitelabel/domains/{id}/validate # + +$id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->domains()->_($id)->validate()->post(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Create a reverse DNS record # +// POST /whitelabel/ips # + +$request_body = json_decode('{ + "domain": "example.com", + "ip": "192.168.1.1", + "subdomain": "email" +}'); + +try { + $response = $sg->client->whitelabel()->ips()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all reverse DNS records # +// GET /whitelabel/ips # + +$query_params = json_decode('{"ip": "test_string", "limit": 1, "offset": 1}'); + +try { + $response = $sg->client->whitelabel()->ips()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a reverse DNS record # +// GET /whitelabel/ips/{id} # + +$id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->ips()->_($id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a reverse DNS record # +// DELETE /whitelabel/ips/{id} # + +$id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->ips()->_($id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Validate a reverse DNS record # +// POST /whitelabel/ips/{id}/validate # + +$id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->ips()->_($id)->validate()->post(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Create a Branded Link # +// POST /whitelabel/links # + +$request_body = json_decode('{ + "default": true, + "domain": "example.com", + "subdomain": "mail" +}'); +$query_params = json_decode('{"limit": 1, "offset": 1}'); + +try { + $response = $sg->client->whitelabel()->links()->post($request_body, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all link brandings # +// GET /whitelabel/links # + +$query_params = json_decode('{"limit": 1}'); + +try { + $response = $sg->client->whitelabel()->links()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a Default Link BrandingBranding # +// GET /whitelabel/links/default # + +$query_params = json_decode('{"domain": "test_string"}'); + +try { + $response = $sg->client->whitelabel()->links()->default()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve Associated Link Branding # +// GET /whitelabel/links/subuser # + +$query_params = json_decode('{"username": "test_string"}'); + +try { + $response = $sg->client->whitelabel()->links()->subuser()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Disassociate a Link Branding # +// DELETE /whitelabel/links/subuser # + +$query_params = json_decode('{"username": "test_string"}'); + +try { + $response = $sg->client->whitelabel()->links()->subuser()->delete(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update a Link Branding # +// PATCH /whitelabel/links/{id} # + +$request_body = json_decode('{ + "default": true +}'); +$id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->links()->_($id)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a Link Branding # +// GET /whitelabel/links/{id} # + +$id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->links()->_($id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a Link Branding # +// DELETE /whitelabel/links/{id} # + +$id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->links()->_($id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Validate a Link Branding # +// POST /whitelabel/links/{id}/validate # + +$id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->links()->_($id)->validate()->post(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Associate a Link Branding # +// POST /whitelabel/links/{link_id}/subuser # + +$request_body = json_decode('{ + "username": "jane@example.com" +}'); +$link_id = "test_url_param"; + +try { + $response = $sg->client->whitelabel()->links()->_($link_id)->subuser()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/senders/senders.php b/lib/sendgrid-php/examples/senders/senders.php new file mode 100644 index 000000000..5e8f406cd --- /dev/null +++ b/lib/sendgrid-php/examples/senders/senders.php @@ -0,0 +1,129 @@ +client->senders()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Get all Sender Identities # +// GET /senders # + +try { + $response = $sg->client->senders()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update a Sender Identity # +// PATCH /senders/{sender_id} # + +$request_body = json_decode('{ + "address": "123 Elm St.", + "address_2": "Apt. 456", + "city": "Denver", + "country": "United States", + "from": { + "email": "from@example.com", + "name": "Example INC" + }, + "nickname": "My Sender ID", + "reply_to": { + "email": "replyto@example.com", + "name": "Example INC" + }, + "state": "Colorado", + "zip": "80202" +}'); +$sender_id = "test_url_param"; + +try { + $response = $sg->client->senders()->_($sender_id)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// View a Sender Identity # +// GET /senders/{sender_id} # + +$sender_id = "test_url_param"; + +try { + $response = $sg->client->senders()->_($sender_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a Sender Identity # +// DELETE /senders/{sender_id} # + +$sender_id = "test_url_param"; + +try { + $response = $sg->client->senders()->_($sender_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Resend Sender Identity Verification # +// POST /senders/{sender_id}/resend_verification # + +$sender_id = "test_url_param"; + +try { + $response = $sg->client->senders()->_($sender_id)->resend_verification()->post(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/stats/stats.php b/lib/sendgrid-php/examples/stats/stats.php new file mode 100644 index 000000000..458bc4800 --- /dev/null +++ b/lib/sendgrid-php/examples/stats/stats.php @@ -0,0 +1,23 @@ +client->stats()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/subusers/subusers.php b/lib/sendgrid-php/examples/subusers/subusers.php new file mode 100644 index 000000000..ed669fa99 --- /dev/null +++ b/lib/sendgrid-php/examples/subusers/subusers.php @@ -0,0 +1,241 @@ +client->subusers()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// List all Subusers # +// GET /subusers # + +$query_params = json_decode('{"username": "test_string", "limit": 1, "offset": 1}'); + +try { + $response = $sg->client->subusers()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve Subuser Reputations # +// GET /subusers/reputations # + +$query_params = json_decode('{"usernames": "test_string"}'); + +try { + $response = $sg->client->subusers()->reputations()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve email statistics for your subusers. # +// GET /subusers/stats # + +$query_params = json_decode('{"end_date": "2016-04-01", "aggregated_by": "day", "limit": 1, "offset": 1, "start_date": "2016-01-01", "subusers": "test_string"}'); + +try { + $response = $sg->client->subusers()->stats()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve monthly stats for all subusers # +// GET /subusers/stats/monthly # + +$query_params = json_decode('{"subuser": "test_string", "limit": 1, "sort_by_metric": "test_string", "offset": 1, "date": "test_string", "sort_by_direction": "asc"}'); + +try { + $response = $sg->client->subusers()->stats()->monthly()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve the totals for each email statistic metric for all subusers. # +// GET /subusers/stats/sums # + +$query_params = json_decode('{"end_date": "2016-04-01", "aggregated_by": "day", "limit": 1, "sort_by_metric": "test_string", "offset": 1, "start_date": "2016-01-01", "sort_by_direction": "asc"}'); + +try { + $response = $sg->client->subusers()->stats()->sums()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Enable/disable a subuser # +// PATCH /subusers/{subuser_name} # + +$request_body = json_decode('{ + "disabled": false +}'); +$subuser_name = "test_url_param"; + +try { + $response = $sg->client->subusers()->_($subuser_name)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a subuser # +// DELETE /subusers/{subuser_name} # + +$subuser_name = "test_url_param"; + +try { + $response = $sg->client->subusers()->_($subuser_name)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update IPs assigned to a subuser # +// PUT /subusers/{subuser_name}/ips # + +$request_body = json_decode('[ + "127.0.0.1" +]'); +$subuser_name = "test_url_param"; + +try { + $response = $sg->client->subusers()->_($subuser_name)->ips()->put($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update Monitor Settings for a subuser # +// PUT /subusers/{subuser_name}/monitor # + +$request_body = json_decode('{ + "email": "example@example.com", + "frequency": 500 +}'); +$subuser_name = "test_url_param"; + +try { + $response = $sg->client->subusers()->_($subuser_name)->monitor()->put($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Create monitor settings # +// POST /subusers/{subuser_name}/monitor # + +$request_body = json_decode('{ + "email": "example@example.com", + "frequency": 50000 +}'); +$subuser_name = "test_url_param"; + +try { + $response = $sg->client->subusers()->_($subuser_name)->monitor()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve monitor settings for a subuser # +// GET /subusers/{subuser_name}/monitor # + +$subuser_name = "test_url_param"; + +try { + $response = $sg->client->subusers()->_($subuser_name)->monitor()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete monitor settings # +// DELETE /subusers/{subuser_name}/monitor # + +$subuser_name = "test_url_param"; + +try { + $response = $sg->client->subusers()->_($subuser_name)->monitor()->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve the monthly email statistics for a single subuser # +// GET /subusers/{subuser_name}/stats/monthly # + +$query_params = json_decode('{"date": "test_string", "sort_by_direction": "asc", "limit": 1, "sort_by_metric": "test_string", "offset": 1}'); +$subuser_name = "test_url_param"; + +try { + $response = $sg->client->subusers()->_($subuser_name)->stats()->monthly()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/suppression/suppression.php b/lib/sendgrid-php/examples/suppression/suppression.php new file mode 100644 index 000000000..9c0702f75 --- /dev/null +++ b/lib/sendgrid-php/examples/suppression/suppression.php @@ -0,0 +1,288 @@ +client->suppression()->blocks()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete blocks # +// DELETE /suppression/blocks # + +$request_body = json_decode('{ + "delete_all": false, + "emails": [ + "example1@example.com", + "example2@example.com" + ] +}'); + +try { + $response = $sg->client->suppression()->blocks()->delete($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a specific block # +// GET /suppression/blocks/{email} # + +$email = "test_url_param"; + +try { + $response = $sg->client->suppression()->blocks()->_($email)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a specific block # +// DELETE /suppression/blocks/{email} # + +$email = "test_url_param"; + +try { + $response = $sg->client->suppression()->blocks()->_($email)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all bounces # +// GET /suppression/bounces # + +$query_params = json_decode('{"start_time": 1, "end_time": 1}'); + +try { + $response = $sg->client->suppression()->bounces()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete bounces # +// DELETE /suppression/bounces # + +$request_body = json_decode('{ + "delete_all": true, + "emails": [ + "example@example.com", + "example2@example.com" + ] +}'); + +try { + $response = $sg->client->suppression()->bounces()->delete($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a Bounce # +// GET /suppression/bounces/{email} # + +$email = "test_url_param"; + +try { + $response = $sg->client->suppression()->bounces()->_($email)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a bounce # +// DELETE /suppression/bounces/{email} # + +$query_params = json_decode('{"email_address": "example@example.com"}'); +$email = "test_url_param"; + +try { + $response = $sg->client->suppression()->bounces()->_($email)->delete(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all invalid emails # +// GET /suppression/invalid_emails # + +$query_params = json_decode('{"start_time": 1, "limit": 1, "end_time": 1, "offset": 1}'); + +try { + $response = $sg->client->suppression()->invalid_emails()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete invalid emails # +// DELETE /suppression/invalid_emails # + +$request_body = json_decode('{ + "delete_all": false, + "emails": [ + "example1@example.com", + "example2@example.com" + ] +}'); + +try { + $response = $sg->client->suppression()->invalid_emails()->delete($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a specific invalid email # +// GET /suppression/invalid_emails/{email} # + +$email = "test_url_param"; + +try { + $response = $sg->client->suppression()->invalid_emails()->_($email)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a specific invalid email # +// DELETE /suppression/invalid_emails/{email} # + +$email = "test_url_param"; + +try { + $response = $sg->client->suppression()->invalid_emails()->_($email)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a specific spam report # +// GET /suppression/spam_report/{email} # + +$email = "test_url_param"; + +try { + $response = $sg->client->suppression()->spam_reports()->_($email)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a specific spam report # +// DELETE /suppression/spam_report/{email} # + +$email = "test_url_param"; + +try { + $response = $sg->client->suppression()->spam_reports()->_($email)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all spam reports # +// GET /suppression/spam_reports # + +$query_params = json_decode('{"start_time": 1, "limit": 1, "end_time": 1, "offset": 1}'); + +try { + $response = $sg->client->suppression()->spam_reports()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete spam reports # +// DELETE /suppression/spam_reports # + +$request_body = json_decode('{ + "delete_all": false, + "emails": [ + "example1@example.com", + "example2@example.com" + ] +}'); + +try { + $response = $sg->client->suppression()->spam_reports()->delete($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all global suppressions # +// GET /suppression/unsubscribes # + +$query_params = json_decode('{"start_time": 1, "limit": 1, "end_time": 1, "offset": 1}'); + +try { + $response = $sg->client->suppression()->unsubscribes()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/templates/templates.php b/lib/sendgrid-php/examples/templates/templates.php new file mode 100644 index 000000000..4dce6df9d --- /dev/null +++ b/lib/sendgrid-php/examples/templates/templates.php @@ -0,0 +1,182 @@ +client->templates()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all transactional templates (legacy & dynamic). # +// GET /templates # + +$query_params = ['generations' => 'legacy,dynamic']; + +try { + $response = $sg->client->templates()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Edit a transactional template. # +// PATCH /templates/{template_id} # + +$request_body = json_decode('{ + "name": "new_example_name" +}'); +$template_id = "test_url_param"; + +try { + $response = $sg->client->templates()->_($template_id)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a single transactional template. # +// GET /templates/{template_id} # + +$template_id = "test_url_param"; + +try { + $response = $sg->client->templates()->_($template_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a template. # +// DELETE /templates/{template_id} # + +$template_id = "test_url_param"; + +try { + $response = $sg->client->templates()->_($template_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Create a new transactional template version. # +// POST /templates/{template_id}/versions # + +$request_body = json_decode('{ + "active": 1, + "html_content": "<%body%>", + "name": "example_version_name", + "plain_content": "<%body%>", + "subject": "<%subject%>", + "template_id": "ddb96bbc-9b92-425e-8979-99464621b543" +}'); +$template_id = "test_url_param"; + +try { + $response = $sg->client->templates()->_($template_id)->versions()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Edit a transactional template version. # +// PATCH /templates/{template_id}/versions/{version_id} # + +$request_body = json_decode('{ + "active": 1, + "html_content": "<%body%>", + "name": "updated_example_name", + "plain_content": "<%body%>", + "subject": "<%subject%>" +}'); +$template_id = "test_url_param"; +$version_id = "test_url_param"; + +try { + $response = $sg->client->templates()->_($template_id)->versions()->_($version_id)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a specific transactional template version. # +// GET /templates/{template_id}/versions/{version_id} # + +$template_id = "test_url_param"; +$version_id = "test_url_param"; + +try { + $response = $sg->client->templates()->_($template_id)->versions()->_($version_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a transactional template version. # +// DELETE /templates/{template_id}/versions/{version_id} # + +$template_id = "test_url_param"; +$version_id = "test_url_param"; + +try { + $response = $sg->client->templates()->_($template_id)->versions()->_($version_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Activate a transactional template version. # +// POST /templates/{template_id}/versions/{version_id}/activate # + +$template_id = "test_url_param"; +$version_id = "test_url_param"; + +try { + $response = $sg->client->templates()->_($template_id)->versions()->_($version_id)->activate()->post(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/trackingsettings/trackingsettings.php b/lib/sendgrid-php/examples/trackingsettings/trackingsettings.php new file mode 100644 index 000000000..98477d788 --- /dev/null +++ b/lib/sendgrid-php/examples/trackingsettings/trackingsettings.php @@ -0,0 +1,153 @@ +client->tracking_settings()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update Click Tracking Settings # +// PATCH /tracking_settings/click # + +$request_body = json_decode('{ + "enabled": true +}'); + +try { + $response = $sg->client->tracking_settings()->click()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve Click Track Settings # +// GET /tracking_settings/click # + +try { + $response = $sg->client->tracking_settings()->click()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update Google Analytics Settings # +// PATCH /tracking_settings/google_analytics # + +$request_body = json_decode('{ + "enabled": true, + "utm_campaign": "website", + "utm_content": "", + "utm_medium": "email", + "utm_source": "sendgrid.com", + "utm_term": "" +}'); + +try { + $response = $sg->client->tracking_settings()->google_analytics()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve Google Analytics Settings # +// GET /tracking_settings/google_analytics # + +try { + $response = $sg->client->tracking_settings()->google_analytics()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update Open Tracking Settings # +// PATCH /tracking_settings/open # + +$request_body = json_decode('{ + "enabled": true +}'); + +try { + $response = $sg->client->tracking_settings()->open()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Get Open Tracking Settings # +// GET /tracking_settings/open # + +try { + $response = $sg->client->tracking_settings()->open()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update Subscription Tracking Settings # +// PATCH /tracking_settings/subscription # + +$request_body = json_decode('{ + "enabled": true, + "html_content": "html content", + "landing": "landing page html", + "plain_content": "text content", + "replace": "replacement tag", + "url": "url" +}'); + +try { + $response = $sg->client->tracking_settings()->subscription()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve Subscription Tracking Settings # +// GET /tracking_settings/subscription # + +try { + $response = $sg->client->tracking_settings()->subscription()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/examples/user/user.php b/lib/sendgrid-php/examples/user/user.php new file mode 100644 index 000000000..9754ffba5 --- /dev/null +++ b/lib/sendgrid-php/examples/user/user.php @@ -0,0 +1,412 @@ +client->user()->account()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve your credit balance # +// GET /user/credits # + +try { + $response = $sg->client->user()->credits()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update your account email address # +// PUT /user/email # + +$request_body = json_decode('{ + "email": "example@example.com" +}'); + +try { + $response = $sg->client->user()->email()->put($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve your account email address # +// GET /user/email # + +try { + $response = $sg->client->user()->email()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update your password # +// PUT /user/password # + +$request_body = json_decode('{ + "new_password": "new_password", + "old_password": "old_password" +}'); + +try { + $response = $sg->client->user()->password()->put($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update a user's profile # +// PATCH /user/profile # + +$request_body = json_decode('{ + "city": "Orange", + "first_name": "Example", + "last_name": "User" +}'); + +try { + $response = $sg->client->user()->profile()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Get a user's profile # +// GET /user/profile # + + +try { + $response = $sg->client->user()->profile()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Cancel or pause a scheduled send # +// POST /user/scheduled_sends # + +$request_body = json_decode('{ + "batch_id": "YOUR_BATCH_ID", + "status": "pause" +}'); + +try { + $response = $sg->client->user()->scheduled_sends()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all scheduled sends # +// GET /user/scheduled_sends # + +try { + $response = $sg->client->user()->scheduled_sends()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update user scheduled send information # +// PATCH /user/scheduled_sends/{batch_id} # + +$request_body = json_decode('{ + "status": "pause" +}'); +$batch_id = "test_url_param"; + +try { + $response = $sg->client->user()->scheduled_sends()->_($batch_id)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve scheduled send # +// GET /user/scheduled_sends/{batch_id} # + +$batch_id = "test_url_param"; + +try { + $response = $sg->client->user()->scheduled_sends()->_($batch_id)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a cancellation or pause of a scheduled send # +// DELETE /user/scheduled_sends/{batch_id} # + +$batch_id = "test_url_param"; + +try { + $response = $sg->client->user()->scheduled_sends()->_($batch_id)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update Enforced TLS settings # +// PATCH /user/settings/enforced_tls # + +$request_body = json_decode('{ + "require_tls": true, + "require_valid_cert": false +}'); + +try { + $response = $sg->client->user()->settings()->enforced_tls()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve current Enforced TLS settings. # +// GET /user/settings/enforced_tls # + +try { + $response = $sg->client->user()->settings()->enforced_tls()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update your username # +// PUT /user/username # + +$request_body = json_decode('{ + "username": "test_username" +}'); + +try { + $response = $sg->client->user()->username()->put($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve your username # +// GET /user/username # + +try { + $response = $sg->client->user()->username()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update Event Notification Settings # +// PATCH /user/webhooks/event/settings # + +$request_body = json_decode('{ + "bounce": true, + "click": true, + "deferred": true, + "delivered": true, + "dropped": true, + "enabled": true, + "group_resubscribe": true, + "group_unsubscribe": true, + "open": true, + "processed": true, + "spam_report": true, + "unsubscribe": true, + "url": "url" +}'); + +try { + $response = $sg->client->user()->webhooks()->event()->settings()->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve Event Webhook settings # +// GET /user/webhooks/event/settings # + +try { + $response = $sg->client->user()->webhooks()->event()->settings()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Test Event Notification Settings # +// POST /user/webhooks/event/test # + +$request_body = json_decode('{ + "url": "url" +}'); + +try { + $response = $sg->client->user()->webhooks()->event()->test()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Create a parse setting # +// POST /user/webhooks/parse/settings # + +$request_body = json_decode('{ + "hostname": "myhostname.com", + "send_raw": false, + "spam_check": true, + "url": "http://email.myhosthame.com" +}'); + +try { + $response = $sg->client->user()->webhooks()->parse()->settings()->post($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve all parse settings # +// GET /user/webhooks/parse/settings # + +try { + $response = $sg->client->user()->webhooks()->parse()->settings()->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Update a parse setting # +// PATCH /user/webhooks/parse/settings/{hostname} # + +$request_body = json_decode('{ + "send_raw": true, + "spam_check": false, + "url": "http://newdomain.com/parse" +}'); +$hostname = "test_url_param"; + +try { + $response = $sg->client->user()->webhooks()->parse()->settings()->_($hostname)->patch($request_body); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieve a specific parse setting # +// GET /user/webhooks/parse/settings/{hostname} # + +$hostname = "test_url_param"; + +try { + $response = $sg->client->user()->webhooks()->parse()->settings()->_($hostname)->get(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Delete a parse setting # +// DELETE /user/webhooks/parse/settings/{hostname} # + +$hostname = "test_url_param"; + +try { + $response = $sg->client->user()->webhooks()->parse()->settings()->_($hostname)->delete(); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} + +//////////////////////////////////////////////////// +// Retrieves Inbound Parse Webhook statistics. # +// GET /user/webhooks/parse/stats # + +$query_params = json_decode('{"aggregated_by": "day", "limit": "test_string", "start_date": "2016-01-01", "end_date": "2016-04-01", "offset": "test_string"}'); + +try { + $response = $sg->client->user()->webhooks()->parse()->stats()->get(null, $query_params); + print $response->statusCode() . "\n"; + print_r($response->headers()); + print $response->body() . "\n"; +} catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; +} diff --git a/lib/sendgrid-php/lib/BaseSendGridClientInterface.php b/lib/sendgrid-php/lib/BaseSendGridClientInterface.php new file mode 100644 index 000000000..e72cbda64 --- /dev/null +++ b/lib/sendgrid-php/lib/BaseSendGridClientInterface.php @@ -0,0 +1,90 @@ + "https://api.eu.sendgrid.com", + "global" => "https://api.sendgrid.com", + ]; + + /** + * Set up the HTTP Client. + * + * @param string $auth Authorization header value. + * @param string $host Default host/base URL for the client. + * @param array $options An array of options, currently only "host", "curl", + * "version", "verify_ssl", and "impersonateSubuser", + * are implemented. + */ + public function __construct($auth, $host, $options = array()) + { + $headers = [ + $auth, + 'User-Agent: sendgrid/' . $this->version . ';php', + 'Accept: application/json', + ]; + + $host = isset($options['host']) ? $options['host'] : $host; + + $version = isset($options['version']) ? $options['version'] : '/v3'; + + if (!empty($options['impersonateSubuser'])) { + $headers[] = 'On-Behalf-Of: ' . $options['impersonateSubuser']; + } + + $this->client = new Client($host, $headers, $version); + + $this->client->setCurlOptions(isset($options['curl']) ? $options['curl'] : []); + $this->client->setVerifySSLCerts(isset($options['verify_ssl']) ? $options['verify_ssl'] : true); + } + + /** + * Make an API request. + * + * @param Mail $email A Mail object, containing the request object + * + * @return Response + */ + public function send(Mail $email) + { + return $this->client->mail()->send()->post($email); + } + + /* + * Client libraries contain setters for specifying region/edge. + * This allows support global and eu regions only. This set will likely expand in the future. + * Global should be the default + * Global region means the message should be sent through: + * HTTP: api.sendgrid.com + * EU region means the message should be sent through: + * HTTP: api.eu.sendgrid.com + */ + public function setDataResidency($region): void + { + if (array_key_exists($region, $this->allowedRegionsHostMap)) { + $this->client->setHost($this->allowedRegionsHostMap[$region]); + } else { + throw new InvalidArgumentException("region can only be \"eu\" or \"global\""); + } + } + +} diff --git a/lib/sendgrid-php/lib/SendGrid.php b/lib/sendgrid-php/lib/SendGrid.php new file mode 100644 index 000000000..1fdaccc59 --- /dev/null +++ b/lib/sendgrid-php/lib/SendGrid.php @@ -0,0 +1,25 @@ +firstName = $firstName; + $this->lastName = $lastName; + $this->email = $email; + } + + /** + * Retrieve the first name of the recipient + * + * @return string + */ + public function getFirstName() + { + return $this->firstName; + } + + /** + * Retrieve the last name of the recipient + * + * @return string + */ + public function getLastName() + { + return $this->lastName; + } + + /** + * Retrieve the email address of the recipient + * + * @return string + */ + public function getEmail() + { + return $this->email; + } + + /** + * Return an array representing a recipient object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'email' => $this->getEmail(), + 'first_name' => $this->getFirstName(), + 'last_name' => $this->getLastName() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/contacts/RecipientForm.php b/lib/sendgrid-php/lib/contacts/RecipientForm.php new file mode 100644 index 000000000..b93e814c6 --- /dev/null +++ b/lib/sendgrid-php/lib/contacts/RecipientForm.php @@ -0,0 +1,45 @@ + + First Name:
+ Last Name:
+ E-mail:
+ +'; + $this->html = $html; + } + + /** + * Return the HTML form + * + * @return string + */ + public function __toString() + { + return $this->html; + } +} diff --git a/lib/sendgrid-php/lib/eventwebhook/EventWebhook.php b/lib/sendgrid-php/lib/eventwebhook/EventWebhook.php new file mode 100644 index 000000000..71e6c30d4 --- /dev/null +++ b/lib/sendgrid-php/lib/eventwebhook/EventWebhook.php @@ -0,0 +1,46 @@ + $size) { + $message = sprintf( + $message ?: 'Number of elements in "$%s" can not be more than %d.', + $property, + $size + ); + + throw new TypeException($message); + } + } + + /** + * Assert that number of elements in array is more than a given limit. + * + * @param mixed $value + * @param string $property + * @param int $size + * @param string|null $message + * + * @throws TypeException + */ + public static function minItems($value, $property, $size, $message = null) + { + static::isArray($value, $property, $message); + + if (\count($value) < $size) { + $message = sprintf( + $message ?: 'Number of elements in "$%s" can not be less than %d.', + $property, + $size + ); + + throw new TypeException($message); + } + } + + /** + * Assert that a number is smaller as a given limit. + * + * @param mixed $value + * @param string $property + * @param int $limit + * @param string|null $message + * + * @throws TypeException + */ + public static function maxValue($value, $property, $limit, $message = null) + { + static::integer($value, $property, $message); + + $limit = (int) $limit; + + if ($value > $limit) { + $message = sprintf( + $message ?: '"$%s" expected to be at most %d. Got: %s', + $property, + $limit, + $value + ); + + throw new TypeException($message); + } + } + + /** + * Assert that a number is at least as big as a given limit. + * + * @param mixed $value + * @param string $property + * @param int $limit + * @param string|null $message + * + * @throws TypeException + */ + public static function minValue($value, $property, $limit, $message = null) + { + static::integer($value, $property, $message); + + $limit = (int) $limit; + + if ($value < $limit) { + $message = sprintf( + $message ?: '"$%s" expected to be at least %d. Got: %s', + $property, + $limit, + $value + ); + + throw new TypeException($message); + } + } + + /** + * Assert that string value is not longer than a given limit. + * + * @param mixed $value + * @param string $property + * @param int $limit + * @param string|null $message + * + * @throws TypeException + */ + public static function maxLength($value, $property, $limit, $message = null) + { + static::string($value, $property, $message); + + $length = mb_strlen($value, 'utf8'); + + if ($length > $limit) { + $message = sprintf( + $message ?: '"$%s" must have no more than %d characters. Got: %d', + $property, + $limit, + $length + ); + + throw new TypeException($message); + } + } + + /** + * Assert that string value length is greater than a given limit. + * + * @param mixed $value + * @param string $property + * @param int $limit + * @param string|null $message + * + * @throws TypeException + */ + public static function minLength($value, $property, $limit, $message = null) + { + static::string($value, $property, $message); + + $length = mb_strlen($value, 'utf8'); + + if ($length < $limit) { + $message = sprintf( + $message ?: '"$%s" must have at least %d characters. Got: %d', + $property, + $limit, + $length + ); + + throw new TypeException($message); + } + } + + /** + * Assert that value is in array of choices. + * + * @param mixed $value + * @param string $property + * @param array $choices + * @param string|null $message + * + * @throws TypeException + */ + public static function anyOf($value, $property, array $choices, $message = null) + { + if (!\in_array($value, $choices, true)) { + $message = sprintf( + $message ?: '"$%s" must be any of "%s". Got: %s', + $property, + implode(', ', $choices), + $value + ); + + throw new TypeException($message); + } + } +} diff --git a/lib/sendgrid-php/lib/mail/Asm.php b/lib/sendgrid-php/lib/mail/Asm.php new file mode 100644 index 000000000..9f3b71990 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Asm.php @@ -0,0 +1,143 @@ +setGroupId($group_id); + } + if (isset($groups_to_display)) { + $this->setGroupsToDisplay($groups_to_display); + } + } + + /** + * Add the group id to a Asm object + * + * @param int|GroupId $group_id The unsubscribe group to associate with this + * email + * + * @throws \SendGrid\Mail\TypeException + */ + public function setGroupId($group_id) + { + if ($group_id instanceof GroupId) { + $this->group_id = $group_id->getGroupId(); + } else { + Assert::integer( + $group_id, 'group_id', 'Value "$group_id" must be an instance of SendGrid\Mail\GroupId or an integer.' + ); + + $this->group_id = new GroupId($group_id); + } + } + + /** + * Retrieve the GroupId object from a Asm object + * + * The unsubscribe group to associate with this email + * + * @return int + */ + public function getGroupId() + { + return $this->group_id; + } + + /** + * Add the groups to display id(s) to a Asm object + * + * @param int[]|GroupsToDisplay $groups_to_display A GroupsToDisplay + * object or an array + * containing the + * unsubscribe groups + * that you would like + * to be displayed + * on the unsubscribe + * preferences page. + * + * @throws \SendGrid\Mail\TypeException + */ + public function setGroupsToDisplay($groups_to_display) + { + if ($groups_to_display instanceof GroupsToDisplay) { + $this->groups_to_display = $groups_to_display->getGroupsToDisplay(); + } else { + Assert::isArray( + $groups_to_display, 'groups_to_display', + 'Value "$groups_to_display" must be an instance of SendGrid\Mail\GroupsToDisplay or an array.' + ); + Assert::maxItems($groups_to_display, 'groups_to_display', 25); + + $this->groups_to_display = new GroupsToDisplay($groups_to_display); + } + } + + /** + * Retrieve the groups to display id(s) from a Asm object + * + * @return int[] + */ + public function getGroupsToDisplay() + { + return $this->groups_to_display; + } + + /** + * Return an array representing a Asm object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'group_id' => $this->getGroupId(), + 'groups_to_display' => $this->getGroupsToDisplay() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/Attachment.php b/lib/sendgrid-php/lib/mail/Attachment.php new file mode 100644 index 000000000..44e56a73f --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Attachment.php @@ -0,0 +1,226 @@ +setContent($content); + } + if (isset($type)) { + $this->setType($type); + } + if (isset($filename)) { + $this->setFilename($filename); + } + if (isset($disposition)) { + $this->setDisposition($disposition); + } + if (isset($content_id)) { + $this->setContentID($content_id); + } + } + + /** + * Add the content to a Attachment object + * + * @param string $content Base64 encoded content + * + * @throws \SendGrid\Mail\TypeException + */ + public function setContent($content) + { + Assert::minLength($content, 'content', 1); + + if (!$this->isBase64($content)) { + $this->content = base64_encode($content); + } else { + $this->content = $content; + } + } + + /** + * Retrieve the content from a Attachment object + * + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * Add the mime type to a Attachment object + * + * @param string $type Mime type of the attachment + * + * @throws \SendGrid\Mail\TypeException + */ + public function setType($type) + { + Assert::minLength($type, 'type', 1); + + $this->type = $type; + } + + /** + * Retrieve the mime type from a Attachment object + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Add the file name to a Attachment object + * + * @param string $filename File name of the attachment + * + * @throws \SendGrid\Mail\TypeException + */ + public function setFilename($filename) + { + Assert::string($filename, 'filename'); + + $this->filename = $filename; + } + + /** + * Retrieve the file name from a Attachment object + * + * @return string + */ + public function getFilename() + { + return $this->filename; + } + + /** + * Add the disposition to a Attachment object + * + * @param string $disposition How the attachment should be displayed: + * inline or attachment, default is attachment + * + * @throws \SendGrid\Mail\TypeException + */ + public function setDisposition($disposition) + { + Assert::anyOf($disposition, 'disposition', ['inline', 'attachment']); + + $this->disposition = $disposition; + } + + /** + * Retrieve the disposition from a Attachment object + * + * @return string + */ + public function getDisposition() + { + return $this->disposition; + } + + /** + * Add the content id to a Attachment object + * + * @param string $content_id Used when disposition is inline to display + * the file within the body of the email + * @throws \SendGrid\Mail\TypeException + */ + public function setContentID($content_id) + { + Assert::string($content_id, 'content_id'); + + $this->content_id = $content_id; + } + + /** + * Retrieve the content id from a Attachment object + * + * @return string + */ + public function getContentID() + { + return $this->content_id; + } + + /** + * Verifies whether or not the provided string is a valid base64 string + * + * @param $string string The string that has to be checked + * @return bool + */ + private function isBase64($string) + { + $decoded_data = base64_decode($string, true); + $encoded_data = base64_encode($decoded_data); + if ($encoded_data != $string) { + return false; + } + return true; + } + + /** + * Return an array representing a Attachment object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'content' => $this->getContent(), + 'type' => $this->getType(), + 'filename' => $this->getFilename(), + 'disposition' => $this->getDisposition(), + 'content_id' => $this->getContentID() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/BatchId.php b/lib/sendgrid-php/lib/mail/BatchId.php new file mode 100644 index 000000000..9d59c2b60 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/BatchId.php @@ -0,0 +1,69 @@ +setBatchId($batch_id); + } + } + + /** + * Add the batch id to a BatchId object + * + * @param string $batch_id This ID represents a batch of emails to be sent + * at the same time + * + * @throws \SendGrid\Mail\TypeException + */ + public function setBatchId($batch_id) + { + Assert::string($batch_id, 'batch_id'); + + $this->batch_id = $batch_id; + } + + /** + * Return the batch id from a BatchId object + * + * @return string + */ + public function getBatchId() + { + return $this->batch_id; + } + + /** + * Return an array representing a BatchId object for the Twilio SendGrid API + * + * @return null|string + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getBatchId(); + } +} diff --git a/lib/sendgrid-php/lib/mail/Bcc.php b/lib/sendgrid-php/lib/mail/Bcc.php new file mode 100644 index 000000000..fa764b106 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Bcc.php @@ -0,0 +1,15 @@ +setEnable($enable); + } + if (isset($email)) { + $this->setEmail($email); + } + } + + /** + * Update the enable setting on a BccSettings object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting on a BccSettings object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Add the email setting on a BccSettings object + * + * @param string $email The email address that you would like + * to receive the BCC + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEmail($email) + { + Assert::email($email, 'email'); + + $this->email = $email; + } + + /** + * Retrieve the email setting on a BccSettings object + * + * @return string + */ + public function getEmail() + { + return $this->email; + } + + /** + * Return an array representing a BccSettings object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable(), + 'email' => $this->getEmail() + ], + static function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/BypassBounceManagement.php b/lib/sendgrid-php/lib/mail/BypassBounceManagement.php new file mode 100644 index 000000000..dc393d961 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/BypassBounceManagement.php @@ -0,0 +1,82 @@ +setEnable($enable); + } + } + + /** + * Update the enable setting on a BypassBounceManagement object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting on a BypassBounceManagement object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Return an array representing a BypassBounceManagement object for + * the SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/BypassListManagement.php b/lib/sendgrid-php/lib/mail/BypassListManagement.php new file mode 100644 index 000000000..5b88e7a1a --- /dev/null +++ b/lib/sendgrid-php/lib/mail/BypassListManagement.php @@ -0,0 +1,81 @@ +setEnable($enable); + } + } + + /** + * Update the enable setting on a BypassListManagement object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting on a BypassListManagement object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Return an array representing a BypassListManagement object for + * the SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/BypassSpamManagement.php b/lib/sendgrid-php/lib/mail/BypassSpamManagement.php new file mode 100644 index 000000000..d4c74709e --- /dev/null +++ b/lib/sendgrid-php/lib/mail/BypassSpamManagement.php @@ -0,0 +1,82 @@ +setEnable($enable); + } + } + + /** + * Update the enable setting on a BypassSpamManagement object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting on a BypassSpamManagement object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Return an array representing a BypassSpamManagement object for + * the SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/BypassUnsubscribeManagement.php b/lib/sendgrid-php/lib/mail/BypassUnsubscribeManagement.php new file mode 100644 index 000000000..fb03d5f07 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/BypassUnsubscribeManagement.php @@ -0,0 +1,83 @@ +setEnable($enable); + } + } + + /** + * Update the enable setting on a BypassUnsubscribeManagement object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting on a BypassUnsubscribeManagement object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Return an array representing a BypassUnsubscribeManagement object for + * the SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/Category.php b/lib/sendgrid-php/lib/mail/Category.php new file mode 100644 index 000000000..d04643f51 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Category.php @@ -0,0 +1,71 @@ +setCategory($category); + } + } + + /** + * Add a category to a Category object + * + * @param string $category A category name for an email message. + * Each category name may not exceed 255 + * characters + * + * @throws \SendGrid\Mail\TypeException + */ + public function setCategory($category) + { + Assert::maxLength($category, 'category', 255); + + $this->category = $category; + } + + /** + * Retrieve a category from a Category object + * + * @return string + */ + public function getCategory() + { + return $this->category; + } + + /** + * Return an array representing a Category object for the Twilio SendGrid API + * + * @return string + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getCategory(); + } +} diff --git a/lib/sendgrid-php/lib/mail/Cc.php b/lib/sendgrid-php/lib/mail/Cc.php new file mode 100644 index 000000000..28fcd3e78 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Cc.php @@ -0,0 +1,15 @@ +setEnable($enable); + } + if (isset($enable_text)) { + $this->setEnableText($enable_text); + } + } + + /** + * Update the enable setting on a ClickTracking object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting on a ClickTracking object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Update the enable text setting on a ClickTracking object + * + * @param bool $enable_text Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnableText($enable_text) + { + Assert::boolean($enable_text, 'enable_text'); + + $this->enable_text = $enable_text; + } + + /** + * Retrieve the enable_text setting on a ClickTracking object + * + * @return bool + */ + public function getEnableText() + { + return $this->enable_text; + } + + /** + * Return an array representing a ClickTracking object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable(), + 'enable_text' => $this->getEnableText() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/Content.php b/lib/sendgrid-php/lib/mail/Content.php new file mode 100644 index 000000000..70ca26111 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Content.php @@ -0,0 +1,119 @@ +setType($type); + } + if (isset($value)) { + $this->setValue($value); + } + } + + /** + * Add the mime type on a Content object + * + * @param string $type The mime type of the content you are including + * in your email. For example, “text/plain” or + * “text/html” + * + * @throws TypeException + */ + public function setType($type) + { + Assert::string($type, 'type'); + + $this->type = $type; + } + + /** + * Retrieve the mime type on a Content object + * + * @return string|null + */ + public function getType() + { + return $this->type; + } + + /** + * Add the content value to a Content object + * + * @param string $value The actual content of the specified mime type + * that you are including in your email + * + * @throws TypeException + */ + public function setValue($value) + { + Assert::minLength($value, 'value', 1); + + $this->value = mb_convert_encoding((string)$value, 'UTF-8', 'UTF-8'); + } + + /** + * Retrieve the content value to a Content object + * + * @return string|null + */ + public function getValue() + { + return $this->value; + } + + /** + * Return an array representing a Contact object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'type' => $this->getType(), + 'value' => $this->getValue() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/CustomArg.php b/lib/sendgrid-php/lib/mail/CustomArg.php new file mode 100644 index 000000000..28476f863 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/CustomArg.php @@ -0,0 +1,112 @@ +setKey($key); + } + if (isset($value)) { + $this->setValue($value); + } + } + + /** + * Add a custom arg key on a CustomArg object + * + * @param string $key Custom arg key + * + * @throws \SendGrid\Mail\TypeException + */ + public function setKey($key) + { + Assert::string($key, 'key'); + + $this->key = $key; + } + + /** + * Retrieve a custom arg key on a CustomArg object + * + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * Add a custom arg value on a CustomArg object + * + * @param string $value Custom arg value + * + * @throws \SendGrid\Mail\TypeException + */ + public function setValue($value) + { + Assert::string($value, 'value'); + + $this->value = $value; + } + + /** + * Retrieve a custom arg key on a CustomArg object + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * Return an array representing a CustomArg object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'key' => $this->getKey(), + 'value' => $this->getValue() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/EmailAddress.php b/lib/sendgrid-php/lib/mail/EmailAddress.php new file mode 100644 index 000000000..dc7934e95 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/EmailAddress.php @@ -0,0 +1,196 @@ +setEmailAddress($emailAddress); + } + if (isset($name) && $name !== null) { + $this->setName($name); + } + if (isset($substitutions)) { + $this->setSubstitutions($substitutions); + } + if (isset($subject)) { + $this->setSubject($subject); + } + } + + /** + * Add the email address to a EmailAddress object + * + * @param string $emailAddress The email address + * + * @throws TypeException + */ + public function setEmailAddress($emailAddress) + { + Assert::email($emailAddress, 'emailAddress'); + + $this->email = $emailAddress; + } + + /** + * Retrieve the email address from a EmailAddress object + * + * @return string + */ + public function getEmailAddress() + { + return $this->email; + } + + /** + * Retrieve the email address from a EmailAddress object + * + * @return string + */ + public function getEmail() + { + return $this->getEmailAddress(); + } + + /** + * Add a name to a EmailAddress object + * + * @param string $name The name of the person associated with the email + * + * @throws TypeException + */ + public function setName($name) + { + Assert::string($name, 'name'); + + $this->name = (!empty($name)) ? $name : null; + } + + /** + * Retrieve the name from a EmailAddress object + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Add substitutions to a EmailAddress object + * + * @param array $substitutions An array of key/value substitutions to + * be be applied to the text and html content + * of the email body + * + * @throws TypeException + */ + public function setSubstitutions($substitutions) + { + Assert::maxItems($substitutions, 'substitutions', 10000); + + $this->substitutions = $substitutions; + } + + /** + * Retrieve substitutions from a EmailAddress object + */ + public function getSubstitutions() + { + return $this->substitutions; + } + + /** + * Add a subject to a EmailAddress object + * + * @param string $subject The personalized subject of the email + * + * @throws TypeException + */ + public function setSubject($subject) + { + Assert::string($subject, 'subject'); + + // Now that we know it is a string, we can safely create a new subject + $this->subject = new Subject($subject); + } + + /** + * Retrieve a subject from an EmailAddress object + * + * @return Subject + */ + public function getSubject() + { + return $this->subject; + } + + /** + * Determine if this EmailAddress object is personalized by either + * containing substitutions or a specific subject. + * + * @return bool + */ + public function isPersonalized() + { + return $this->getSubstitutions() || $this->getSubject(); + } + + /** + * Return an array representing an EmailAddress object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'name' => $this->getName(), + 'email' => $this->getEmail() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/Footer.php b/lib/sendgrid-php/lib/mail/Footer.php new file mode 100644 index 000000000..77f025cc3 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Footer.php @@ -0,0 +1,136 @@ +setEnable($enable); + } + if (isset($text)) { + $this->setText($text); + } + if (isset($html)) { + $this->setHtml($html); + } + } + + /** + * Update the enable setting on a Footer object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting on a Footer object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Add text to a Footer object + * + * @param string $text The plain text content of your footer + * + * @throws \SendGrid\Mail\TypeException + */ + public function setText($text) + { + Assert::string($text, 'text'); + + $this->text = $text; + } + + /** + * Retrieve text to a Footer object + * + * @return string + */ + public function getText() + { + return $this->text; + } + + /** + * Add html to a Footer object + * + * @param string $html The HTML content of your footer + * + * @throws \SendGrid\Mail\TypeException + */ + public function setHtml($html) + { + Assert::string($html, 'html'); + + $this->html = $html; + } + + /** + * Retrieve html from a Footer object + * + * @return string + */ + public function getHtml() + { + return $this->html; + } + + /** + * Return an array representing a Footer object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable(), + 'text' => $this->getText(), + 'html' => $this->getHtml() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/From.php b/lib/sendgrid-php/lib/mail/From.php new file mode 100644 index 000000000..00d67b621 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/From.php @@ -0,0 +1,15 @@ +setEnable($enable); + } + if (isset($utm_source)) { + $this->setCampaignSource($utm_source); + } + if (isset($utm_medium)) { + $this->setCampaignMedium($utm_medium); + } + if (isset($utm_term)) { + $this->setCampaignTerm($utm_term); + } + if (isset($utm_content)) { + $this->setCampaignContent($utm_content); + } + if (isset($utm_campaign)) { + $this->setCampaignName($utm_campaign); + } + } + + /** + * Update the enable setting on a Ganalytics object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting on a Ganalytics object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Add the campaign source to a Ganalytics object + * + * @param string $utm_source Name of the referrer source. (e.g. + * Google, SomeDomain.com, or Marketing Email) + * + * @throws \SendGrid\Mail\TypeException + */ + public function setCampaignSource($utm_source) + { + Assert::string($utm_source, 'utm_source'); + + $this->utm_source = $utm_source; + } + + /** + * Return the campaign source from a Ganalytics object + * + * @return string + */ + public function getCampaignSource() + { + return $this->utm_source; + } + + /** + * Add the campaign medium to a Ganalytics object + * + * @param string $utm_medium Name of the marketing medium. (e.g. Email) + * + * @throws \SendGrid\Mail\TypeException + */ + public function setCampaignMedium($utm_medium) + { + Assert::string($utm_medium, 'utm_medium'); + + $this->utm_medium = $utm_medium; + } + + /** + * Return the campaign medium from a Ganalytics object + * + * @return string + */ + public function getCampaignMedium() + { + return $this->utm_medium; + } + + /** + * Add the campaign term to a Ganalytics object + * + * @param string $utm_term Used to identify any paid keywords + * + * @throws \SendGrid\Mail\TypeException + */ + public function setCampaignTerm($utm_term) + { + Assert::string($utm_term, 'utm_term'); + + $this->utm_term = $utm_term; + } + + /** + * Return the campaign term from a Ganalytics object + * + * @return string + */ + public function getCampaignTerm() + { + return $this->utm_term; + } + + /** + * Add the campaign content to a Ganalytics object + * + * @param string $utm_content Used to differentiate your campaign from + * advertisements + * + * @throws \SendGrid\Mail\TypeException + */ + public function setCampaignContent($utm_content) + { + Assert::string($utm_content, 'utm_content'); + + $this->utm_content = $utm_content; + } + + /** + * Return the campaign content from a Ganalytics object + * + * @return string + */ + public function getCampaignContent() + { + return $this->utm_content; + } + + /** + * Add the campaign name to a Ganalytics object + * + * @param string $utm_campaign The name of the campaign + * + * @throws \SendGrid\Mail\TypeException + */ + public function setCampaignName($utm_campaign) + { + Assert::string($utm_campaign, 'utm_campaign'); + + $this->utm_campaign = $utm_campaign; + } + + /** + * Return the campaign name from a Ganalytics object + * + * @return string + */ + public function getCampaignName() + { + return $this->utm_campaign; + } + + /** + * Return an array representing a Ganalytics object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable(), + 'utm_source' => $this->getCampaignSource(), + 'utm_medium' => $this->getCampaignMedium(), + 'utm_term' => $this->getCampaignTerm(), + 'utm_content' => $this->getCampaignContent(), + 'utm_campaign' => $this->getCampaignName() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/GroupId.php b/lib/sendgrid-php/lib/mail/GroupId.php new file mode 100644 index 000000000..9c173a5bb --- /dev/null +++ b/lib/sendgrid-php/lib/mail/GroupId.php @@ -0,0 +1,67 @@ +setGroupId($group_id); + } + } + + /** + * Add the group id to a GroupId object + * + * @param int $group_id The unsubscribe group to associate with this email + * + * @throws \SendGrid\Mail\TypeException + */ + public function setGroupId($group_id) + { + Assert::integer($group_id, 'group_id'); + + $this->group_id = $group_id; + } + + /** + * Retrieve the group id from a GroupId object + * + * @return int + */ + public function getGroupId() + { + return $this->group_id; + } + + /** + * Return an array representing a GroupId object for the Twilio SendGrid API + * + * @return int + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getGroupId(); + } +} diff --git a/lib/sendgrid-php/lib/mail/GroupsToDisplay.php b/lib/sendgrid-php/lib/mail/GroupsToDisplay.php new file mode 100644 index 000000000..c37688398 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/GroupsToDisplay.php @@ -0,0 +1,103 @@ +setGroupsToDisplay($groups_to_display); + } + } + + /** + * Set groups list to display on a GroupsToDisplay object + * + * @param int[] $groups_to_display The unsubscribe group(s) + * that you would like to be + * displayed on the unsubscribe + * preferences page + * + * @throws \SendGrid\Mail\TypeException + */ + public function setGroupsToDisplay($groups_to_display) + { + Assert::maxItems($groups_to_display, 'groups_to_display', 25); + + $this->groups_to_display = $groups_to_display; + } + + /** + * Add group to display on a GroupsToDisplay object + * + * @param int $group_to_display The unsubscribe group + * that you would like to be + * displayed on the unsubscribe + * preferences page + * + * @throws TypeException + */ + public function addGroupToDisplay($group_to_display) + { + Assert::integer($group_to_display, 'group_to_display'); + Assert::accept($group_to_display, 'group_to_display', function () { + $groups = $this->groups_to_display; + if (!\is_array($groups)) { + $groups = []; + } + return \count($groups) < 25; + }, 'Number of elements in "$groups_to_display" can not be more than 25.'); + + $this->groups_to_display[] = $group_to_display; + } + + /** + * Return the group(s) to display on a GroupsToDisplay object + * + * @return int[] + */ + public function getGroupsToDisplay() + { + return $this->groups_to_display; + } + + /** + * Return an array representing a GroupsToDisplay object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getGroupsToDisplay(); + } +} diff --git a/lib/sendgrid-php/lib/mail/Header.php b/lib/sendgrid-php/lib/mail/Header.php new file mode 100644 index 000000000..3f63abaa4 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Header.php @@ -0,0 +1,109 @@ +setKey($key); + } + if (isset($value)) { + $this->setValue($value); + } + } + + /** + * Add the key on a Header object + * + * @param string $key Header key + * + * @throws \SendGrid\Mail\TypeException + */ + public function setKey($key) + { + Assert::string($key, 'key'); + + $this->key = $key; + } + + /** + * Retrieve the key from a Header object + * + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * Add the value on a Header object + * + * @param string $value Header value + * + * @throws \SendGrid\Mail\TypeException + */ + public function setValue($value) + { + Assert::string($value, 'value'); + + $this->value = $value; + } + + /** + * Retrieve the value from a Header object + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * Return an array representing a Header object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'key' => $this->getKey(), + 'value' => $this->getValue() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/HtmlContent.php b/lib/sendgrid-php/lib/mail/HtmlContent.php new file mode 100644 index 000000000..1aff8aea3 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/HtmlContent.php @@ -0,0 +1,26 @@ +setIpPoolName($ip_pool_name); + } + } + + /** + * Set the ip pool name on a IpPoolName object + * + * @param string $ip_pool_name The IP Pool that you would like to + * send this email from. Minimum length: + * 2, Maximum Length: 64 + * + * @throws \SendGrid\Mail\TypeException + */ + public function setIpPoolName($ip_pool_name) + { + Assert::minLength($ip_pool_name, 'ip_pool_name', 2); + Assert::maxLength($ip_pool_name, 'ip_pool_name', 64); + + $this->ip_pool_name = $ip_pool_name; + } + + /** + * Retrieve the ip pool name from a IpPoolName object + * + * @return string + */ + public function getIpPoolName() + { + return $this->ip_pool_name; + } + + /** + * Return an array representing a IpPoolName object for the Twilio SendGrid API + * + * @return string + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getIpPoolName(); + } +} diff --git a/lib/sendgrid-php/lib/mail/Mail.php b/lib/sendgrid-php/lib/mail/Mail.php new file mode 100644 index 000000000..c8511852a --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Mail.php @@ -0,0 +1,1994 @@ +personalization[] = new Personalization(); + return; + } + if (isset($from)) { + $this->setFrom($from); + } + if (isset($to)) { + if (!\is_array($to)) { + $to = [$to]; + } + $subjectCount = 0; + foreach ($to as $email) { + if (\is_array($subject) || $email->isPersonalized()) { + $personalization = new Personalization(); + $this->addTo($email, null, null, null, $personalization); + } else { + $this->addTo($email); + $personalization = \end($this->personalization); + } + + if (\is_array($subject) && $subjectCount < \count($subject)) { + $personalization->setSubject($subject[$subjectCount]); + $subjectCount++; + } + + if (\is_array($globalSubstitutions)) { + foreach ($globalSubstitutions as $key => $value) { + if ($value instanceof Substitution) { + $personalization->addSubstitution($value); + } else { + $personalization->addSubstitution($key, $value); + } + } + } + } + } + if (isset($subject) && !\is_array($subject)) { + $this->setSubject($subject); + } + if (isset($plainTextContent)) { + Assert::isInstanceOf($plainTextContent, 'plainTextContent', Content::class); + + $this->addContent($plainTextContent); + } + if (isset($htmlContent)) { + Assert::isInstanceOf($htmlContent, 'htmlContent', Content::class); + + $this->addContent($htmlContent); + } + } + + /** + * Adds a To, Cc or Bcc object to a Personalization object + * + * @param string $emailType Object type name: To, Cc or Bcc + * @param string $email Recipient email address + * @param string|null $name Recipient name + * @param Substitution[]|array|null $substitutions Personalized + * substitutions + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * + * @throws TypeException + */ + private function addRecipientEmail( + $emailType, + $email, + $name = null, + $substitutions = null, + $personalizationIndex = null, + $personalization = null + ) { + $personalizationFunctionCall = 'add' . $emailType; + $emailTypeClass = '\SendGrid\Mail\\' . $emailType; + if (!($email instanceof $emailTypeClass)) { + $email = new $emailTypeClass( + $email, + $name, + $substitutions + ); + } + + if ($personalizationIndex === null && $personalization === null + && $emailType === 'To' && $email->isPersonalized()) { + $personalization = new Personalization(); + } + + $personalization = $this->getPersonalization($personalizationIndex, $personalization); + $personalization->$personalizationFunctionCall($email); + + if ($subs = $email->getSubstitutions()) { + foreach ($subs as $key => $value) { + $personalization->addSubstitution($key, $value); + } + } + + if ($email->getSubject()) { + $personalization->setSubject($email->getSubject()); + } + } + + /** + * Adds an array of To, Cc or Bcc objects to a Personalization object + * + * @param string $emailType Object type name: To, Cc or Bcc + * @param To[]|Cc[]|Bcc[] $emails Array of email recipients + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * + * @throws TypeException + */ + private function addRecipientEmails( + $emailType, + $emails, + $personalizationIndex = null, + $personalization = null + ) { + $emailFunctionCall = 'add' . $emailType; + + if (\current($emails) instanceof EmailAddress) { + foreach ($emails as $email) { + $this->$emailFunctionCall( + $email, + $name = null, + $substitutions = null, + $personalizationIndex, + $personalization + ); + } + } else { + foreach ($emails as $email => $name) { + $this->$emailFunctionCall( + $email, + $name, + $substitutions = null, + $personalizationIndex, + $personalization + ); + } + } + } + + /** + * Add a Personalization object to the Mail object + * + * @param Personalization $personalization A Personalization object + * + * @throws TypeException + */ + public function addPersonalization($personalization) + { + Assert::isInstanceOf($personalization, 'personalization', Personalization::class); + + $this->personalization[] = $personalization; + } + + /** + * Retrieves a Personalization object, adds a pre-created Personalization + * object, or creates and adds a Personalization object. + * + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * + * @return Personalization + * + * @throws TypeException + */ + public function getPersonalization($personalizationIndex = null, $personalization = null) + { + /** + * Approach: + * - Append if provided personalization + return + * - Return last added if not provided personalizationIndex (create on empty) + * - Return existing personalizationIndex + * - InvalidArgumentException on unexpected personalizationIndex ( > count) + * - Create + add Personalization and return + */ + + // If given a Personalization instance + if (null !== $personalization) { + // Just append it onto Mail and return it + $this->addPersonalization($personalization); + return $personalization; + } + + // Retrieve count of existing Personalization instances + $personalizationCount = $this->getPersonalizationCount(); + + // Not providing a personalizationIndex? + if (null === $personalizationIndex) { + // Create new Personalization instance depending on current count + if (0 === $personalizationCount) { + $this->addPersonalization(new Personalization()); + } + + // Return last added Personalization instance + return end($this->personalization); + } + + // Existing personalizationIndex in personalization? + if (isset($this->personalization[$personalizationIndex])) { + // Return referred personalization + return $this->personalization[$personalizationIndex]; + } + + // Non-existent personalizationIndex given + // Only allow creation of next Personalization if given + // personalizationIndex equals personalizationCount + if ( + ($personalizationIndex < 0) || + ($personalizationIndex > $personalizationCount) + ) { + throw new InvalidArgumentException( + 'personalizationIndex ' . $personalizationIndex . + ' must be less than ' . $personalizationCount + ); + } + + // Create new Personalization and return it + $personalization = new Personalization(); + $this->addPersonalization($personalization); + return $personalization; + } + + /** + * Retrieves Personalization object collection from the Mail object. + * + * @return Personalization[]|null + */ + public function getPersonalizations() + { + return $this->personalization; + } + + /** + * Retrieve the number of Personalization objects associated with the Mail object + * + * @return int + */ + public function getPersonalizationCount() + { + return isset($this->personalization) ? \count($this->personalization) : 0; + } + + /** + * Adds an email recipient to a Personalization object + * + * @param string|To $to Email address or To object + * @param string $name Recipient name + * @param array|Substitution[] $substitutions Personalized substitutions + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * + * @throws TypeException + */ + public function addTo( + $to, + $name = null, + $substitutions = null, + $personalizationIndex = null, + $personalization = null + ) { + $this->addRecipientEmail( + 'To', + $to, + $name, + $substitutions, + $personalizationIndex, + $personalization + ); + } + + /** + * Adds multiple email recipients to a Personalization object + * + * @param To[]|array $toEmails Array of To objects or key/value pairs of + * email address/recipient names + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * + * @throws TypeException + */ + public function addTos( + $toEmails, + $personalizationIndex = null, + $personalization = null + ) { + Assert::minItems($toEmails, 'toEmails', 1); + Assert::maxItems($toEmails, 'toEmails', 1000); + + $this->addRecipientEmails( + 'To', + $toEmails, + $personalizationIndex, + $personalization + ); + } + + /** + * Adds an email cc recipient to a Personalization object + * + * @param string|Cc $cc Email address or Cc object + * @param string $name Recipient name + * @param Substitution[]|array|null $substitutions Personalized + * substitutions + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * + * @throws TypeException + */ + public function addCc( + $cc, + $name = null, + $substitutions = null, + $personalizationIndex = null, + $personalization = null + ) { + $this->addRecipientEmail( + 'Cc', + $cc, + $name, + $substitutions, + $personalizationIndex, + $personalization + ); + } + + /** + * Adds multiple email cc recipients to a Personalization object + * + * @param Cc[]|array $ccEmails Array of Cc objects or key/value pairs of + * email address/recipient names + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * + * @throws TypeException + */ + public function addCcs( + $ccEmails, + $personalizationIndex = null, + $personalization = null + ) { + Assert::minItems($ccEmails, 'ccEmails', 1); + Assert::maxItems($ccEmails, 'ccEmails', 1000); + + $this->addRecipientEmails( + 'Cc', + $ccEmails, + $personalizationIndex, + $personalization + ); + } + + /** + * Adds an email bcc recipient to a Personalization object + * + * @param string|Bcc $bcc Email address or Bcc object + * @param string $name Recipient name + * @param Substitution[]|array|null $substitutions Personalized + * substitutions + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * + * @throws TypeException + */ + public function addBcc( + $bcc, + $name = null, + $substitutions = null, + $personalizationIndex = null, + $personalization = null + ) { + $this->addRecipientEmail( + 'Bcc', + $bcc, + $name, + $substitutions, + $personalizationIndex, + $personalization + ); + } + + /** + * Adds multiple email bcc recipients to a Personalization object + * + * @param Bcc[]|array $bccEmails Array of Bcc objects or key/value pairs of + * email address/recipient names + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * + * @throws TypeException + */ + public function addBccs( + $bccEmails, + $personalizationIndex = null, + $personalization = null + ) { + Assert::minItems($bccEmails, 'bccEmails', 1); + Assert::maxItems($bccEmails, 'bccEmails', 1000); + + $this->addRecipientEmails( + 'Bcc', + $bccEmails, + $personalizationIndex, + $personalization + ); + } + + /** + * Add a subject to a Personalization or Mail object + * + * If you don't provide a Personalization object or index, the + * subject will be global to entire message. Note that + * subjects added to Personalization objects override + * global subjects. + * + * @param string|Subject $subject Email subject + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * @throws TypeException + */ + public function setSubject( + $subject, + $personalizationIndex = null, + $personalization = null + ) { + if (!($subject instanceof Subject)) { + $subject = new Subject($subject); + } + + if ($personalization !== null) { + $personalization->setSubject($subject); + $this->addPersonalization($personalization); + return; + } + if ($personalizationIndex !== null) { + $this->personalization[$personalizationIndex]->setSubject($subject); + return; + } + $this->setGlobalSubject($subject); + } + + /** + * Retrieve a subject attached to a Personalization object + * + * @param int $personalizationIndex Index into the array of existing + * Personalization objects + * @return Subject + */ + public function getSubject($personalizationIndex = 0) + { + return $this->personalization[$personalizationIndex]->getSubject(); + } + + /** + * Add a header to a Personalization or Mail object + * + * If you don't provide a Personalization object or index, the + * header will be global to entire message. Note that + * headers added to Personalization objects override + * global headers. + * + * @param string|Header $key Key or Header object + * @param string|null $value Value + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * @throws TypeException + */ + public function addHeader( + $key, + $value = null, + $personalizationIndex = null, + $personalization = null + ) { + $header = null; + if ($key instanceof Header) { + $h = $key; + $header = new Header($h->getKey(), $h->getValue()); + } else { + $header = new Header($key, $value); + } + + $personalization = $this->getPersonalization($personalizationIndex, $personalization); + $personalization->addHeader($header); + } + + /** + * Adds multiple headers to a Personalization or Mail object + * + * If you don't provide a Personalization object or index, the + * header will be global to entire message. Note that + * headers added to Personalization objects override + * global headers. + * + * @param array|Header[] $headers Array of Header objects or key values + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * @throws TypeException + */ + public function addHeaders( + $headers, + $personalizationIndex = null, + $personalization = null + ) { + if (\current($headers) instanceof Header) { + foreach ($headers as $header) { + $this->addHeader($header); + } + } else { + foreach ($headers as $key => $value) { + $this->addHeader( + $key, + $value, + $personalizationIndex, + $personalization + ); + } + } + } + + /** + * Retrieve the headers attached to a Personalization object + * + * @param int $personalizationIndex Index into the array of existing + * Personalization objects + * @return Header[] + */ + public function getHeaders($personalizationIndex = 0) + { + return $this->personalization[$personalizationIndex]->getHeaders(); + } + + /** + * Add a Substitution object or key/value to a Personalization object + * + * @param Substitution|string $key Substitution object or the key of a + * dynamic data + * @param string|null $value Value + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * @throws TypeException + */ + public function addDynamicTemplateData( + $key, + $value = null, + $personalizationIndex = null, + $personalization = null + ) { + $this->addSubstitution($key, $value, $personalizationIndex, $personalization); + } + + /** + * Add a Substitution object or key/value to a Personalization object + * + * @param array|Substitution[] $datas Array of Substitution + * objects or key/values + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * @throws TypeException + */ + public function addDynamicTemplateDatas( + $datas, + $personalizationIndex = null, + $personalization = null + ) { + $this->addSubstitutions($datas, $personalizationIndex, $personalization); + } + + /** + * Retrieve dynamic template data key/value pairs from a Personalization object + * + * @param int|0 $personalizationIndex Index into the array of existing + * Personalization objects + * @return array + */ + public function getDynamicTemplateDatas($personalizationIndex = 0) + { + return $this->getSubstitutions($personalizationIndex); + } + + /** + * Add a substitution to a Personalization or Mail object + * + * If you don't provide a Personalization object or index, the + * substitution will be global to entire message. Note that + * substitutions added to Personalization objects override + * global substitutions. + * + * @param string|Substitution $key Key or Substitution object + * @param string|null $value Value + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * @throws TypeException + */ + public function addSubstitution( + $key, + $value = null, + $personalizationIndex = null, + $personalization = null + ) { + $substitution = null; + if ($key instanceof Substitution) { + $s = $key; + $substitution = new Substitution($s->getKey(), $s->getValue()); + } else { + $substitution = new Substitution($key, $value); + } + + $personalization = $this->getPersonalization($personalizationIndex, $personalization); + $personalization->addSubstitution($substitution); + } + + /** + * Adds multiple substitutions to a Personalization or Mail object + * + * If you don't provide a Personalization object or index, the + * substitution will be global to entire message. Note that + * substitutions added to Personalization objects override + * global headers. + * + * @param array|Substitution[] $substitutions Array of Substitution + * objects or key/values + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * @throws TypeException + */ + public function addSubstitutions( + $substitutions, + $personalizationIndex = null, + $personalization = null + ) { + if (\current($substitutions) instanceof Substitution) { + foreach ($substitutions as $substitution) { + $this->addSubstitution($substitution); + } + } else { + foreach ($substitutions as $key => $value) { + $this->addSubstitution( + $key, + $value, + $personalizationIndex, + $personalization + ); + } + } + } + + /** + * Retrieve the substitutions attached to a Personalization object + * + * @param int|0 $personalizationIndex Index into the array of existing + * Personalization objects + * @return Substitution[] + */ + public function getSubstitutions($personalizationIndex = 0) + { + return $this->personalization[$personalizationIndex]->getSubstitutions(); + } + + /** + * Add a custom arg to a Personalization or Mail object + * + * Note that custom args added to Personalization objects + * override global custom args. + * + * @param string|CustomArg $key Key or CustomArg object + * @param string|null $value Value + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * @throws TypeException + */ + public function addCustomArg( + $key, + $value = null, + $personalizationIndex = null, + $personalization = null + ) { + $custom_arg = null; + if ($key instanceof CustomArg) { + $ca = $key; + $custom_arg = new CustomArg($ca->getKey(), $ca->getValue()); + } else { + $custom_arg = new CustomArg($key, $value); + } + + $personalization = $this->getPersonalization($personalizationIndex, $personalization); + $personalization->addCustomArg($custom_arg); + } + + /** + * Adds multiple custom args to a Personalization or Mail object + * + * If you don't provide a Personalization object or index, the + * custom arg will be global to entire message. Note that + * custom args added to Personalization objects override + * global custom args. + * + * @param array|CustomArg[] $custom_args Array of CustomArg objects or + * key/values + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * @throws TypeException + */ + public function addCustomArgs( + $custom_args, + $personalizationIndex = null, + $personalization = null + ) { + if (\current($custom_args) instanceof CustomArg) { + foreach ($custom_args as $custom_arg) { + $this->addCustomArg($custom_arg); + } + } else { + foreach ($custom_args as $key => $value) { + $this->addCustomArg( + $key, + $value, + $personalizationIndex, + $personalization + ); + } + } + } + + /** + * Retrieve the custom args attached to a Personalization object + * + * @param int|0 $personalizationIndex Index into the array of existing + * Personalization objects + * @return CustomArg[] + */ + public function getCustomArgs($personalizationIndex = 0) + { + return $this->personalization[$personalizationIndex]->getCustomArgs(); + } + + /** + * Add a unix timestamp allowing you to specify when you want your + * email to be delivered to a Personalization or Mail object + * + * If you don't provide a Personalization object or index, the + * send at timestamp will be global to entire message. Note that + * timestamps added to Personalization objects override + * global timestamps. + * + * @param int|SendAt $send_at A unix timestamp + * @param int|null $personalizationIndex Index into the array of existing + * Personalization objects + * @param Personalization|null $personalization A pre-created + * Personalization object + * @throws TypeException + */ + public function setSendAt( + $send_at, + $personalizationIndex = null, + $personalization = null + ) { + if (!($send_at instanceof SendAt)) { + $send_at = new SendAt($send_at); + } + + $personalization = $this->getPersonalization($personalizationIndex, $personalization); + $personalization->setSendAt($send_at); + } + + /** + * Retrieve the unix timestamp attached to a Personalization object + * + * @param int|0 $personalizationIndex Index into the array of existing + * Personalization objects + * @return SendAt|null + */ + public function getSendAt($personalizationIndex = 0) + { + return $this->personalization[$personalizationIndex]->getSendAt(); + } + + /** + * Add the sender email address to a Mail object + * + * @param string|From $email Email address or From object + * @param string|null $name Sender name + * + * @throws TypeException + */ + public function setFrom($email, $name = null) + { + if ($email instanceof From) { + $this->from = $email; + } else { + Assert::email( + $email, + 'email', + '"$email" must be an instance of SendGrid\Mail\From or a valid email address' + ); + $this->from = new From($email, $name); + } + } + + /** + * Retrieve the sender attached to a Mail object + * + * @return From + */ + public function getFrom() + { + return $this->from; + } + + /** + * Add the reply to email address to a Mail object + * + * @param string|ReplyTo $email Email address or From object + * @param string|null $name Reply to name + * + * @throws TypeException + */ + public function setReplyTo($email, $name = null) + { + if ($email instanceof ReplyTo) { + $this->reply_to = $email; + } else { + $this->reply_to = new ReplyTo($email, $name); + } + } + + /** + * Retrieve the reply to information attached to a Mail object + * + * @return ReplyTo + */ + public function getReplyTo() + { + return $this->reply_to; + } + + /** + * Add a subject to a Mail object + * + * Note that + * subjects added to Personalization objects override + * global subjects. + * + * @param string|Subject $subject Email subject + * + * @throws TypeException + */ + public function setGlobalSubject($subject) + { + if (!($subject instanceof Subject)) { + $subject = new Subject($subject); + } + $this->subject = $subject; + } + + /** + * Retrieve a subject attached to a Mail object + * + * @return Subject + */ + public function getGlobalSubject() + { + return $this->subject; + } + + /** + * Add content to a Mail object + * + * For a list of pre-configured mime types, please see + * MimeType.php + * + * @param string|Content $type Mime type or Content object + * @param string|null $value Contents (e.g. text or html) + * + * @throws TypeException + */ + public function addContent($type, $value = null) + { + if ($type instanceof Content) { + $content = $type; + } else { + $content = new Content($type, $value); + } + $this->contents[] = $content; + } + + /** + * Adds multiple Content objects to a Mail object + * + * @param array|Content[] $contents Array of Content objects + * or key value pairs + * + * @throws TypeException + */ + public function addContents($contents) + { + if (\current($contents) instanceof Content) { + foreach ($contents as $content) { + $this->addContent($content); + } + } else { + foreach ($contents as $key => $value) { + $this->addContent($key, $value); + } + } + } + + /** + * Retrieve the contents attached to a Mail object + * + * Will return array of Content Objects with text/plain MimeType first + * Array re-ordered before return where this is not already the case + * + * @return Content[] + */ + public function getContents() + { + if ($this->contents) { + if ($this->contents[0]->getType() !== MimeType::TEXT && \count($this->contents) > 1) { + foreach ($this->contents as $key => $value) { + if ($value->getType() === MimeType::TEXT) { + $plain_content = $value; + unset($this->contents[$key]); + break; + } + } + if (isset($plain_content)) { + array_unshift($this->contents, $plain_content); + } + } + } + + return $this->contents; + } + + /** + * Add an attachment to a Mail object + * + * @param string|Attachment $attachment Attachment object or + * Base64 encoded content + * @param string|null $type Mime type of the attachment + * @param string|null $filename File name of the attachment + * @param string|null $disposition How the attachment should be + * displayed: inline or attachment + * default is attachment + * @param string|null $content_id Used when disposition is inline + * to display the file within the + * body of the email + * @throws TypeException + */ + public function addAttachment( + $attachment, + $type = null, + $filename = null, + $disposition = null, + $content_id = null + ) { + if (\is_array($attachment)) { + $attachment = new Attachment( + $attachment[0], + $attachment[1], + $attachment[2], + $attachment[3], + $attachment[4] + ); + } else if (!($attachment instanceof Attachment)) { + $attachment = new Attachment( + $attachment, + $type, + $filename, + $disposition, + $content_id + ); + } + $this->attachments[] = $attachment; + } + + /** + * Adds multiple attachments to a Mail object + * + * @param array|Attachment[] $attachments Array of Attachment objects or + * arrays + * @throws TypeException + */ + public function addAttachments($attachments) + { + foreach ($attachments as $attachment) { + $this->addAttachment($attachment); + } + } + + /** + * Retrieve the attachments attached to a Mail object + * + * @return Attachment[] + */ + public function getAttachments() + { + return $this->attachments; + } + + /** + * Add a template id to a Mail object + * + * @param TemplateId|string $template_id The id of the template to be + * applied to this email + * @throws TypeException + */ + public function setTemplateId($template_id) + { + if (!($template_id instanceof TemplateId)) { + $template_id = new TemplateId($template_id); + } + + $this->template_id = $template_id; + } + + /** + * Retrieve a template id attached to a Mail object + * + * @return TemplateId + */ + public function getTemplateId() + { + return $this->template_id; + } + + /** + * Add a section to a Mail object + * + * @param string|Section $key Key or Section object + * @param string|null $value Value + */ + public function addSection($key, $value = null) + { + if ($key instanceof Section) { + $section = $key; + $this->sections[$section->getKey()] = $section->getValue(); + return; + } + $this->sections[$key] = (string)$value; + } + + /** + * Adds multiple sections to a Mail object + * + * @param array|Section[] $sections Array of CustomArg objects + * or key/values + */ + public function addSections($sections) + { + if (\current($sections) instanceof Section) { + foreach ($sections as $section) { + $this->addSection($section); + } + } else { + foreach ($sections as $key => $value) { + $this->addSection($key, $value); + } + } + } + + /** + * Retrieve the section(s) attached to a Mail object + * + * @return Section[] + */ + public function getSections() + { + return $this->sections; + } + + /** + * Add a header to a Mail object + * + * Note that headers added to Personalization objects override + * global headers. + * + * @param string|Header $key Key or Header object + * @param string|null $value Value + */ + public function addGlobalHeader($key, $value = null) + { + if ($key instanceof Header) { + $header = $key; + $this->headers[$header->getKey()] = $header->getValue(); + return; + } + $this->headers[$key] = (string)$value; + } + + /** + * Adds multiple headers to a Mail object + * + * Note that headers added to Personalization objects override + * global headers. + * + * @param array|Header[] $headers Array of Header objects + * or key values + */ + public function addGlobalHeaders($headers) + { + if (\current($headers) instanceof Header) { + foreach ($headers as $header) { + $this->addGlobalHeader($header); + } + } else { + foreach ($headers as $key => $value) { + $this->addGlobalHeader($key, $value); + } + } + } + + /** + * Retrieve the headers attached to a Mail object + * + * @return Header[] + */ + public function getGlobalHeaders() + { + return $this->headers; + } + + /** + * Add a substitution to a Mail object + * + * Note that substitutions added to Personalization objects override + * global substitutions. + * + * @param string|Substitution $key Key or Substitution object + * @param string|null $value Value + */ + public function addGlobalSubstitution($key, $value = null) + { + if ($key instanceof Substitution) { + $substitution = $key; + $this->substitutions[$substitution->getKey()] = $substitution->getValue(); + return; + } + $this->substitutions[$key] = $value; + } + + /** + * Adds multiple substitutions to a Mail object + * + * Note that substitutions added to Personalization objects override + * global headers. + * + * @param array|Substitution[] $substitutions Array of Substitution + * objects or key/values + */ + public function addGlobalSubstitutions($substitutions) + { + if (\current($substitutions) instanceof Substitution) { + foreach ($substitutions as $substitution) { + $this->addGlobalSubstitution($substitution); + } + } else { + foreach ($substitutions as $key => $value) { + $this->addGlobalSubstitution($key, $value); + } + } + } + + /** + * Retrieve the substitutions attached to a Mail object + * + * @return Substitution[] + */ + public function getGlobalSubstitutions() + { + return $this->substitutions; + } + + /** + * Add a category to a Mail object + * + * @param string|Category $category Category object or category name + * @throws TypeException + */ + public function addCategory($category) + { + if (!($category instanceof Category)) { + $category = new Category($category); + } + + Assert::accept($category, 'category', function () { + $categories = $this->categories; + if (!\is_array($categories)) { + $categories = []; + } + return \count($categories) < 10; + }, 'Number of elements in "$categories" can not exceed 10.'); + + $this->categories[] = $category; + } + + /** + * Adds multiple categories to a Mail object + * + * @param array|Category[] $categories Array of Category objects or arrays + * @throws TypeException + */ + public function addCategories($categories) + { + foreach ($categories as $category) { + $this->addCategory($category); + } + } + + /** + * Retrieve the categories attached to a Mail object + * + * @return Category[] + */ + public function getCategories() + { + return $this->categories; + } + + /** + * Add a custom arg to a Mail object + * + * Note that custom args added to Personalization objects override + * global custom args. + * + * @param string|CustomArg $key Key or CustomArg object + * @param string|null $value Value + */ + public function addGlobalCustomArg($key, $value = null) + { + if ($key instanceof CustomArg) { + $custom_arg = $key; + $this->custom_args[$custom_arg->getKey()] = $custom_arg->getValue(); + return; + } + $this->custom_args[$key] = (string)$value; + } + + /** + * Adds multiple custom args to a Mail object + * + * Note that custom args added to Personalization objects override + * global custom args. + * + * @param array|CustomArg[] $custom_args Array of CustomArg objects + * or key/values + */ + public function addGlobalCustomArgs($custom_args) + { + if (\current($custom_args) instanceof CustomArg) { + foreach ($custom_args as $custom_arg) { + $this->addGlobalCustomArg($custom_arg); + } + } else { + foreach ($custom_args as $key => $value) { + $this->addGlobalCustomArg($key, $value); + } + } + } + + /** + * Retrieve the custom args attached to a Mail object + * + * @return CustomArg[] + */ + public function getGlobalCustomArgs() + { + return $this->custom_args; + } + + /** + * Add a unix timestamp allowing you to specify when you want your + * email to be delivered to a Mail object + * + * Note that timestamps added to Personalization objects override + * global timestamps. + * + * @param int|SendAt $send_at A unix timestamp + * @throws TypeException + */ + public function setGlobalSendAt($send_at) + { + if (!($send_at instanceof SendAt)) { + $send_at = new SendAt($send_at); + } + $this->send_at = $send_at; + } + + /** + * Retrieve the unix timestamp attached to a Mail object + * + * @return SendAt + */ + public function getGlobalSendAt() + { + return $this->send_at; + } + + /** + * Add a batch id to a Mail object + * + * @param string|BatchId $batch_id Id for a batch of emails + * to be sent at the same time + * @throws TypeException + */ + public function setBatchId($batch_id) + { + if (!($batch_id instanceof BatchId)) { + $batch_id = new BatchId($batch_id); + } + $this->batch_id = $batch_id; + } + + /** + * Retrieve the batch id attached to a Mail object + * + * @return BatchId + */ + public function getBatchId() + { + return $this->batch_id; + } + + /** + * Add a Asm describing how to handle unsubscribes to a Mail object + * + * @param int|Asm $group_id Asm object or unsubscribe group id + * to associate this email with + * @param array $groups_to_display Array of integer ids of unsubscribe + * groups to be displayed on the + * unsubscribe preferences page + * @throws TypeException + */ + public function setAsm($group_id, $groups_to_display = null) + { + if ($group_id instanceof Asm) { + $asm = $group_id; + $this->asm = $asm; + } else { + $this->asm = new Asm($group_id, $groups_to_display); + } + } + + /** + * Retrieve the Asm object describing how to handle unsubscribes attached + * to a Mail object + * + * @return Asm + */ + public function getAsm() + { + return $this->asm; + } + + /** + * Add the IP pool name to a Mail object + * + * @param string|IpPoolName $ip_pool_name The IP Pool that you would + * like to send this email from + * @throws TypeException + */ + public function setIpPoolName($ip_pool_name) + { + if ($ip_pool_name instanceof IpPoolName) { + $this->ip_pool_name = $ip_pool_name->getIpPoolName(); + } else { + $this->ip_pool_name = new IpPoolName($ip_pool_name); + } + } + + /** + * Retrieve the IP pool name attached to a Mail object + * + * @return IpPoolName + */ + public function getIpPoolName() + { + return $this->ip_pool_name; + } + + /** + * Add a MailSettings object to a Mail object + * + * @param MailSettings $mail_settings A collection of different + * mail settings that you can + * use to specify how you would + * like this email to be handled + */ + public function setMailSettings($mail_settings) + { + $this->mail_settings = $mail_settings; + } + + /** + * Retrieve the MailSettings object attached to a Mail object + * + * @return MailSettings + */ + public function getMailSettings() + { + return $this->mail_settings; + } + + /** + * Set the Bcc settings on a MailSettings object + * + * @param bool|BccSettings $enable A BccSettings object or a boolean + * to determine if this setting is active + * @param string|null $email The email address to be bcc'ed + * @throws TypeException + */ + public function setBccSettings($enable, $email = null) + { + if (!($this->mail_settings instanceof MailSettings)) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setBccSettings($enable, $email); + } + + /** + * Enable bypass bounce management on a MailSettings object + * + * Allows you to bypass the bounce list to ensure that the email is delivered to recipients. + * Spam report and unsubscribe lists will still be checked; addresses on these other lists + * will not receive the message. + * + * This filter cannot be combined with the bypass_list_management filter. + * + * @throws TypeException + */ + public function enableBypassBounceManagement() + { + if (!$this->mail_settings instanceof MailSettings) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setBypassBounceManagement(true); + } + + /** + * Enable bypass list management on a MailSettings object + * + * Allows you to bypass all unsubscribe groups and suppressions to ensure + * that the email is delivered to every single recipient. This should only + * be used in emergencies when it is absolutely necessary that every + * recipient receives your email. + * + * @throws TypeException + */ + public function enableBypassListManagement() + { + if (!$this->mail_settings instanceof MailSettings) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setBypassListManagement(true); + } + + /** + * Enable bypass spam management on a MailSettings object + * + * Allows you to bypass the spam report list to ensure that the email is delivered to recipients. + * Bounce and unsubscribe lists will still be checked; addresses on these other lists will not + * receive the message. + * + * This filter cannot be combined with the bypass_list_management filter. + * + * @throws TypeException + */ + public function enableBypassSpamManagement() + { + if (!$this->mail_settings instanceof MailSettings) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setBypassSpamManagement(true); + } + + /** + * Enable bypass unsubscribe management on a MailSettings object + * + * Allows you to bypass the global unsubscribe list to ensure that the email is delivered + * to recipients. Bounce and spam report lists will still be checked; addresses on these + * other lists will not receive the message. This filter applies only to global unsubscribes + * and will not bypass group unsubscribes. + * + * This filter cannot be combined with the bypass_list_management filter. + * + * @throws TypeException + */ + public function enableBypassUnsubscribeManagement() + { + if (!$this->mail_settings instanceof MailSettings) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setBypassUnsubscribeManagement(true); + } + + /** + * Disable bypass bounce management on a MailSettings object + * + * Allows you to bypass the bounce list to ensure that the email is delivered to recipients. + * Spam report and unsubscribe lists will still be checked; addresses on these other lists + * will not receive the message. + * + * This filter cannot be combined with the bypass_list_management filter. + * + * @throws TypeException + */ + public function disableBypassBounceManagement() + { + if (!($this->mail_settings instanceof MailSettings)) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setBypassBounceManagement(false); + } + + /** + * Disable bypass list management on a MailSettings object + * + * Allows you to bypass all unsubscribe groups and suppressions to ensure + * that the email is delivered to every single recipient. This should only + * be used in emergencies when it is absolutely necessary that every + * recipient receives your email. + * + * @throws TypeException + */ + public function disableBypassListManagement() + { + if (!($this->mail_settings instanceof MailSettings)) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setBypassListManagement(false); + } + + + /** + * Disable bypass spam management on a MailSettings object + * + * Allows you to bypass the spam report list to ensure that the email is delivered to recipients. + * Bounce and unsubscribe lists will still be checked; addresses on these other lists will not + * receive the message. + * + * This filter cannot be combined with the bypass_list_management filter. + * + * @throws TypeException + */ + public function disableBypassSpamManagement() + { + if (!($this->mail_settings instanceof MailSettings)) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setBypassSpamManagement(false); + } + + /** + * Disable bypass global unsubscribe management on a MailSettings object + * + * Allows you to bypass the global unsubscribe list to ensure that the email is delivered + * to recipients. Bounce and spam report lists will still be checked; addresses on these + * other lists will not receive the message. This filter applies only to global unsubscribes + * and will not bypass group unsubscribes. + * + * This filter cannot be combined with the bypass_list_management filter. + * + * @throws TypeException + */ + public function disableBypassUnsubscribeManagement() + { + if (!($this->mail_settings instanceof MailSettings)) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setBypassUnsubscribeManagement(false); + } + + /** + * Set the Footer settings on a MailSettings object + * + * @param bool|Footer $enable A Footer object or a boolean + * to determine if this setting is active + * @param string|null $text The plain text content of the footer + * @param string|null $html The HTML content of the footer + * + * @throws TypeException + */ + public function setFooter($enable = null, $text = null, $html = null) + { + if (!$this->mail_settings instanceof MailSettings) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setFooter($enable, $text, $html); + } + + /** + * Enable sandbox mode on a MailSettings object + * + * This allows you to send a test email to ensure that your request + * body is valid and formatted correctly. + * + * @throws TypeException + */ + public function enableSandBoxMode() + { + if (!($this->mail_settings instanceof MailSettings)) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setSandBoxMode(true); + } + + /** + * Disable sandbox mode on a MailSettings object + * + * This to ensure that your request is not in sandbox mode. + * + * @throws TypeException + */ + public function disableSandBoxMode() + { + if (!($this->mail_settings instanceof MailSettings)) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setSandBoxMode(false); + } + + /** + * Set the spam check settings on a MailSettings object + * + * @param bool|SpamCheck $enable A SpamCheck object or a boolean + * to determine if this setting is active + * @param int|null $threshold The threshold used to determine if your + * content qualifies as spam on a scale from + * 1 to 10, with 10 being most strict, or + * most likely to be considered as spam + * @param string|null $post_to_url An Inbound Parse URL that you would like + * a copy of your email along with the spam + * report to be sent to + * + * @throws TypeException + */ + public function setSpamCheck($enable = null, $threshold = null, $post_to_url = null) + { + if (!$this->mail_settings instanceof MailSettings) { + $this->mail_settings = new MailSettings(); + } + $this->mail_settings->setSpamCheck($enable, $threshold, $post_to_url); + } + + /** + * Add a TrackingSettings object to a Mail object + * + * @param TrackingSettings $tracking_settings Settings to determine how you + * would like to track the metrics + * of how your recipients interact + * with your email + */ + public function setTrackingSettings($tracking_settings) + { + $this->tracking_settings = $tracking_settings; + } + + /** + * Retrieve the TrackingSettings object attached to a Mail object + * + * @return TrackingSettings + */ + public function getTrackingSettings() + { + return $this->tracking_settings; + } + + /** + * Set the click tracking settings on a TrackingSettings object + * + * @param bool|ClickTracking $enable A ClickTracking object or a boolean + * to determine if this setting is active + * @param bool|null $enable_text Indicates if this setting should be + * included in the text/plain portion of + * your email + * + * @throws TypeException + */ + public function setClickTracking($enable = null, $enable_text = null) + { + if (!($this->tracking_settings instanceof TrackingSettings)) { + $this->tracking_settings = new TrackingSettings(); + } + $this->tracking_settings->setClickTracking($enable, $enable_text); + } + + /** + * Set the open tracking settings on a TrackingSettings object + * + * @param bool|OpenTracking $enable A OpenTracking object or a boolean + * to determine if this setting is + * active + * @param string|null $substitution_tag Allows you to specify a + * substitution tag that you can + * insert in the body of your email + * at a location that you desire. + * This tag will be replaced by the + * open tracking pixel + * + * @throws TypeException + */ + public function setOpenTracking($enable = null, $substitution_tag = null) + { + if (!($this->tracking_settings instanceof TrackingSettings)) { + $this->tracking_settings = new TrackingSettings(); + } + $this->tracking_settings->setOpenTracking($enable, $substitution_tag); + } + + /** + * Set the subscription tracking settings on a TrackingSettings object + * + * @param bool|SubscriptionTracking $enable A SubscriptionTracking + * object or a boolean to + * determine if this setting + * is active + * @param string|null $text Text to be appended to the + * email, with the + * subscription tracking + * link. You may control + * where the link is by using + * the tag <% %> + * @param string|null $html HTML to be appended to the + * email, with the + * subscription tracking + * link. You may control + * where the link is by using + * the tag <% %> + * @param string|null $substitution_tag A tag that will be + * replaced with the + * unsubscribe URL. for + * example: + * [unsubscribe_url]. If this + * parameter is used, it will + * override both the text and + * html parameters. The URL + * of the link will be placed + * at the substitution tag’s + * location, with no + * additional formatting + */ + public function setSubscriptionTracking( + $enable = null, + $text = null, + $html = null, + $substitution_tag = null + ) { + if (!($this->tracking_settings instanceof TrackingSettings)) { + $this->tracking_settings = new TrackingSettings(); + } + $this->tracking_settings->setSubscriptionTracking( + $enable, + $text, + $html, + $substitution_tag + ); + } + + /** + * Set the Google anatlyics settings on a TrackingSettings object + * + * @param bool|Ganalytics $enable A Ganalytics object or a boolean to + * determine if this setting + * is active + * @param string|null $utm_source Name of the referrer source. (e.g. + * Google, SomeDomain.com, or + * Marketing Email) + * @param string|null $utm_medium Name of the marketing medium. + * (e.g. Email) + * @param string|null $utm_term Used to identify any paid keywords. + * @param string|null $utm_content Used to differentiate your campaign + * from advertisements + * @param string|null $utm_campaign The name of the campaign + * + * @throws TypeException + */ + public function setGanalytics( + $enable = null, + $utm_source = null, + $utm_medium = null, + $utm_term = null, + $utm_content = null, + $utm_campaign = null + ) { + if (!($this->tracking_settings instanceof TrackingSettings)) { + $this->tracking_settings = new TrackingSettings(); + } + $this->tracking_settings->setGanalytics( + $enable, + $utm_source, + $utm_medium, + $utm_term, + $utm_content, + $utm_campaign + ); + } + + /** + * Return an array representing a request object for the Twilio SendGrid API + * + * @return null|array + * @throws TypeException + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + // Detect if we are using the new dynamic templates + if ($this->getTemplateId() !== null && strpos($this->getTemplateId()->getTemplateId(), 'd-') === 0) { + foreach ($this->personalization as $personalization) { + $personalization->setHasDynamicTemplate(true); + } + } + + return array_filter( + [ + 'personalizations' => array_values(array_filter( + $this->getPersonalizations(), + static function ($value) { + return null !== $value && null !== $value->jsonSerialize(); + } + )), + 'from' => $this->getFrom(), + 'reply_to' => $this->getReplyTo(), + 'subject' => $this->getGlobalSubject(), + 'content' => $this->getContents(), + 'attachments' => $this->getAttachments(), + 'template_id' => $this->getTemplateId(), + 'sections' => $this->getSections(), + 'headers' => $this->getGlobalHeaders(), + 'categories' => $this->getCategories(), + 'custom_args' => $this->getGlobalCustomArgs(), + 'send_at' => $this->getGlobalSendAt(), + 'batch_id' => $this->getBatchId(), + 'asm' => $this->getASM(), + 'ip_pool_name' => $this->getIpPoolName(), + 'substitutions' => $this->getGlobalSubstitutions(), + 'mail_settings' => $this->getMailSettings(), + 'tracking_settings' => $this->getTrackingSettings() + ], + static function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/MailSettings.php b/lib/sendgrid-php/lib/mail/MailSettings.php new file mode 100644 index 000000000..6440d8aac --- /dev/null +++ b/lib/sendgrid-php/lib/mail/MailSettings.php @@ -0,0 +1,398 @@ +setBccSettings($bcc_settings); + } + if (isset($bypass_bounce_management)) { + $this->setBypassBounceManagement($bypass_bounce_management); + } + if (isset($bypass_list_management)) { + $this->setBypassListManagement($bypass_list_management); + } + if (isset($bypass_spam_management)) { + $this->setBypassSpamManagement($bypass_spam_management); + } + if (isset($bypass_unsubscribe_management)) { + $this->setBypassUnsubscribeManagement($bypass_unsubscribe_management); + } + if (isset($footer)) { + $this->setFooter($footer); + } + if (isset($sandbox_mode)) { + $this->setSandboxMode($sandbox_mode); + } + if (isset($spam_check)) { + $this->setSpamCheck($spam_check); + } + } + + /** + * Set the bcc settings on a MailSettings object + * + * @param BccSettings|bool $enable The BccSettings object or an indication + * if the setting is enabled + * @param string|null $email The email address that you would like + * to receive the BCC + * + * @throws \SendGrid\Mail\TypeException + */ + public function setBccSettings($enable, $email = null) + { + if ($enable instanceof BccSettings) { + $bcc = $enable; + $this->bcc = $bcc; + return; + } + Assert::boolean( + $enable, 'enable', 'Value "$enable" must be an instance of SendGrid\Mail\BccSettings or a boolean.' + ); + $this->bcc = new BccSettings($enable, $email); + } + + /** + * Retrieve the bcc settings from a MailSettings object + * + * @return Bcc + */ + public function getBccSettings() + { + return $this->bcc; + } + + + /** + * Set bypass bounce management settings on a MailSettings object + * + * @param BypassBounceManagement|bool $enable The BypassBounceManagement + * object or an indication + * if the setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setBypassBounceManagement($enable) + { + if ($enable instanceof BypassBounceManagement) { + $bypass_bounce_management = $enable; + $this->bypass_bounce_management = $bypass_bounce_management; + return; + } + Assert::boolean( + $enable, 'enable', 'Value "$enable" must be an instance of SendGrid\Mail\BypassBounceManagement + or a boolean.' + ); + $this->bypass_bounce_management = new BypassBounceManagement($enable); + } + + /** + * Set bypass list management settings on a MailSettings object + * + * @param BypassListManagement|bool $enable The BypassListManagement + * object or an indication + * if the setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setBypassListManagement($enable) + { + if ($enable instanceof BypassListManagement) { + $bypass_list_management = $enable; + $this->bypass_list_management = $bypass_list_management; + return; + } + Assert::boolean( + $enable, 'enable', 'Value "$enable" must be an instance of SendGrid\Mail\BypassListManagement + or a boolean.' + ); + $this->bypass_list_management = new BypassListManagement($enable); + } + + /** + * Set bypass spam management settings on a MailSettings object + * + * @param BypassSpamManagement|bool $enable The BypassSpamManagement + * object or an indication + * if the setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setBypassSpamManagement($enable) + { + if ($enable instanceof BypassSpamManagement) { + $bypass_spam_management = $enable; + $this->bypass_spam_management = $bypass_spam_management; + return; + } + Assert::boolean( + $enable, 'enable', 'Value "$enable" must be an instance of SendGrid\Mail\BypassSpamManagement or a boolean.' + ); + $this->bypass_spam_management = new BypassSpamManagement($enable); + } + + /** + * Set bypass unsubscribe management settings on a MailSettings object + * + * @param BypassUnsubscribeManagement|bool $enable The BypassUnsubscribeManagement + * object or an indication + * if the setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setBypassUnsubscribeManagement($enable) + { + if ($enable instanceof BypassUnsubscribeManagement) { + $bypass_unsubscribe_management = $enable; + $this->bypass_unsubscribe_management = $bypass_unsubscribe_management; + return; + } + Assert::boolean( + $enable, 'enable', 'Value "$enable" must be an instance of SendGrid\Mail\BypassUnsubscribeManagement + or a boolean.' + ); + $this->bypass_unsubscribe_management = new BypassUnsubscribeManagement($enable); + } + + /** + * Retrieve bypass bounce management settings from a MailSettings object + * + * @return BypassBounceManagement + */ + public function getBypassBounceManagement() + { + return $this->bypass_bounce_management; + } + + /** + * Retrieve bypass list management settings from a MailSettings object + * + * @return BypassListManagement + */ + public function getBypassListManagement() + { + return $this->bypass_list_management; + } + + /** + * Retrieve bypass spam management settings from a MailSettings object + * + * @return BypassSpamManagement + */ + public function getBypassSpamManagement() + { + return $this->bypass_spam_management; + } + + /** + * Retrieve bypass unsubscribe management settings from a MailSettings object + * + * @return BypassUnsubscribeManagement + */ + public function getBypassUnsubscribeManagement() + { + return $this->bypass_unsubscribe_management; + } + + /** + * Set the footer settings on a MailSettings object + * + * @param Footer|bool $enable The Footer object or an indication + * if the setting is enabled + * @param string|null $text The plain text content of your footer + * @param string|null $html The HTML content of your footer + * + * @throws TypeException + */ + public function setFooter($enable, $text = null, $html = null) + { + if ($enable instanceof Footer) { + $footer = $enable; + $this->footer = $footer; + return; + } + $this->footer = new Footer($enable, $text, $html); + } + + /** + * Retrieve the footer settings from a MailSettings object + * + * @return Footer + */ + public function getFooter() + { + return $this->footer; + } + + /** + * Set sandbox mode settings on a MailSettings object + * + * @param SandBoxMode|bool $enable The SandBoxMode object or an + * indication if the setting is enabled + * + * @throws TypeException + */ + public function setSandboxMode($enable) + { + if ($enable instanceof SandBoxMode) { + $sandbox_mode = $enable; + $this->sandbox_mode = $sandbox_mode; + return; + } + Assert::boolean( + $enable, 'enable', 'Value "$enable" must be an instance of SendGrid\Mail\SandBoxMode or a boolean.' + ); + $this->sandbox_mode = new SandBoxMode($enable); + } + + /** + * Retrieve sandbox mode settings on a MailSettings object + * + * @return SandBoxMode + */ + public function getSandboxMode() + { + return $this->sandbox_mode; + } + + /** + * Enable sandbox mode on a MailSettings object + * + * @throws TypeException + */ + public function enableSandboxMode() + { + $this->setSandboxMode(true); + } + + /** + * Disable sandbox mode on a MailSettings object + * + * @throws TypeException + */ + public function disableSandboxMode() + { + $this->setSandboxMode(false); + } + + /** + * Set spam check settings on a MailSettings object + * + * @param SpamCheck|bool $enable The SpamCheck object or an + * indication if the setting is enabled + * @param int $threshold The threshold used to determine if your + * content qualifies as spam on a scale + * from 1 to 10, with 10 being most strict, + * or most + * @param string $post_to_url An Inbound Parse URL that you would like + * a copy of your email along with the spam + * report to be sent to + * + * @throws TypeException + */ + public function setSpamCheck($enable, $threshold = null, $post_to_url = null) + { + if ($enable instanceof SpamCheck) { + $spam_check = $enable; + $this->spam_check = $spam_check; + return; + } + Assert::boolean( + $enable, 'enable', 'Value "$enable" must be an instance of SendGrid\Mail\SpamCheck or a boolean.' + ); + $this->spam_check = new SpamCheck($enable, $threshold, $post_to_url); + } + + /** + * Retrieve spam check settings from a MailSettings object + * + * @return SpamCheck + */ + public function getSpamCheck() + { + return $this->spam_check; + } + + /** + * Return an array representing a MailSettings object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'bcc' => $this->getBccSettings(), + 'bypass_bounce_management' => $this->getBypassBounceManagement(), + 'bypass_list_management' => $this->getBypassListManagement(), + 'bypass_spam_management' => $this->getBypassSpamManagement(), + 'bypass_unsubscribe_management' => $this->getBypassUnsubscribeManagement(), + 'footer' => $this->getFooter(), + 'sandbox_mode' => $this->getSandboxMode(), + 'spam_check' => $this->getSpamCheck() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/MimeType.php b/lib/sendgrid-php/lib/mail/MimeType.php new file mode 100644 index 000000000..dbedaf94f --- /dev/null +++ b/lib/sendgrid-php/lib/mail/MimeType.php @@ -0,0 +1,17 @@ +setEnable($enable); + } + if (isset($substitution_tag)) { + $this->setSubstitutionTag($substitution_tag); + } + } + + /** + * Update the enable setting on a OpenTracking object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting on a OpenTracking object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Set the substitution tag on a OpenTracking object + * + * @param string $substitution_tag Allows you to specify a substitution + * tag that you can insert in the body + * of your email at a location that you + * desire. This tag will be replaced by + * the open tracking pixel + * + * @throws \SendGrid\Mail\TypeException + */ + public function setSubstitutionTag($substitution_tag) + { + Assert::string($substitution_tag, 'substitution_tag'); + + $this->substitution_tag = $substitution_tag; + } + + /** + * Retrieve the substitution tag from a OpenTracking object + * + * @return string + */ + public function getSubstitutionTag() + { + return $this->substitution_tag; + } + + /** + * Return an array representing a OpenTracking object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable(), + 'substitution_tag' => $this->getSubstitutionTag() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/Personalization.php b/lib/sendgrid-php/lib/mail/Personalization.php new file mode 100644 index 000000000..020e8245c --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Personalization.php @@ -0,0 +1,331 @@ +tos[] = $email; + } + + /** + * Retrieve To object(s) from a Personalization object + * + * @return To[] + */ + public function getTos() + { + return $this->tos; + } + + /** + * Add a From object to a Personalization object + * + * @param From $email From object + */ + public function addFrom($email) + { + $this->from = $email; + } + + /** + * Retrieve From object from a Personalization object + * + * @return From|null + */ + public function getFrom() + { + return $this->from; + } + + /** + * Add a Cc object to a Personalization object + * + * @param Cc $email Cc object + */ + public function addCc($email) + { + $this->ccs[] = $email; + } + + /** + * Retrieve Cc object(s) from a Personalization object + * + * @return Cc[] + */ + public function getCcs() + { + return $this->ccs; + } + + /** + * Add a Bcc object to a Personalization object + * + * @param Bcc $email Bcc object + */ + public function addBcc($email) + { + $this->bccs[] = $email; + } + + /** + * Retrieve Bcc object(s) from a Personalization object + * + * @return Bcc[] + */ + public function getBccs() + { + return $this->bccs; + } + + /** + * Add a subject object to a Personalization object + * + * @param Subject|string $subject Subject object or string + * + * @throws TypeException + */ + public function setSubject($subject) + { + if (!($subject instanceof Subject)) { + Assert::string($subject, 'subject', '"$subject" must be an instance of SendGrid\Mail\Subject or a string'); + + $subject = new Subject($subject); + } + $this->subject = $subject; + } + + /** + * Retrieve a Subject object from a Personalization object + * + * @return Subject|null + */ + public function getSubject() + { + return $this->subject; + } + + /** + * Add a Header object to a Personalization object + * + * @param Header $header Header object + */ + public function addHeader($header) + { + Assert::isInstanceOf($header, 'header', Header::class); + + $this->headers[$header->getKey()] = $header->getValue(); + } + + /** + * Retrieve header key/value pairs from a Personalization object + * + * @return array|null + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * Add a Substitution object or key/value to a Personalization object + * + * @param Substitution|string $data DynamicTemplateData object or the key of a + * dynamic data + * @param string|null $value The value of dynamic data + * + * @throws TypeException + */ + public function addDynamicTemplateData($data, $value = null) + { + $this->addSubstitution($data, $value); + } + + /** + * Retrieve dynamic template data key/value pairs from a Personalization object + * + * @return array|null + */ + public function getDynamicTemplateData() + { + return $this->getSubstitutions(); + } + + /** + * Add a Substitution object or key/value to a Personalization object + * + * @param Substitution|string $substitution Substitution object or the key of a + * substitution + * @param string|null $value The value of a substitution + * + * @throws TypeException + */ + public function addSubstitution($substitution, $value = null) + { + if (!($substitution instanceof Substitution)) { + $key = $substitution; + $substitution = new Substitution($key, $value); + } + $this->substitutions[$substitution->getKey()] = $substitution->getValue(); + } + + /** + * Retrieve substitution key/value pairs from a Personalization object + * + * @return array|null + */ + public function getSubstitutions() + { + return $this->substitutions; + } + + /** + * Add a CustomArg object to a Personalization object + * + * @param CustomArg $custom_arg CustomArg object + * + * @throws TypeException + */ + public function addCustomArg($custom_arg) + { + Assert::isInstanceOf($custom_arg, 'custom_arg', CustomArg::class); + + $this->custom_args[$custom_arg->getKey()] = (string)$custom_arg->getValue(); + } + + /** + * Retrieve custom arg key/value pairs from a Personalization object + * + * @return array|null + */ + public function getCustomArgs() + { + return $this->custom_args; + } + + /** + * Add a SendAt object to a Personalization object + * + * @param SendAt $send_at SendAt object + * + * @throws TypeException + */ + public function setSendAt($send_at) + { + Assert::isInstanceOf($send_at, 'send_at', SendAt::class); + + $this->send_at = $send_at; + } + + /** + * Retrieve a SendAt object from a Personalization object + * + * @return SendAt|null + */ + public function getSendAt() + { + return $this->send_at; + } + + /** + * Specify if this personalization is using dynamic templates + * + * @param bool $has_dynamic_template are we using dynamic templates + * + * @throws TypeException + */ + public function setHasDynamicTemplate($has_dynamic_template) + { + Assert::boolean($has_dynamic_template, 'has_dynamic_template'); + + $this->has_dynamic_template = $has_dynamic_template; + } + + /** + * Determine if this Personalization object is using dynamic templates + * + * @return bool + */ + public function getHasDynamicTemplate() + { + return $this->has_dynamic_template; + } + + /** + * Return an array representing a Personalization object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + if ($this->getHasDynamicTemplate()) { + $dynamic_substitutions = $this->getSubstitutions(); + $substitutions = null; + } else { + $substitutions = $this->getSubstitutions(); + $dynamic_substitutions = null; + } + + return array_filter( + [ + 'to' => $this->getTos(), + 'from' => $this->getFrom(), + 'cc' => $this->getCcs(), + 'bcc' => $this->getBccs(), + 'subject' => $this->getSubject(), + 'headers' => $this->getHeaders(), + 'substitutions' => $substitutions, + 'dynamic_template_data' => $dynamic_substitutions, + 'custom_args' => $this->getCustomArgs(), + 'send_at' => $this->getSendAt() + ], + static function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/PlainTextContent.php b/lib/sendgrid-php/lib/mail/PlainTextContent.php new file mode 100644 index 000000000..bae8b671e --- /dev/null +++ b/lib/sendgrid-php/lib/mail/PlainTextContent.php @@ -0,0 +1,26 @@ +setEnable($enable); + } + } + + /** + * Update the enable setting on a SandBoxMode object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting on a SandBoxMode object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Return an array representing a SandBoxMode object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/Section.php b/lib/sendgrid-php/lib/mail/Section.php new file mode 100644 index 000000000..ae7123fd3 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Section.php @@ -0,0 +1,108 @@ +setKey($key); + } + if (isset($value)) { + $this->setValue($value); + } + } + + /** + * Add the key on a Section object + * + * @param string $key Section key + * + * @throws \SendGrid\Mail\TypeException + */ + public function setKey($key) + { + Assert::string($key, 'key'); + + $this->key = $key; + } + + /** + * Retrieve the key from a Section object + * + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * Add the value on a Section object + * + * @param string $value Section value + * + * @throws \SendGrid\Mail\TypeException + */ + public function setValue($value) + { + Assert::string($value, 'value'); + + $this->value = $value; + } + + /** + * Retrieve the value from a Section object + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * Return an array representing a Section object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'key' => $this->getKey(), + 'value' => $this->getValue() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/SendAt.php b/lib/sendgrid-php/lib/mail/SendAt.php new file mode 100644 index 000000000..f1a22e965 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/SendAt.php @@ -0,0 +1,95 @@ +setSendAt($send_at); + } + } + + /** + * Add the send at value to a SendAt object + * + * @param int $send_at A unix timestamp allowing you to specify when you + * want your email to be delivered. This may be + * overridden by the personalizations[x].send_at + * parameter. You can't schedule more than 72 hours + * in advance. If you have the flexibility, it's better + * to schedule mail for off-peak times. Most emails are + * scheduled and sent at the top of the hour or half + * hour. Scheduling email to avoid those times (for + * example, scheduling at 10:53) can result in lower + * deferral rates because it won't be going through + * our servers at the same times as everyone else's mail + * + * @throws \SendGrid\Mail\TypeException + */ + public function setSendAt($send_at) + { + Assert::integer($send_at, 'send_at'); + + $this->send_at = $send_at; + } + + /** + * Retrieve the send at value from a SendAt object + * + * @return int + */ + public function getSendAt() + { + return $this->send_at; + } + + /** + * Return an array representing a SendAt object for the Twilio SendGrid API + * + * @return int + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getSendAt(); + } +} diff --git a/lib/sendgrid-php/lib/mail/SpamCheck.php b/lib/sendgrid-php/lib/mail/SpamCheck.php new file mode 100644 index 000000000..a43906038 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/SpamCheck.php @@ -0,0 +1,156 @@ +setEnable($enable); + } + if (isset($threshold)) { + $this->setThreshold($threshold); + } + if (isset($post_to_url)) { + $this->setPostToUrl($post_to_url); + } + } + + /** + * Update the enable setting on a SpamCheck object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting on a SpamCheck object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Set the threshold value on a SpamCheck object + * + * @param int $threshold The threshold used to determine if your + * content qualifies as spam on a scale + * from 1 to 10, with 10 being most strict, + * or most + * + * @throws \SendGrid\Mail\TypeException + */ + public function setThreshold($threshold) + { + Assert::minValue($threshold, 'threshold', 1); + Assert::maxValue($threshold, 'threshold', 10); + + $this->threshold = $threshold; + } + + /** + * Retrieve the threshold value from a SpamCheck object + * + * @return int + */ + public function getThreshold() + { + return $this->threshold; + } + + /** + * Set the post to url value on a SpamCheck object + * + * @param string $post_to_url An Inbound Parse URL that you would like + * a copy of your email along with the spam + * report to be sent to + * + * @throws \SendGrid\Mail\TypeException + */ + public function setPostToUrl($post_to_url) + { + Assert::string($post_to_url, 'post_to_url'); + + $this->post_to_url = $post_to_url; + } + + /** + * Retrieve the post to url value from a SpamCheck object + * + * @return string + */ + public function getPostToUrl() + { + return $this->post_to_url; + } + + /** + * Return an array representing a SpamCheck object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable(), + 'threshold' => $this->getThreshold(), + 'post_to_url' => $this->getPostToUrl() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/Subject.php b/lib/sendgrid-php/lib/mail/Subject.php new file mode 100644 index 000000000..f62c4050d --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Subject.php @@ -0,0 +1,68 @@ +setSubject($subject); + } + } + + /** + * Set the subject on a Subject object + * + * @param string $subject The email subject + * + * @throws TypeException + */ + public function setSubject($subject) + { + Assert::minLength($subject, 'subject', 1); + + $this->subject = $subject; + } + + /** + * Retrieve the subject from a Subject object + * + * @return string + */ + public function getSubject() + { + return mb_convert_encoding((string)$this->subject, 'UTF-8', 'UTF-8'); + } + + /** + * Return an array representing a Subject object for the Twilio SendGrid API + * + * @return string + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getSubject(); + } +} diff --git a/lib/sendgrid-php/lib/mail/SubscriptionTracking.php b/lib/sendgrid-php/lib/mail/SubscriptionTracking.php new file mode 100644 index 000000000..4a51eb4d3 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/SubscriptionTracking.php @@ -0,0 +1,217 @@ + + */ + private $text; + /** + * @var $htmlstring string to be appended to the email, with the + * subscription tracking link. You may control where the + * link is by using the tag <% %> + */ + private $html; + /** + * @var $substitution_tag string A tag that will be replaced with the + * unsubscribe URL. for example: [unsubscribe_url]. If + * this parameter is used, it will override both the text + * and html parameters. The URL of the link will be placed + * at the substitution tag’s location, with no additional + * formatting + */ + private $substitution_tag; + + /** + * Optional constructor + * + * @param bool|null $enable Indicates if this setting is enabled + * @param string|null $text Text to be appended to the email, with + * the subscription tracking link. You may + * control where the link is by using the + * tag <% %> + * @param string|null $html HTML to be appended to the email, with + * the subscription tracking link. You may + * control where the link is by using the + * tag <% %> + * @param string|null $substitution_tag A tag that will be replaced with the + * unsubscribe URL. For example: + * [unsubscribe_url]. If this parameter + * is used, it will override both the text + * and html parameters. The URL of the link + * will be placed at the substitution tag’s + * location, with no additional formatting + * @throws \SendGrid\Mail\TypeException + */ + public function __construct( + $enable = null, + $text = null, + $html = null, + $substitution_tag = null + ) { + if (isset($enable)) { + $this->setEnable($enable); + } + if (isset($text)) { + $this->setText($text); + } + if (isset($html)) { + $this->setHtml($html); + } + if (isset($substitution_tag)) { + $this->setSubstitutionTag($substitution_tag); + } + } + + /** + * Update the enable setting on a SubscriptionTracking object + * + * @param bool $enable Indicates if this setting is enabled + * + * @throws \SendGrid\Mail\TypeException + */ + public function setEnable($enable) + { + Assert::boolean($enable, 'enable'); + + $this->enable = $enable; + } + + /** + * Retrieve the enable setting from a SubscriptionTracking object + * + * @return bool + */ + public function getEnable() + { + return $this->enable; + } + + /** + * Add text to a SubscriptionTracking object + * + * @param string $text Text to be appended to the email, with + * the subscription tracking link. You may + * control where the link is by using the + * tag <% %> + * + * @throws \SendGrid\Mail\TypeException + */ + public function setText($text) + { + Assert::string($text, 'text'); + + $this->text = $text; + } + + /** + * Retrieve text from a SubscriptionTracking object + * + * @return string + */ + public function getText() + { + return $this->text; + } + + /** + * Add HTML to a SubscriptionTracking object + * + * @param string $html HTML to be appended to the email, with + * the subscription tracking link. You may + * control where the link is by using the + * tag <% %> + * + * @throws \SendGrid\Mail\TypeException + */ + public function setHtml($html) + { + Assert::string($html, 'html'); + + $this->html = $html; + } + + /** + * Retrieve HTML from a SubscriptionTracking object + * + * @return string + */ + public function getHtml() + { + return $this->html; + } + + /** + * Add a substitution tag to a SubscriptionTracking object + * + * @param string $substitution_tag A tag that will be replaced with the + * unsubscribe URL. for example: + * [unsubscribe_url]. If this parameter + * is used, it will override both the text + * and html parameters. The URL of the link + * will be placed at the substitution tag’s + * location, with no additional formatting %> + * + * @throws \SendGrid\Mail\TypeException + */ + public function setSubstitutionTag($substitution_tag) + { + Assert::string($substitution_tag, 'substitution_tag'); + + $this->substitution_tag = $substitution_tag; + } + + /** + * Retrieve a substitution tag from a SubscriptionTracking object + * + * @return string + */ + public function getSubstitutionTag() + { + return $this->substitution_tag; + } + + /** + * Return an array representing a SubscriptionTracking object + * for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'enable' => $this->getEnable(), + 'text' => $this->getText(), + 'html' => $this->getHtml(), + 'substitution_tag' => $this->getSubstitutionTag() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/Substitution.php b/lib/sendgrid-php/lib/mail/Substitution.php new file mode 100644 index 000000000..6e608b446 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/Substitution.php @@ -0,0 +1,118 @@ +setKey($key); + } + if (isset($value)) { + $this->setValue($value); + } + } + + /** + * Add the key on a Substitution object + * + * @param string $key Substitution key + * + * @throws \SendGrid\Mail\TypeException + */ + public function setKey($key) + { + Assert::string($key, 'key'); + + $this->key = $key; + } + + /** + * Retrieve the key from a Substitution object + * + * @return string + */ + public function getKey() + { + return $this->key; + } + + /** + * Add the value on a Substitution object + * + * @param string|array|object|bool|int|float $value Substitution value + * + * @throws \SendGrid\Mail\TypeException + */ + public function setValue($value) + { + Assert::accept($value, 'value', static function ($val) { + return \is_string($val) + || filter_var($val, FILTER_VALIDATE_INT) !== false + || \is_float($val) + || \is_bool($val) + || \is_array($val) + || \is_object($val); + }, '"$value" must be an array, object, boolean, string, numeric or integer.'); + + $this->value = $value; + } + + /** + * Retrieve the value from a Substitution object + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * Return an array representing a Substitution object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'key' => $this->getKey(), + 'value' => $this->getValue() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/TemplateId.php b/lib/sendgrid-php/lib/mail/TemplateId.php new file mode 100644 index 000000000..06661f3c0 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/TemplateId.php @@ -0,0 +1,79 @@ +setTemplateId($template_id); + } + } + + /** + * Add a template id to a TemplateId object + * + * @param string $template_id The id of a template that you would like + * to use. If you use a template that contains + * a subject and content (either text or html), + * you do not need to specify those at the + * personalizations nor message level + * + * @throws \SendGrid\Mail\TypeException + */ + public function setTemplateId($template_id) + { + Assert::string($template_id, 'template_id'); + + $this->template_id = $template_id; + } + + /** + * Retrieve a template id from a TemplateId object + * + * @return string + */ + public function getTemplateId() + { + return $this->template_id; + } + + /** + * Return an array representing a TemplateId object for the Twilio SendGrid API + * + * @return string + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getTemplateId(); + } +} diff --git a/lib/sendgrid-php/lib/mail/To.php b/lib/sendgrid-php/lib/mail/To.php new file mode 100644 index 000000000..1ebdf5c3e --- /dev/null +++ b/lib/sendgrid-php/lib/mail/To.php @@ -0,0 +1,15 @@ +setClickTracking($click_tracking); + } + if (isset($open_tracking)) { + $this->setOpenTracking($open_tracking); + } + if (isset($subscription_tracking)) { + $this->setSubscriptionTracking($subscription_tracking); + } + if (isset($ganalytics)) { + $this->setGanalytics($ganalytics); + } + } + + /** + * Set the click tracking settings on a TrackingSettings object + * + * @param ClickTracking|bool $enable The ClickTracking object or an + * indication if the setting is enabled + * @param bool|null $enable_text Indicates if this setting should be + * included in the text/plain portion of + * your email + * + * @throws TypeException + */ + public function setClickTracking($enable, $enable_text = null) + { + if ($enable instanceof ClickTracking) { + $click_tracking = $enable; + $this->click_tracking = $click_tracking; + return; + } + $this->click_tracking = new ClickTracking($enable, $enable_text); + } + + /** + * Retrieve the click tracking settings from a TrackingSettings object + * + * @return ClickTracking + */ + public function getClickTracking() + { + return $this->click_tracking; + } + + /** + * Set the open tracking settings on a TrackingSettings object + * + * @param OpenTracking|bool $enable The ClickTracking object or an + * indication if the setting is + * enabled + * @param string|null $substitution_tag Allows you to specify a + * substitution tag that you can + * insert in the body of your email + * at a location that you desire. + * This tag will be replaced by + * the open tracking pixelail + * + * @throws TypeException + */ + public function setOpenTracking($enable, $substitution_tag = null) + { + if ($enable instanceof OpenTracking) { + $open_tracking = $enable; + $this->open_tracking = $open_tracking; + return; + } + $this->open_tracking = new OpenTracking($enable, $substitution_tag); + } + + /** + * Retrieve the open tracking settings on a TrackingSettings object + * + * @return OpenTracking + */ + public function getOpenTracking() + { + return $this->open_tracking; + } + + /** + * Set the subscription tracking settings on a TrackingSettings object + * + * @param SubscriptionTracking|bool $enable The SubscriptionTracking + * object or an indication + * if the setting is enabled + * @param string|null $text Text to be appended to the + * email, with the + * subscription tracking + * link. You may control + * where the link is by using + * the tag <% %> + * @param string|null $html HTML to be appended to the + * email, with the + * subscription tracking + * link. You may control + * where the link is by using + * the tag <% %> + * @param string|null $substitution_tag A tag that will be + * replaced with the + * unsubscribe URL. For + * example: + * [unsubscribe_url]. If this + * parameter is used, it will + * override both the text + * and html parameters. The + * URL of the link will be + * placed at the substitution + * tag’s location, with no + * additional formatting + * + * @throws TypeException + */ + public function setSubscriptionTracking( + $enable, + $text = null, + $html = null, + $substitution_tag = null + ) { + if ($enable instanceof SubscriptionTracking) { + $subscription_tracking = $enable; + $this->subscription_tracking = $subscription_tracking; + return; + } + $this->subscription_tracking = new SubscriptionTracking($enable, $text, $html, $substitution_tag); + } + + /** + * Retrieve the subscription tracking settings from a TrackingSettings object + * + * @return SubscriptionTracking + */ + public function getSubscriptionTracking() + { + return $this->subscription_tracking; + } + + /** + * Set the Google analytics settings on a TrackingSettings object + * + * @param Ganalytics|bool $enable The Ganalytics object or an indication + * if the setting is enabled + * @param string|null $utm_source Name of the referrer source. (e.g. + * Google, SomeDomain.com, or + * Marketing Email) + * @param string|null $utm_medium Name of the marketing medium. (e.g. + * Email) + * @param string|null $utm_term Used to identify any paid keywords + * @param string|null $utm_content Used to differentiate your campaign from + * advertisements + * @param string|null $utm_campaign The name of the campaign + * + * @throws TypeException + */ + public function setGanalytics( + $enable, + $utm_source = null, + $utm_medium = null, + $utm_term = null, + $utm_content = null, + $utm_campaign = null + ) { + if ($enable instanceof Ganalytics) { + $ganalytics = $enable; + $this->ganalytics = $ganalytics; + return; + } + $this->ganalytics = new Ganalytics( + $enable, + $utm_source, + $utm_medium, + $utm_term, + $utm_content, + $utm_campaign + ); + } + + /** + * Retrieve the Google analytics settings from a TrackingSettings object + * + * @return Ganalytics + */ + public function getGanalytics() + { + return $this->ganalytics; + } + + /** + * Return an array representing a TrackingSettings object for the Twilio SendGrid API + * + * @return null|array + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return array_filter( + [ + 'click_tracking' => $this->getClickTracking(), + 'open_tracking' => $this->getOpenTracking(), + 'subscription_tracking' => $this->getSubscriptionTracking(), + 'ganalytics' => $this->getGanalytics() + ], + function ($value) { + return $value !== null; + } + ) ?: null; + } +} diff --git a/lib/sendgrid-php/lib/mail/TypeException.php b/lib/sendgrid-php/lib/mail/TypeException.php new file mode 100644 index 000000000..0b43f9c45 --- /dev/null +++ b/lib/sendgrid-php/lib/mail/TypeException.php @@ -0,0 +1,8 @@ +validateDateFormat($startDate); + if (null !== $endDate) { + $this->validateDateFormat($endDate); + } + if (null !== $aggregatedBy) { + $this->validateOptions( + 'aggregatedBy', + $aggregatedBy, + self::OPTIONS_AGGREGATED_BY + ); + } + $this->startDate = $startDate; + $this->endDate = $endDate; + $this->aggregatedBy = $aggregatedBy; + } + + /** + * Retrieve global stats parameters, start date, end date and + * aggregated by + * + * @return array + */ + public function getGlobal() + { + return [ + 'start_date' => $this->startDate, + 'end_date' => $this->endDate, + 'aggregated_by' => $this->aggregatedBy + ]; + } + + /** + * Retrieve an array of categories + * + * @param array $categories + * + * @return array + * @throws Exception + */ + public function getCategory($categories) + { + $this->validateNumericArray('categories', $categories); + $stats = $this->getGlobal(); + $stats['categories'] = $categories; + return $stats; + } + + /** + * Retrieve global stats parameters, start date, end date and + * aggregated for the given set of subusers + * + * @param array $subusers Subuser accounts + * + * @return array + * @throws Exception + */ + public function getSubuser($subusers) + { + $this->validateNumericArray('subusers', $subusers); + $stats = $this->getGlobal(); + $stats['subusers'] = $subusers; + return $stats; + } + + /** + * Retrieve global stats parameters, start date, end date, + * aggregated by, sort by metric, sort by direction, limit + * and offset + * + * @param string $sortByMetric blocks|bounce_drops|bounces| + * clicks|deferred|delivered| + * invalid_emails|opens|processed| + * requests|spam_report_drops| + * spam_reports|unique_clicks| + * unique_opens|unsubscribe_drops| + * unsubscribes + * @param string $sortByDirection asc|desc + * @param integer $limit The number of results to return + * @param integer $offset The point in the list to begin + * retrieving results + * + * @return array + * @throws Exception + */ + public function getSum( + $sortByMetric = 'delivered', + $sortByDirection = 'desc', + $limit = 5, + $offset = 0 + ) { + $this->validateOptions( + 'sortByDirection', + $sortByDirection, + self::OPTIONS_SORT_DIRECTION + ); + $this->validateInteger('limit', $limit); + $this->validateInteger('offset', $offset); + $stats = $this->getGlobal(); + $stats['sort_by_metric'] = $sortByMetric; + $stats['sort_by_direction'] = $sortByDirection; + $stats['limit'] = $limit; + $stats['offset'] = $offset; + return $stats; + } + + /** + * Retrieve monthly stats by subuser + * + * @param string $subuser Subuser account + * @param string $sortByMetric blocks|bounce_drops|bounces| + * clicks|deferred|delivered| + * invalid_emails|opens|processed| + * requests|spam_report_drops| + * spam_reports|unique_clicks| + * unique_opens|unsubscribe_drops| + * unsubscribes + * @param string $sortByDirection asc|desc + * @param integer $limit The number of results to return + * @param integer $offset The point in the list to begin + * retrieving results + * + * @return array + * @throws Exception + */ + public function getSubuserMonthly( + $subuser = null, + $sortByMetric = 'delivered', + $sortByDirection = 'desc', + $limit = 5, + $offset = 0 + ) { + $this->validateOptions( + 'sortByDirection', + $sortByDirection, + self::OPTIONS_SORT_DIRECTION + ); + $this->validateInteger('limit', $limit); + $this->validateInteger('offset', $offset); + return [ + 'date' => $this->startDate, + 'subuser' => $subuser, + 'sort_by_metric' => $sortByMetric, + 'sort_by_direction' => $sortByDirection, + 'limit' => $limit, + 'offset' => $offset + ]; + } + + /** + * Validate the date format + * + * @param string $date YYYY-MM-DD + * + * @throws Exception + */ + protected function validateDateFormat($date) + { + if (false === DateTime::createFromFormat(self::DATE_FORMAT, $date)) { + throw new Exception('Date must be in the YYYY-MM-DD format.'); + } + } + + /** + * Validate options + * + * @param string $name Name of option + * @param string $value Value of option + * @param array $options Array of options + * + * @throws Exception + */ + protected function validateOptions($name, $value, $options) + { + if (!in_array($value, $options)) { + throw new Exception( + $name . ' must be one of: ' . implode(', ', $options) + ); + } + } + + /** + * Validate integer + * + * @param string $name Name as a string + * @param integer $value Value as an integer + * + * @throws Exception + */ + protected function validateInteger($name, $value) + { + if (!is_integer($value)) { + throw new Exception($name . ' must be an integer.'); + } + } + + /** + * Validate a numeric array + * + * @param string $name Name as a string + * @param array $value Value as an array of integers + * + * @throws Exception + */ + protected function validateNumericArray($name, $value) + { + if (!\is_array($value) || empty($value) || !$this->isNumeric($value)) { + throw new Exception($name . ' must be a non-empty numeric array.'); + } + } + + /** + * Determine if the array is numeric + * + * @param array $array Array of values + * + * @return bool + */ + protected function isNumeric(array $array) + { + return \array_keys($array) === range(0, \count($array) - 1); + } +} diff --git a/lib/sendgrid-php/phpcs.xml b/lib/sendgrid-php/phpcs.xml new file mode 100644 index 000000000..f646392e8 --- /dev/null +++ b/lib/sendgrid-php/phpcs.xml @@ -0,0 +1,196 @@ + + + The PSR-2 coding standard. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + 0 + + + 0 + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/sendgrid-php/sendgrid-php.php b/lib/sendgrid-php/sendgrid-php.php new file mode 100644 index 000000000..1ffbc58c9 --- /dev/null +++ b/lib/sendgrid-php/sendgrid-php.php @@ -0,0 +1,28 @@ +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 Oll&$)VFC_ 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 literal 0 HcmV?d00001 diff --git a/lib/sendgrid-php/vendor/autoload.php b/lib/sendgrid-php/vendor/autoload.php new file mode 100644 index 000000000..eb134107f --- /dev/null +++ b/lib/sendgrid-php/vendor/autoload.php @@ -0,0 +1,25 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ + */ +class ClassLoader +{ + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + + // PSR-4 + /** + * @var array> + */ + private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ + private $prefixDirsPsr4 = array(); + /** + * @var list + */ + private $fallbackDirsPsr4 = array(); + + // PSR-0 + /** + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> + */ + private $prefixesPsr0 = array(); + /** + * @var list + */ + private $fallbackDirsPsr0 = array(); + + /** @var bool */ + private $useIncludePath = false; + + /** + * @var array + */ + private $classMap = array(); + + /** @var bool */ + private $classMapAuthoritative = false; + + /** + * @var array + */ + private $missingClasses = array(); + + /** @var string|null */ + private $apcuPrefix; + + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); + } + + return array(); + } + + /** + * @return array> + */ + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + /** + * @return list + */ + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + /** + * @return list + */ + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + /** + * @return array Array of classname => path + */ + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + * + * @return void + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void + */ + public function add($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + $paths = (array) $paths; + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories + * + * @return void + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + * + * @return void + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + * + * @return void + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + * + * @return void + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * APCu prefix to use to cache found/not-found classes, if the extension is enabled. + * + * @param string|null $apcuPrefix + * + * @return void + */ + public function setApcuPrefix($apcuPrefix) + { + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; + } + + /** + * The APCu prefix in use, or null if APCu caching is not enabled. + * + * @return string|null + */ + public function getApcuPrefix() + { + return $this->apcuPrefix; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } + } + + /** + * Unregisters this instance as an autoloader. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return true|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + $includeFile = self::$includeFile; + $includeFile($file); + + return true; + } + + return null; + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { + return false; + } + if (null !== $this->apcuPrefix) { + $file = apcu_fetch($this->apcuPrefix.$class, $hit); + if ($hit) { + return $file; + } + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath . '\\'; + if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); + foreach ($this->prefixDirsPsr4[$search] as $dir) { + if (file_exists($file = $dir . $pathEnd)) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } + + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } +} diff --git a/lib/sendgrid-php/vendor/composer/InstalledVersions.php b/lib/sendgrid-php/vendor/composer/InstalledVersions.php new file mode 100644 index 000000000..51e734a77 --- /dev/null +++ b/lib/sendgrid-php/vendor/composer/InstalledVersions.php @@ -0,0 +1,359 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + $installed[] = self::$installedByVendor[$vendorDir] = $required; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; + } + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array()) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/lib/sendgrid-php/vendor/composer/LICENSE b/lib/sendgrid-php/vendor/composer/LICENSE new file mode 100644 index 000000000..f27399a04 --- /dev/null +++ b/lib/sendgrid-php/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) Nils Adermann, Jordi Boggiano + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/lib/sendgrid-php/vendor/composer/autoload_classmap.php b/lib/sendgrid-php/vendor/composer/autoload_classmap.php new file mode 100644 index 000000000..25a09918a --- /dev/null +++ b/lib/sendgrid-php/vendor/composer/autoload_classmap.php @@ -0,0 +1,13 @@ + $baseDir . '/lib/BaseSendGridClientInterface.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', + 'SendGrid' => $baseDir . '/lib/SendGrid.php', + 'TwilioEmail' => $baseDir . '/lib/TwilioEmail.php', +); diff --git a/lib/sendgrid-php/vendor/composer/autoload_files.php b/lib/sendgrid-php/vendor/composer/autoload_files.php new file mode 100644 index 000000000..17b184b07 --- /dev/null +++ b/lib/sendgrid-php/vendor/composer/autoload_files.php @@ -0,0 +1,10 @@ + $vendorDir . '/starkbank/ecdsa/src/ellipticcurve.php', +); diff --git a/lib/sendgrid-php/vendor/composer/autoload_namespaces.php b/lib/sendgrid-php/vendor/composer/autoload_namespaces.php new file mode 100644 index 000000000..15a2ff3ad --- /dev/null +++ b/lib/sendgrid-php/vendor/composer/autoload_namespaces.php @@ -0,0 +1,9 @@ + array($baseDir . '/lib/stats'), + 'SendGrid\\Mail\\' => array($baseDir . '/lib/mail'), + 'SendGrid\\Helper\\' => array($baseDir . '/lib/helper'), + 'SendGrid\\EventWebhook\\' => array($baseDir . '/lib/eventwebhook'), + 'SendGrid\\Contacts\\' => array($baseDir . '/lib/contacts'), + 'SendGrid\\' => array($vendorDir . '/sendgrid/php-http-client/lib'), +); diff --git a/lib/sendgrid-php/vendor/composer/autoload_real.php b/lib/sendgrid-php/vendor/composer/autoload_real.php new file mode 100644 index 000000000..35106207b --- /dev/null +++ b/lib/sendgrid-php/vendor/composer/autoload_real.php @@ -0,0 +1,50 @@ +register(true); + + $filesToLoad = \Composer\Autoload\ComposerStaticInitb8cdbfd36fe8771e1ec1488c63934b81::$files; + $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; + } + }, null, null); + foreach ($filesToLoad as $fileIdentifier => $file) { + $requireFile($fileIdentifier, $file); + } + + return $loader; + } +} diff --git a/lib/sendgrid-php/vendor/composer/autoload_static.php b/lib/sendgrid-php/vendor/composer/autoload_static.php new file mode 100644 index 000000000..887ae35a4 --- /dev/null +++ b/lib/sendgrid-php/vendor/composer/autoload_static.php @@ -0,0 +1,68 @@ + __DIR__ . '/..' . '/starkbank/ecdsa/src/ellipticcurve.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'S' => + array ( + 'SendGrid\\Stats\\' => 15, + 'SendGrid\\Mail\\' => 14, + 'SendGrid\\Helper\\' => 16, + 'SendGrid\\EventWebhook\\' => 22, + 'SendGrid\\Contacts\\' => 18, + 'SendGrid\\' => 9, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'SendGrid\\Stats\\' => + array ( + 0 => __DIR__ . '/../..' . '/lib/stats', + ), + 'SendGrid\\Mail\\' => + array ( + 0 => __DIR__ . '/../..' . '/lib/mail', + ), + 'SendGrid\\Helper\\' => + array ( + 0 => __DIR__ . '/../..' . '/lib/helper', + ), + 'SendGrid\\EventWebhook\\' => + array ( + 0 => __DIR__ . '/../..' . '/lib/eventwebhook', + ), + 'SendGrid\\Contacts\\' => + array ( + 0 => __DIR__ . '/../..' . '/lib/contacts', + ), + 'SendGrid\\' => + array ( + 0 => __DIR__ . '/..' . '/sendgrid/php-http-client/lib', + ), + ); + + public static $classMap = array ( + 'BaseSendGridClientInterface' => __DIR__ . '/../..' . '/lib/BaseSendGridClientInterface.php', + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + 'SendGrid' => __DIR__ . '/../..' . '/lib/SendGrid.php', + 'TwilioEmail' => __DIR__ . '/../..' . '/lib/TwilioEmail.php', + ); + + public static function getInitializer(ClassLoader $loader) + { + return \Closure::bind(function () use ($loader) { + $loader->prefixLengthsPsr4 = ComposerStaticInitb8cdbfd36fe8771e1ec1488c63934b81::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitb8cdbfd36fe8771e1ec1488c63934b81::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInitb8cdbfd36fe8771e1ec1488c63934b81::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/lib/sendgrid-php/vendor/composer/installed.json b/lib/sendgrid-php/vendor/composer/installed.json new file mode 100644 index 000000000..b108c8462 --- /dev/null +++ b/lib/sendgrid-php/vendor/composer/installed.json @@ -0,0 +1,117 @@ +{ + "packages": [ + { + "name": "sendgrid/php-http-client", + "version": "4.1.1", + "version_normalized": "4.1.1.0", + "source": { + "type": "git", + "url": "https://github.com/sendgrid/php-http-client.git", + "reference": "ec09bcfccabeb21d69e245a1e1c0e51f2813fc35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sendgrid/php-http-client/zipball/ec09bcfccabeb21d69e245a1e1c0e51f2813fc35", + "reference": "ec09bcfccabeb21d69e245a1e1c0e51f2813fc35", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": ">=7.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "~2.0" + }, + "suggest": { + "composer/ca-bundle": "Including this library will ensure that a valid CA bundle is available for secure connections" + }, + "time": "2023-12-14T08:50:59+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "SendGrid\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Bernier", + "email": "mbernier@twilio.com" + }, + { + "name": "Elmer Thomas", + "email": "ethomas@twilio.com" + } + ], + "description": "HTTP REST client, simplified for PHP", + "homepage": "http://github.com/sendgrid/php-http-client", + "keywords": [ + "api", + "fluent", + "http", + "rest", + "sendgrid" + ], + "support": { + "source": "https://github.com/sendgrid/php-http-client/tree/4.1.1" + }, + "install-path": "../sendgrid/php-http-client" + }, + { + "name": "starkbank/ecdsa", + "version": "0.0.5", + "version_normalized": "0.0.5.0", + "source": { + "type": "git", + "url": "https://github.com/starkbank/ecdsa-php.git", + "reference": "484bedac47bac4012dc73df91da221f0a66845cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/starkbank/ecdsa-php/zipball/484bedac47bac4012dc73df91da221f0a66845cb", + "reference": "484bedac47bac4012dc73df91da221f0a66845cb", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "time": "2021-06-06T22:24:49+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "src/ellipticcurve.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "StarkBank", + "email": "developers@starkbank.com", + "homepage": "https://starkbank.com", + "role": "Developer" + } + ], + "description": "fast openSSL-compatible implementation of the Elliptic Curve Digital Signature Algorithm (ECDSA)", + "homepage": "https://github.com/starkbank/ecdsa-php", + "support": { + "issues": "https://github.com/starkbank/ecdsa-php/issues", + "source": "https://github.com/starkbank/ecdsa-php/tree/v0.0.5" + }, + "install-path": "../starkbank/ecdsa" + } + ], + "dev": false, + "dev-package-names": [] +} diff --git a/lib/sendgrid-php/vendor/composer/installed.php b/lib/sendgrid-php/vendor/composer/installed.php new file mode 100644 index 000000000..6034f808b --- /dev/null +++ b/lib/sendgrid-php/vendor/composer/installed.php @@ -0,0 +1,47 @@ + array( + 'name' => 'sendgrid/sendgrid', + 'pretty_version' => '8.1.2', + 'version' => '8.1.2.0', + 'reference' => '6700d2cf50df38915fa2d9a03affbca58c48599f', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => false, + ), + 'versions' => array( + 'sendgrid/php-http-client' => array( + 'pretty_version' => '4.1.1', + 'version' => '4.1.1.0', + 'reference' => 'ec09bcfccabeb21d69e245a1e1c0e51f2813fc35', + 'type' => 'library', + 'install_path' => __DIR__ . '/../sendgrid/php-http-client', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'sendgrid/sendgrid' => array( + 'pretty_version' => '8.1.2', + 'version' => '8.1.2.0', + 'reference' => '6700d2cf50df38915fa2d9a03affbca58c48599f', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'sendgrid/sendgrid-php' => array( + 'dev_requirement' => false, + 'replaced' => array( + 0 => '*', + ), + ), + 'starkbank/ecdsa' => array( + 'pretty_version' => '0.0.5', + 'version' => '0.0.5.0', + 'reference' => '484bedac47bac4012dc73df91da221f0a66845cb', + 'type' => 'library', + 'install_path' => __DIR__ . '/../starkbank/ecdsa', + 'aliases' => array(), + 'dev_requirement' => false, + ), + ), +); diff --git a/lib/sendgrid-php/vendor/composer/platform_check.php b/lib/sendgrid-php/vendor/composer/platform_check.php new file mode 100644 index 000000000..92370c5a0 --- /dev/null +++ b/lib/sendgrid-php/vendor/composer/platform_check.php @@ -0,0 +1,26 @@ += 70300)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/.php_cs.dist b/lib/sendgrid-php/vendor/sendgrid/php-http-client/.php_cs.dist new file mode 100644 index 000000000..c906f2ecd --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/.php_cs.dist @@ -0,0 +1,20 @@ +in(__DIR__ . '/lib'); + +return PhpCsFixer\Config::create() + ->setRiskyAllowed(true) + ->setRules([ + '@PSR2' => true, + '@Symfony' => true, + 'array_syntax' => ['syntax' => 'short'], + 'cast_spaces' => ['space' => 'single'], + 'yoda_style' => false, + 'concat_space' => ['spacing' => 'one'], + 'phpdoc_summary' => false, + 'combine_consecutive_unsets' => true, + 'final_internal_class' => true, + 'global_namespace_import' => ['import_classes' => false], + ]) + ->setFinder($finder); diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/CHANGELOG.md b/lib/sendgrid-php/vendor/sendgrid/php-http-client/CHANGELOG.md new file mode 100644 index 000000000..000471d6d --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/CHANGELOG.md @@ -0,0 +1,279 @@ +# Change Log +All notable changes to this project will be documented in this file. + +This project adheres to [Semantic Versioning](http://semver.org/). + +[2023-12-14] Version 4.1.1 +-------------------------- +**Library - Chore** +- [PR #162](https://github.com/sendgrid/php-http-client/pull/162): added test for setHost. Thanks to [@tiwarishubham635](https://github.com/tiwarishubham635)! + + +[2023-12-01] Version 4.1.0 +-------------------------- +**Library - Feature** +- [PR #161](https://github.com/sendgrid/php-http-client/pull/161): added setHost for client. Thanks to [@tiwarishubham635](https://github.com/tiwarishubham635)! + +**Library - Test** +- [PR #155](https://github.com/sendgrid/php-http-client/pull/155): Adding misc as PR type. Thanks to [@rakatyal](https://github.com/rakatyal)! + +**Library - Docs** +- [PR #154](https://github.com/sendgrid/php-http-client/pull/154): Update docs to align with SendGrid Support. Thanks to [@garethpaul](https://github.com/garethpaul)! + + +[2022-05-04] Version 4.0.0 +-------------------------- +**Note:** This release contains breaking changes, check our [upgrade guide](./UPGRADE.md#2022-05-04-3xx-to-4xx) for detailed migration notes. + +**Library - Chore** +- [PR #153](https://github.com/sendgrid/php-http-client/pull/153): drop support for EOL PHP versions and add support for PHP 8. Thanks to [@childish-sambino](https://github.com/childish-sambino)! **(breaking change)** + + +[2022-03-09] Version 3.14.4 +--------------------------- +**Library - Chore** +- [PR #152](https://github.com/sendgrid/php-http-client/pull/152): push Datadog Release Metric upon deploy success. Thanks to [@eshanholtz](https://github.com/eshanholtz)! + + +[2022-02-09] Version 3.14.3 +--------------------------- +**Library - Chore** +- [PR #151](https://github.com/sendgrid/php-http-client/pull/151): add deploy steps to build library release artifacts. Thanks to [@Hunga1](https://github.com/Hunga1)! +- [PR #150](https://github.com/sendgrid/php-http-client/pull/150): add gh release to workflow. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)! + + +[2022-01-26] Version 3.14.2 +--------------------------- +**Library - Chore** +- [PR #149](https://github.com/sendgrid/php-http-client/pull/149): migrate to Github actions. Thanks to [@JenniferMah](https://github.com/JenniferMah)! + + +[2022-01-12] Version 3.14.1 +--------------------------- +**Library - Chore** +- [PR #148](https://github.com/sendgrid/php-http-client/pull/148): update license year. Thanks to [@JenniferMah](https://github.com/JenniferMah)! + + +[2021-03-24] Version 3.14.0 +--------------------------- +**Library - Feature** +- [PR #136](https://github.com/sendgrid/php-http-client/pull/136): Build URL with multiple instances of the same param. Thanks to [@agh1](https://github.com/agh1)! + + +[2020-11-05] Version 3.13.0 +--------------------------- +**Library - Feature** +- [PR #101](https://github.com/sendgrid/php-http-client/pull/101): Allows for a user to utilize self-signed certificates. Thanks to [@davcpas1234](https://github.com/davcpas1234)! + + +[2020-10-14] Version 3.12.0 +--------------------------- +**Library - Feature** +- [PR #103](https://github.com/sendgrid/php-http-client/pull/103): Throw an InvalidRequest whenever a curl request fails. Thanks to [@colinodell](https://github.com/colinodell)! + + +[2020-08-19] Version 3.11.1 +--------------------------- +**Library - Docs** +- [PR #116](https://github.com/sendgrid/php-http-client/pull/116): Add first-timers.md for newcomers. Thanks to [@daniloff200](https://github.com/daniloff200)! + +**Library - Chore** +- [PR #145](https://github.com/sendgrid/php-http-client/pull/145): update GitHub branch references to use HEAD. Thanks to [@thinkingserious](https://github.com/thinkingserious)! + + +[2020-07-22] Version 3.11.0 +--------------------------- +**Library - Test** +- [PR #120](https://github.com/sendgrid/php-http-client/pull/120): test enhancements. Thanks to [@peter279k](https://github.com/peter279k)! + +**Library - Feature** +- [PR #109](https://github.com/sendgrid/php-http-client/pull/109): automatic code style checking. Thanks to [@misantron](https://github.com/misantron)! + + +[2020-06-24] Version 3.10.8 +--------------------------- +**Library - Fix** +- [PR #143](https://github.com/sendgrid/php-http-client/pull/143): Composer configuration, typos and type hints. Thanks to [@kampalex](https://github.com/kampalex)! + + +[2020-06-10] Version 3.10.7 +--------------------------- +**Library - Fix** +- [PR #144](https://github.com/sendgrid/php-http-client/pull/144): replace Throwable with Exception. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-04-29] Version 3.10.6 +--------------------------- +**Library - Fix** +- [PR #141](https://github.com/sendgrid/php-http-client/pull/141): add the singular 'suppression' method. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-03-18] Version 3.10.5 +--------------------------- +**Library - Docs** +- [PR #111](https://github.com/sendgrid/php-http-client/pull/111): run .md files through grammarly. Thanks to [@redbrickone](https://github.com/redbrickone)! + + +[2020-03-04] Version 3.10.4 +--------------------------- +**Library - Chore** +- [PR #140](https://github.com/sendgrid/php-http-client/pull/140): add PHP 7.4 to Travis and test with lowest dependencies. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-02-19] Version 3.10.3 +--------------------------- +**Library - Fix** +- [PR #134](https://github.com/sendgrid/php-http-client/pull/134): Change contactsdb to marketing api #133. Thanks to [@murich](https://github.com/murich)! + + +[2020-01-22] Version 3.10.2 +--------------------------- +**Library - Docs** +- [PR #139](https://github.com/sendgrid/php-http-client/pull/139): baseline all the templated markdown docs. Thanks to [@childish-sambino](https://github.com/childish-sambino)! + + +[2020-01-09] Version 3.10.1 +--------------------------- +**Library - Chore** +- [PR #138](https://github.com/sendgrid/php-http-client/pull/138): prep the repo for automated releasing. Thanks to [@childish-sambino](https://github.com/childish-sambino)! +- [PR #135](https://github.com/sendgrid/php-http-client/pull/135): add more PHP versions to .travis.yml. Thanks to [@PaiizZ](https://github.com/PaiizZ)! + +**Library - Docs** +- [PR #122](https://github.com/sendgrid/php-http-client/pull/122): fix grammar in Readme. Thanks to [@jmauerhan](https://github.com/jmauerhan)! + + +[2019-12-11] Version 3.10.0 +--------------------------- + +**Library - Fix** +- [PR #99](https://github.com/sendgrid/php-http-client/pull/99): Throw InvalidRequest exception on invalid CURL request. Thanks to [@alextech](https://github.com/alextech)! + +**Library - Docs** +- [PR #102](https://github.com/sendgrid/php-http-client/pull/102): Create a Use Cases Directory. Thanks to [@ProZsolt](https://github.com/ProZsolt)! +- [PR #106](https://github.com/sendgrid/php-http-client/pull/106): Only mention the lowest required PHP version in README. Thanks to [@svenluijten](https://github.com/svenluijten)! + +## [3.9.6] - 2018-04-10 +### Added +- PR [#98](https://github.com/sendgrid/php-http-client/pull/98). Updated documention of `Client.php` using PHPDoc. +- Thanks to [Martijn Melchers](https://github.com/martijnmelchers) for the pull request! + +## [3.9.5] - 2018-03-26 +### Added +- Fixes [#94](https://github.com/sendgrid/php-http-client/issues/94), PR [#95](https://github.com/sendgrid/php-http-client/pull/95): CreateCurlOptions method regression tests +- Thanks to [Alexandr Ivanov](https://github.com/misantron) for the pull request! + +## [3.9.4] - 2018-03-22 +### Fixed +- Fixes [#586](https://github.com/sendgrid/sendgrid-php/issues/586), PR [#96](https://github.com/sendgrid/php-http-client/pull/96): Fix constructor function signature regression. + +## [3.9.3] - 2018-03-11 +### Fixed +- Fixes [#584](https://github.com/sendgrid/sendgrid-php/issues/584), PR [#93](https://github.com/sendgrid/php-http-client/pull/93): Don't overwrite headers set from upstream dependencies. + +## [3.9.2] - 2018-03-10 +### Fixed +- Fixes [#12](https://github.com/sendgrid/php-http-client/issues/12), PR [#91](https://github.com/sendgrid/php-http-client/pull/91): Curl Options broken as array merge does not preserve keys. + +## [3.9.1] - 2018-03-09 +### Fixed +- Fixes [#88](https://github.com/sendgrid/php-http-client/issues/88), PR [#89](https://github.com/sendgrid/php-http-client/pull/89): Restore missing function 'prepareResponse' due to bad previous merge. + +## [3.9.0] - 2018-03-08 +### Added +- PR [#24](https://github.com/sendgrid/php-http-client/pull/24): implements sending concurrent requests with curl multi, thanks to [Tuan Nguyen](https://github.com/lightbringer1991) for the PR! +- PR [#25](https://github.com/sendgrid/php-http-client/pull/25): Add description how install php-http-client manually, thanks to [Ivan](https://github.com/janczer) for the PR! +- PR [#28](https://github.com/sendgrid/php-http-client/pull/28): Create code of conduct, thanks to [Alexander Androsyuk](https://github.com/alex2sat) for the PR! +- Closes [#32](https://github.com/sendgrid/php-http-client/issues/32), PR [#33](https://github.com/sendgrid/php-http-client/pull/33): Added TROUBLESHOOTING + Debug Info, thanks to [Braunson Yager](https://github.com/Braunson) for the PR! +- Closes [#35](https://github.com/sendgrid/php-http-client/issues/35), PR [#37](https://github.com/sendgrid/php-http-client/pull/37): Update README badges, thanks to [Tim Harshman](https://github.com/goteamtim) for the PR! +- Closes [#34](https://github.com/sendgrid/php-http-client/issues/34), PR [#38](https://github.com/sendgrid/php-http-client/pull/38): Update .md files with hyphen vs underscore for page links to enhance SEO, thanks to [Eric Kates](https://github.com/positronek) for the PR! +- Closes [#39](https://github.com/sendgrid/php-http-client/issues/39), PR [#40](https://github.com/sendgrid/php-http-client/pull/40): Added Packagist badge to README, thanks to [Rakshan Shetty](https://github.com/rakshans1) for the PR! +- PR [#41](https://github.com/sendgrid/php-http-client/pull/41): Add table of contents in README.md, thanks to [thepriefy](https://github.com/thepriefy) for the PR! +- PR [#42](https://github.com/sendgrid/php-http-client/pull/42): Add SendGrid logo at the top of README.md, thanks to [thepriefy](https://github.com/thepriefy) for the PR! +- PR [#43](https://github.com/sendgrid/php-http-client/pull/43): Add License section to the README.md, thanks to [thepriefy](https://github.com/thepriefy) for the PR! +- Closes [#46](https://github.com/sendgrid/php-http-client/issues/46), PR [#47](https://github.com/sendgrid/php-http-client/pull/47): Create Pull Request Template, thanks to [Paweł Lewtak](https://github.com/pawel-lewtak) for the PR! +- Closes [#48](https://github.com/sendgrid/php-http-client/issues/48), PR [#51](https://github.com/sendgrid/php-http-client/pull/51): Added example file, updated .gitignore and README, thanks to [Diego Rocha](https://github.com/dhsrocha) for the PR! +- PR [#53](https://github.com/sendgrid/php-http-client/pull/53): README and usage example update, thanks to [Alexandr Ivanov](https://github.com/misantron) for the PR! +- PR [#55](https://github.com/sendgrid/php-http-client/pull/55): Code climate config, thanks to [Alexandr Ivanov](https://github.com/misantron) for the PR! +- Closes [#58](https://github.com/sendgrid/php-http-client/pull/58), PR [#60](https://github.com/sendgrid/php-http-client/pull/60): Adds unit test for checking file existence in repo, thanks to [Michele Orselli](https://github.com/micheleorselli) for the PR! +- Closes [#57](https://github.com/sendgrid/php-http-client/pull/57), PR [#61](https://github.com/sendgrid/php-http-client/pull/61): Adds unit test for checking year on licence, thanks to [Michele Orselli](https://github.com/micheleorselli) for the PR! +- Closes [#50](https://github.com/sendgrid/php-http-client/issues/50), PR [#62](https://github.com/sendgrid/php-http-client/pull/62): Create USAGE.md, thanks to [Nitanshu](https://github.com/nvzard) for the PR! +- Closes [#57](https://github.com/sendgrid/php-http-client/issues/57), PR [#66](https://github.com/sendgrid/php-http-client/pull/66): Add unit test for license year, thanks to [Alex](https://github.com/pushkyn) for the PR! +- Closes [#67](https://github.com/sendgrid/php-http-client/issues/67), PR [#68](https://github.com/sendgrid/php-http-client/pull/68): Add CodeCov support to .travis.yml, thanks to [Manjiri Tapaswi](https://github.com/mptap) for the PR! +- Closes [#49](https://github.com/sendgrid/php-http-client/issues/49), PR [#73](https://github.com/sendgrid/php-http-client/pull/73): Create Dockerfile, thanks to [Jessica Mauerhan](https://github.com/jmauerhan) for the PR! +- Closes [#76](https://github.com/sendgrid/php-http-client/issues/76), PR [#78](https://github.com/sendgrid/php-http-client/pull/78): Added Pull Requests Review section to CONTRIBUTE.md Closes, thanks to [Povilas Balzaravičius](https://github.com/Pawka) for the PR! +- Closes [#63](https://github.com/sendgrid/php-http-client/issues/63), PR [#79](https://github.com/sendgrid/php-http-client/pull/79): Refactor makeRequest method, thanks to [Michael Dennis](https://github.com/michaeljdennis) for the PR! +- PR [#82](https://github.com/sendgrid/php-http-client/pull/82): Add JsonSerializable type to phpDoc of $body parameter in makeRequest method, thanks to [Jan Konáš](https://github.com/jankonas) for the PR! +- PR [#85](https://github.com/sendgrid/php-http-client/pull/85): Updated the PHP Version details, thanks to [Siddhant Sharma](https://github.com/ssiddhantsharma) for the PR! +- PR [#86](https://github.com/sendgrid/php-http-client/pull/86): Add phpdoc for send method, thanks to [Vitaliy Ryaboy](https://github.com/rvitaliy) for the PR! +- PR [#84](https://github.com/sendgrid/php-http-client/pull/84): Update Docker instructions + + +### Fixed +- PR [#26](https://github.com/sendgrid/php-http-client/pull/26): README typo, thanks to [Cícero Pablo](https://github.com/ciceropablo) for the PR! +- PR [#30](https://github.com/sendgrid/php-http-client/pull/30), Fixes [#18](https://github.com/sendgrid/php-http-client/issues/18): Disable CURLOPT_FAILONERROR, thanks to [Zsolt Prontvai](https://github.com/ProZsolt) for the PR! +- PR [#44](https://github.com/sendgrid/php-http-client/pull/44): Fix Typo and add missing links to README, thanks to [Alex](https://github.com/pushkyn) for the PR! +- PR [#52](https://github.com/sendgrid/php-http-client/pull/52): Fix syntax errors in README examples, thanks to [Michael Spiss](https://github.com/michaelspiss) for the PR! +- Fixes [#56](https://github.com/sendgrid/php-http-client/pull/56), PR [#59](https://github.com/sendgrid/php-http-client/pull/59): Update LICENSE - fix year, thanks to [Alex](https://github.com/pushkyn) for the PR! +- PR [#69](https://github.com/sendgrid/php-http-client/pull/69): Remove extra parenthesis from README, thanks to [Jessica Mauerhan](https://github.com/jmauerhan) for the PR! +- PR [#71](https://github.com/sendgrid/php-http-client/pull/71): Fix typo in CONTRIBUTING.md, thanks to [thepriefy](https://github.com/thepriefy) for the PR! +- Fixes [#81](https://github.com/sendgrid/php-http-client/issues/81), PR [#87](https://github.com/sendgrid/php-http-client/pull/87): Stop using insecure option by default + +## [3.8.0] - 2017-09-13 +### Added +- Pull request #23: [Automatically retry when rate limit is reached](https://github.com/sendgrid/php-http-client/pull/23) +- Thanks to [Budi Chandra](https://github.com/budirec) for the pull request! + +## [3.7.0] - 2017-05-04 +### Added +- Pull request #19: [Added ability to get headers as associative array](https://github.com/sendgrid/php-http-client/pull/19) +- Solves issue #361: [https://github.com/sendgrid/sendgrid-php/issues/361](https://github.com/sendgrid/sendgrid-php/issues/361) +- Thanks to [Alexander](https://github.com/mazanax) for the pull request! + +## [3.6.0] - 2017-03-01 +### Added +- Pull request #16: [Pass the curlOptions to the client in buildClient](https://github.com/sendgrid/php-http-client/pull/16) +- Thanks to [Baptiste Clavié](https://github.com/Taluu) for the pull request! + +## [3.5.1] - 2016-11-17 +### Fixed +- Pull request #13, fixed issue #12: [Change from to php union operator to combine curl options](https://github.com/sendgrid/php-http-client/pull/13) +- Thanks to [emil](https://github.com/emilva) for the pull request! + +## [3.5.0] - 2016-10-18 +### Added +- Pull request #11: [Added curlOptions property to customize curl instance](https://github.com/sendgrid/php-http-client/pull/11) +- Thanks to [Alain Tiemblo](https://github.com/ninsuo) for the pull request! + +## [3.4.0] - 2016-09-27 +### Added +- Pull request #9: [Add getters for certain properties](https://github.com/sendgrid/php-http-client/pull/9) +- Thanks to [Arjan Keeman](https://github.com/akeeman) for the pull request! + +## [3.3.0] - 2016-09-13 +### Added +- Pull request #6: [Library refactoring around PSR-2 / PSR-4 code standards](https://github.com/sendgrid/php-http-client/pull/6) +- Thanks to [Alexandr Ivanov](https://github.com/misantron) for the pull request! + +## [3.1.0] - 2016-06-10 +### Added +- Automatically add Content-Type: application/json when there is a request body + +## [3.0.0] - 2016-06-06 +### Changed +- Made the Request and Response variables non-redundant. e.g. request.requestBody becomes request.body + +## [2.0.2] - 2016-02-29 +### Fixed +- Renaming files to conform to PSR-0, git ignored the case in 2.0.1 + +## [2.0.1] - 2016-02-29 +### Fixed +- Renaming files to conform to PSR-0 + +## [1.0.1] - 2016-02-29 +### Fixed +- Composer/Packagist install issues resolved + +## [1.0.0] - 2016-02-29 +### Added +- We are live! diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/CODE_OF_CONDUCT.md b/lib/sendgrid-php/vendor/sendgrid/php-http-client/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..2f0727ed5 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/CODE_OF_CONDUCT.md @@ -0,0 +1,73 @@ +# 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 open-source@twilio.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 diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/CONTRIBUTING.md b/lib/sendgrid-php/vendor/sendgrid/php-http-client/CONTRIBUTING.md new file mode 100644 index 000000000..b183415e4 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/CONTRIBUTING.md @@ -0,0 +1,152 @@ +Hello! Thank you for choosing to help contribute to one of the SendGrid open source projects. There are many ways you can contribute and help is always welcome. We merely ask that you follow the following contribution policies. + +- [Improvements to the Codebase](#improvements-to-the-codebase) +- [Understanding the Code Base](#understanding-the-codebase) +- [Testing](#testing) +- [Style Guidelines & Naming Conventions](#style-guidelines-and-naming-conventions) +- [Creating a Pull Request](#creating-a-pull-request) +- [Code Reviews](#code-reviews) + + +## Improvements to the Codebase + +We welcome direct contributions to the php-http-client code base. Thank you! + +### Development Environment ### + +#### Install and Run Locally #### + +##### Prerequisites ##### + +- PHP version 7.3, 7.4, 8.0, or 8.1 +- [Composer](https://getcomposer.org/) + +##### Initial setup: ##### + +```bash +git clone https://github.com/sendgrid/php-http-client.git +cd php-http-client +``` + +### Environment Variables + +First, get your free SendGrid account [here](https://sendgrid.com/free?source=php-http-client). + +Next, update your environment with your [SENDGRID_API_KEY](https://app.sendgrid.com/settings/api_keys). + +```bash +echo "export SENDGRID_API_KEY='YOUR_API_KEY'" > sendgrid.env +echo "sendgrid.env" >> .gitignore +source ./sendgrid.env +``` + +##### Execute: ##### + +See the [examples folder](examples). + + +## Understanding the Code Base + +**/examples** + +Working examples that demonstrate usage. + +**/test/unit** + +Unit tests. + +**/lib/SendGrid/Client.php** + +An HTTP client with a fluent interface using method chaining and reflection. By returning `$this` on [__call](lib/Client.php#L576), we can dynamically build the URL using method chaining and [__call](lib/Client.php#L576) allows us to dynamically receive the method calls to achieve reflection. + +This allows for the following mapping from a URL to a method chain: + +`/api_client/{api_key_id}/version` maps to `client->api_client().->_($api_key_id)->version->()` where is a [HTTP verb](lib/Client.php#L210). + +**/lib/SendGrid/Config.php** + +Loads the environment variables. + + +## Testing + +All PRs require passing tests before the PR will be reviewed. + +All test files are in the [`/test/unit`](test/unit) directory. + +For the purposes of contributing to this repo, please update the [`ClientTest.php`](test/unit/ClientTest.php) file with unit tests as you modify the code. + +```bash +composer install +cd test/unit +../../vendor/bin/phpunit . --bootstrap bootstrap.php --filter test* +``` + + +## Style Guidelines & Naming Conventions + +Generally, we follow the style guidelines as suggested by the official language. However, we ask that you conform to the styles that already exist in the library. If you wish to deviate, please explain your reasoning. + +- [pear coding standards](https://pear.php.net/manual/en/standards.php) + +Please run your code through: + +- [PHP Code Sniffer](https://github.com/squizlabs/PHP_CodeSniffer) + + +## Creating a Pull Request + +1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, + and configure the remotes: + + ```bash + # Clone your fork of the repo into the current directory + git clone https://github.com/sendgrid/php-http-client + # Navigate to the newly cloned directory + cd php-http-client + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com/sendgrid/php-http-client + ``` + +2. If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout + git pull upstream + ``` + +3. Create a new topic branch (off the main project development branch) to + contain your feature, change, or fix: + + ```bash + git checkout -b + ``` + +4. Commit your changes in logical chunks. Please adhere to these [git commit + message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) + or your code is unlikely be merged into the main project. Use Git's + [interactive rebase](https://help.github.com/articles/interactive-rebase) + feature to tidy up your commits before making them public. + +4a. Create tests. + +4b. Create or update the example code that demonstrates the functionality of this change to the code. + +5. Locally merge (or rebase) the upstream development branch into your topic branch: + + ```bash + git pull [--rebase] upstream main + ``` + +6. Push your topic branch up to your fork: + + ```bash + git push origin + ``` + +7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) + with a clear title and description against the `main` branch. All tests must be passing before we will review the PR. + + +## Code Reviews +If you can, please look at open PRs and review them. Give feedback and help us merge these PRs much faster! If you don't know how, Github has some great [information on how to review a Pull Request](https://help.github.com/articles/about-pull-request-reviews/). diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/Dockerfile b/lib/sendgrid-php/vendor/sendgrid/php-http-client/Dockerfile new file mode 100644 index 000000000..2261ad9bf --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/Dockerfile @@ -0,0 +1,14 @@ +FROM php:8.1 + +ARG sendgrid_apikey +ENV SENDGRID_API_KEY=$sendgrid_apikey + +COPY . /var/www/client +WORKDIR /var/www/client + +RUN apt-get update && \ + apt-get install -y git libzip-dev && \ + docker-php-ext-install zip +RUN curl --silent --show-error https://getcomposer.org/installer | php + +RUN php composer.phar install diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/FIRST_TIMERS.md b/lib/sendgrid-php/vendor/sendgrid/php-http-client/FIRST_TIMERS.md new file mode 100644 index 000000000..28e914d1e --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/FIRST_TIMERS.md @@ -0,0 +1,53 @@ +# How To Contribute to Twilio SendGrid Repositories via GitHub +Contributing to the Twilio SendGrid repositories is easy! Once you have submitted your pull request, the team can easily review it before it is merged into the repository. + +To make a pull request, follow these steps: + +1. Log into GitHub. If you do not already have a GitHub account, you will have to create one in order to submit a change. Click the Sign up link in the upper right-hand corner to create an account. Enter your username, password, and email address. If you are an employee of Twilio SendGrid, please use your full name with your GitHub account and enter Twilio SendGrid as your company so we can easily identify you. + + + +2. __[Fork](https://help.github.com/fork-a-repo/)__ the [php-http-client](https://github.com/sendgrid/php-http-client) repository: + + + +3. __Clone__ your fork via the following commands: + +```bash +# Clone your fork of the repo into the current directory +git clone https://github.com/your_username/php-http-client +# Navigate to the newly cloned directory +cd php-http-client +# Assign the original repo to a remote called "upstream" +git remote add upstream https://github.com/sendgrid/php-http-client +``` + +> Don't forget to replace *your_username* in the URL by your real GitHub username. + +4. __Create a new topic branch__ (off the main project development branch) to contain your feature, change, or fix: + +```bash +git checkout -b +``` + +5. __Commit your changes__ in logical chunks. + +Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) or your code is unlikely be merged into the main project. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public. Probably you will also have to create tests (if needed) or create or update the example code that demonstrates the functionality of this change to the code. + +6. __Locally merge (or rebase)__ the upstream development branch into your topic branch: + +```bash +git pull [--rebase] upstream main +``` + +7. __Push__ your topic branch up to your fork: + +```bash +git push origin +``` + +8. __[Open a Pull Request](https://help.github.com/articles/creating-a-pull-request/#changing-the-branch-range-and-destination-repository/)__ with a clear title and description against the `main` branch. All tests must be passing before we will review the PR. + +## Important notice + +Before creating a pull request, make sure that you respect the repository's constraints regarding contributions. You can find them in the [CONTRIBUTING.md](CONTRIBUTING.md) file. diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/LICENSE b/lib/sendgrid-php/vendor/sendgrid/php-http-client/LICENSE new file mode 100644 index 000000000..3154774a9 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (C) 2023, Twilio SendGrid, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/Makefile b/lib/sendgrid-php/vendor/sendgrid/php-http-client/Makefile new file mode 100644 index 000000000..db0764107 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/Makefile @@ -0,0 +1,16 @@ +.PHONY: clean install test + +clean: + @rm -rf vendor composer.lock php-http-client.zip + +ci-install: clean + composer install --no-dev + +install: clean + composer install --no-scripts --no-progress --no-interaction + +test: install + vendor/bin/phpunit test/unit + +bundle: ci-install + zip -r php-http-client.zip . -x \*.git\* \*composer.json\* \*test\* diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/PULL_REQUEST_TEMPLATE.md b/lib/sendgrid-php/vendor/sendgrid/php-http-client/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..0ebe9b723 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,31 @@ + + +# Fixes # + +A short description of what this PR does. + +### Checklist +- [x] I acknowledge that all my contributions will be made under the project's license +- [ ] I have made a material change to the repo (functionality, testing, spelling, grammar) +- [ ] I have read the [Contribution Guidelines](https://github.com/sendgrid/php-http-client/blob/main/CONTRIBUTING.md) and my PR follows them +- [ ] I have titled the PR appropriately +- [ ] I have updated my branch with the main branch +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] I have added the necessary documentation about the functionality in the appropriate .md file +- [ ] I have added inline documentation to the code I modified + +If you have questions, please file a [support ticket](https://support.sendgrid.com). diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/README.md b/lib/sendgrid-php/vendor/sendgrid/php-http-client/README.md new file mode 100644 index 000000000..7aea5d1b1 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/README.md @@ -0,0 +1,193 @@ +![SendGrid Logo](twilio_sendgrid_logo.png) + +[![Tests](https://github.com/sendgrid/php-http-client/actions/workflows/test-and-deploy.yml/badge.svg)](https://github.com/sendgrid/php-http-client/actions/workflows/test-and-deploy.yml) +[![Latest Version on Packagist](https://img.shields.io/packagist/v/sendgrid/php-http-client.svg?style=flat-square)](https://packagist.org/packages/sendgrid/php-http-client) +[![Twitter Follow](https://img.shields.io/twitter/follow/sendgrid.svg?style=social&label=Follow)](https://twitter.com/sendgrid) +[![GitHub contributors](https://img.shields.io/github/contributors/sendgrid/php-http-client.svg)](https://github.com/sendgrid/php-http-client/graphs/contributors) +[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) + +**Quickly and easily access any RESTful or RESTful-like API.** + +If you are looking for the SendGrid API client library, please see [this repo](https://github.com/sendgrid/sendgrid-php). + +# Announcements +All updates to this library are documented in our [CHANGELOG](CHANGELOG.md). + +# Table of Contents +- [Installation](#installation) +- [Quick Start](#quick-start) +- [Usage](#usage) +- [How to Contribute](#contribute) +- [Thanks](#thanks) +- [About](#about) +- [Support](#support) +- [License](#license) + + +# Installation + +## Prerequisites + +- PHP version 7.3, 7.4, 8.0, or 8.1 + +## Install with Composer + +Add php-http-client to your `composer.json` file. If you are not using [Composer](http://getcomposer.org), you should be. It's an excellent way to manage dependencies in your PHP application. + +```json +{ + "require": { + "sendgrid/php-http-client": "^4.1.1" + } +} +``` + +Then at the top of your PHP script require the autoloader: + +```php +require __DIR__ . '/vendor/autoload.php'; +``` + +Then from the command line: + +```bash +composer install +``` + +## Install without Composer + +You should create a `lib` directory in the directory of your application and clone to `lib` repositories [php-http-client](https://github.com/sendgrid/php-http-client.git) and [sendgrid-php](https://github.com/sendgrid/sendgrid-php.git): + +``` +$ cd /path/to/your/app +$ mkdir lib +$ cd lib +$ git clone https://github.com/sendgrid/php-http-client.git +``` + +In the next step you should create `loader.php`: + +``` +$ cd /path/to/your/app +$ touch loader.php +``` + +And add the code below to the `loader.php`: + +```php + +# Quick Start + +Here is a quick example: + +`GET /your/api/{param}/call` + +```php +// include __DIR__ . '/loader.php'; +require 'vendor/autoload.php'; +$apiKey = YOUR_SENDGRID_API_KEY; +$authHeaders = [ + 'Authorization: Bearer ' . $apiKey +]; +$client = new SendGrid\Client('https://api.sendgrid.com', $authHeaders); +$param = 'foo'; +$response = $client->your()->api()->_($param)->call()->get(); + +var_dump( + $response->statusCode(), + $response->headers(), + $response->body() +); +``` + +`POST /your/api/{param}/call` with headers, query parameters and a request body with versioning. + +```php +// include __DIR__ . '/loader.php'; +require 'vendor/autoload.php'; +$apiKey = YOUR_SENDGRID_API_KEY; +$authHeaders = [ + 'Authorization: Bearer ' . $apiKey +]; +$client = new SendGrid\Client('https://api.sendgrid.com', $authHeaders); +$queryParams = [ + 'hello' => 0, 'world' => 1 +]; +$requestHeaders = [ + 'X-Test' => 'test' +]; +$data = [ + 'some' => 1, 'awesome' => 2, 'data' => 3 +]; +$param = 'bar'; +$response = $client->your()->api()->_($param)->call()->post($data, $queryParams, $requestHeaders); + +var_dump( + $response->statusCode(), + $response->headers(), + $response->body() +); +``` + +If there is an issues with the request, such as misconfigured CURL SSL options, an `InvalidRequest` will be thrown +with message from CURL on why the request failed. Use the message as a hit to troubleshooting steps of your environment. + + +# Usage + +- [Usage Examples](USAGE.md) + +## Environment Variables + +You can do the following to create a .env file: + +```cp .env_example .env``` + +Then, just add your API Key into your .env file. + + +# How to Contribute + +We encourage contribution to our libraries, please see our [CONTRIBUTING](CONTRIBUTING.md) guide for details. + +Quick links: + +- [Feature Request](CONTRIBUTING.md#feature-request) +- [Bug Reports](CONTRIBUTING.md#submit-a-bug-report) +- [Improvements to the Codebase](CONTRIBUTING.md#improvements-to-the-codebase) +- [Review Pull Requests](CONTRIBUTING.md#code-reviews) + + +# Thanks + +We were inspired by the work done on [birdy](https://github.com/inueni/birdy) and [universalclient](https://github.com/dgreisen/universalclient). + + +# About + +php-http-client is maintained and funded by Twilio SendGrid, Inc. The names and logos for php-http-client are trademarks of Twilio SendGrid, Inc. + + +# Support + +If you need help using SendGrid, please check the [Twilio SendGrid Support Help Center](https://support.sendgrid.com). + + +# License +[The MIT License (MIT)](LICENSE) diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/TROUBLESHOOTING.md b/lib/sendgrid-php/vendor/sendgrid/php-http-client/TROUBLESHOOTING.md new file mode 100644 index 000000000..52d26b338 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/TROUBLESHOOTING.md @@ -0,0 +1,22 @@ +## Table of Contents + +* [Viewing the Request Body](#request-body) +* [Handling SSL Errors](#ssl-errors) + + +## Viewing the Request Body + +When debugging or testing, it may be useful to examine the raw request body. In the `examples/example.php` file, after your API call, use this code to echo out the status code, body, and headers: + +```php +echo $response->statusCode(); +echo $response->body(); +echo $response->headers(); +``` + + +## Handling SSL Errors + +If any SSL errors occur during API calls, an `InvalidRequest` will be thrown. This will provide information to help debug the issue further. + +If the issue is caused by an unrecognized certificate, it may be possible that PHP is unable to locate your system's CA bundle. An easy fix would be requiring the `composer/ca-bundle` package - this library will automatically detect and use that to locate the CA bundle, or use Mozilla's as a fallback. diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/UPGRADE.md b/lib/sendgrid-php/vendor/sendgrid/php-http-client/UPGRADE.md new file mode 100644 index 000000000..eb3b94452 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/UPGRADE.md @@ -0,0 +1,8 @@ +# Upgrade Guide + +_MAJOR version bumps will have upgrade notes posted here._ + +[2022-05-04] 3.x.x to 4.x.x +--------------------------- + +### CHANGED - Drop support for PHP versions 5.6, 7.0, 7.1, and 7.2 which are EOL. diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/USAGE.md b/lib/sendgrid-php/vendor/sendgrid/php-http-client/USAGE.md new file mode 100644 index 000000000..d979639d4 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/USAGE.md @@ -0,0 +1,126 @@ +# Usage + +Usage examples for SendGrid php-http-client + +## Initialization + +``` +// If running this outside of this context, use the following include and +// comment out the two includes below +// require __DIR__ . '/vendor/autoload.php'; +include(dirname(__DIR__) . '/lib/Client.php'); +// This gets the parent directory, for your current directory use getcwd() +$path_to_config = dirname(__DIR__); +$apiKey = getenv('SENDGRID_API_KEY'); +$headers = ['Authorization: Bearer ' . $apiKey]; +$client = new SendGrid\Client('https://api.sendgrid.com', $headers, '/v3'); +``` + +## Table of Contents + +- [GET](#get) +- [DELETE](#delete) +- [POST](#post) +- [PUT](#put) +- [PATCH](#patch) + + +## GET + +#### GET Collection + +``` +$query_params = ['limit' => 100, 'offset' => 0]; +$request_headers = ['X-Mock: 200']; +$response = $client->api_keys()->get(null, $query_params, $request_headers); +echo $response->statusCode(); +echo $response->body(); +echo $response->headers(); +``` + +#### GET with auto retry on rate limit + +``` +$query_params = ['limit' => 100, 'offset' => 0]; +$request_headers = ['X-Mock: 200']; +$retryOnLimit = true; +$response = $client->api_keys()->get(null, $query_params, $request_headers, $retryOnLimit); +echo $response->statusCode(); +echo $response->body(); +echo $response->headers(); +``` + +#### GET with array of values + +``` +$query_params = [ + 'aggregated_by' => 'month', + 'subusers' => ['one', 'two', 'three'], + 'start_date' => '2019-01-01', + 'end_date' => '2019-01-31', +]; +$request_headers = ['X-Mock: 200']; +$retryOnLimit = true; +$response = $client->subusers()->stats()->get(null, $query_params, $request_headers, $retryOnLimit); +echo $response->statusCode(); +echo $response->body(); +echo $response->headers(); +``` + + +## DELETE + +``` +$response = $client->api_keys()->_($api_key_id)->delete(); +echo $response->statusCode(); +echo $response->body(); +echo $response->headers(); +``` + + +## POST + +``` +$request_body = [ + 'name' => 'My PHP API Key', + 'scopes' => [ + 'mail.send', + 'alerts.create', + 'alerts.read' + ] +]; +$response = $client->api_keys()->post($request_body); +echo $response->statusCode(); +echo $response->body(); +echo $response->headers(); +$response_body = json_decode($response->body()); +$api_key_id = $response_body->api_key_id; +``` + +## PUT + +``` +$request_body = [ + 'name' => 'A New Hope', + 'scopes' => [ + 'user.profile.read', + 'user.profile.update' + ] +]; +$response = $client->api_keys()->_($api_key_id)->put($request_body); +echo $response->statusCode(); +echo $response->body(); +echo $response->headers(); +``` + +## PATCH + +``` +$request_body = [ + 'name' => 'A New Hope' +]; +$response = $client->api_keys()->_($api_key_id)->patch($request_body); +echo $response->statusCode(); +echo $response->body(); +echo $response->headers(); +``` diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/examples/.env_sample b/lib/sendgrid-php/vendor/sendgrid/php-http-client/examples/.env_sample new file mode 100644 index 000000000..30857f428 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/examples/.env_sample @@ -0,0 +1 @@ +export SENDGRID_API_KEY='' \ No newline at end of file diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/examples/example.php b/lib/sendgrid-php/vendor/sendgrid/php-http-client/examples/example.php new file mode 100644 index 000000000..107d95a0d --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/examples/example.php @@ -0,0 +1,61 @@ + 100, 'offset' => 0]; +$requestHeaders = ['X-Mock: 200']; +$response = $client->api_keys()->get(null, $queryParams, $requestHeaders); +echo $response->statusCode(); +echo $response->body(); +echo $response->headers(); + +// GET /v3/api_keys - retrieve all API Keys that belong to the user +$queryParams = ['limit' => 100, 'offset' => 0]; +$requestHeaders = ['X-Mock: 200']; +$retryOnLimit = true; // with auto retry on rate limit +$response = $client->api_keys()->get(null, $queryParams, $requestHeaders, $retryOnLimit); + +// POST /v3/api_keys - create a new user API Key +$requestBody = [ + 'name' => 'My PHP API Key', + 'scopes' => [ + 'mail.send', + 'alerts.create', + 'alerts.read' + ] +]; +$response = $client->api_keys()->post($requestBody); +$responseBody = json_decode($response->body(), true); +$apiKeyId = $responseBody['api_key_id']; + +// GET /v3/api_keys/{api_key_id} - retrieve a single API Key +$response = $client->api_keys()->_($apiKeyId)->get(); + +// PATCH /v3/api_keys/{api_key_id} - update the name of an existing API Key +$requestBody = [ + 'name' => 'A New Hope' +]; +$response = $client->api_keys()->_($apiKeyId)->patch($requestBody); + +// PUT /v3/api_keys/{api_key_id} - update the name and scopes of a given API Key +$requestBody = [ + 'name' => 'A New Hope', + 'scopes' => [ + 'user.profile.read', + 'user.profile.update' + ] +]; +$response = $client->api_keys()->_($apiKeyId)->put($requestBody); + +// DELETE /v3/api_keys/{api_key_id} - revoke an existing API Key +$response = $client->api_keys()->_($apiKeyId)->delete(); diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/lib/Client.php b/lib/sendgrid-php/vendor/sendgrid/php-http-client/lib/Client.php new file mode 100644 index 000000000..b6bcd4a79 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/lib/Client.php @@ -0,0 +1,667 @@ +host = $host; + $this->headers = $headers ?: []; + $this->version = $version; + $this->path = $path ?: []; + $this->curlOptions = $curlOptions ?: []; + $this->retryOnLimit = $retryOnLimit; + $this->verifySSLCerts = $verifySSLCerts; + $this->isConcurrentRequest = false; + $this->savedRequests = []; + } + + /** + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set host + * + * @param string $host + * + * @return Client + */ + public function setHost(string $host) + { + $this->host = $host; + + return $this; + } + + /** + * @return array + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * @return string|null + */ + public function getVersion() + { + return $this->version; + } + + /** + * @return array + */ + public function getPath() + { + return $this->path; + } + + /** + * @return array + */ + public function getCurlOptions() + { + return $this->curlOptions; + } + + /** + * Set extra options to set during curl initialization. + * + * @param array $options + * + * @return Client + */ + public function setCurlOptions(array $options) + { + $this->curlOptions = $options; + + return $this; + } + + /** + * Set default retry on limit flag. + * + * @param bool $retry + * + * @return Client + */ + public function setRetryOnLimit($retry) + { + $this->retryOnLimit = $retry; + + return $this; + } + + /** + * Set default verify certificates flag + * + * @param bool $verifySSLCerts + * + * @return Client + */ + public function setVerifySSLCerts($verifySSLCerts) + { + $this->verifySSLCerts = $verifySSLCerts; + + return $this; + } + + /** + * Set concurrent request flag + * + * @param bool $isConcurrent + * + * @return Client + */ + public function setIsConcurrentRequest($isConcurrent) + { + $this->isConcurrentRequest = $isConcurrent; + + return $this; + } + + /** + * Build the final URL to be passed. + * + * @param array $queryParams an array of all the query parameters + * + * Nested arrays will resolve to multiple instances of the same parameter + * + * @return string + */ + private function buildUrl($queryParams = null) + { + $path = '/' . implode('/', $this->path); + if (isset($queryParams)) { + // Regex replaces `[0]=`, `[1]=`, etc. with `=`. + $path .= '?' . preg_replace('/%5B(?:\d|[1-9]\d+)%5D=/', '=', http_build_query($queryParams)); + } + + return sprintf('%s%s%s', $this->host, $this->version ?: '', $path); + } + + /** + * Creates curl options for a request + * this function does not mutate any private variables. + * + * @param string $method + * @param array $body + * @param array $headers + * + * @return array + */ + private function createCurlOptions($method, $body = null, $headers = null) + { + $options = [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HEADER => true, + CURLOPT_CUSTOMREQUEST => strtoupper($method), + CURLOPT_SSL_VERIFYPEER => $this->verifySSLCerts, + CURLOPT_FAILONERROR => false, + ] + $this->curlOptions; + + if (isset($headers)) { + $headers = array_merge($this->headers, $headers); + } else { + $headers = $this->headers; + } + + if (isset($body)) { + $encodedBody = json_encode($body); + $options[CURLOPT_POSTFIELDS] = $encodedBody; + $headers = array_merge($headers, ['Content-Type: application/json']); + } + $options[CURLOPT_HTTPHEADER] = $headers; + + if (class_exists('\\Composer\\CaBundle\\CaBundle') && method_exists('\\Composer\\CaBundle\\CaBundle', 'getSystemCaRootBundlePath')) { + $caPathOrFile = \Composer\CaBundle\CaBundle::getSystemCaRootBundlePath(); + if (is_dir($caPathOrFile) || (is_link($caPathOrFile) && is_dir(readlink($caPathOrFile)))) { + $options[CURLOPT_CAPATH] = $caPathOrFile; + } else { + $options[CURLOPT_CAINFO] = $caPathOrFile; + } + } + + return $options; + } + + /** + * @param array $requestData (method, url, body and headers) + * @param bool $retryOnLimit + * + * @return array + */ + private function createSavedRequest(array $requestData, $retryOnLimit = false) + { + return array_merge($requestData, ['retryOnLimit' => $retryOnLimit]); + } + + /** + * @param array $requests + * + * @return array + */ + private function createCurlMultiHandle(array $requests) + { + $channels = []; + $multiHandle = curl_multi_init(); + + foreach ($requests as $id => $data) { + $channels[$id] = curl_init($data['url']); + $curlOpts = $this->createCurlOptions($data['method'], $data['body'], $data['headers']); + curl_setopt_array($channels[$id], $curlOpts); + curl_multi_add_handle($multiHandle, $channels[$id]); + } + + return [$channels, $multiHandle]; + } + + /** + * Prepare response object. + * + * @param resource $channel the curl resource + * @param string $content + * + * @return Response object + */ + private function parseResponse($channel, $content) + { + $headerSize = curl_getinfo($channel, CURLINFO_HEADER_SIZE); + $statusCode = curl_getinfo($channel, CURLINFO_HTTP_CODE); + + $responseBody = mb_substr($content, $headerSize); + + $responseHeaders = mb_substr($content, 0, $headerSize); + $responseHeaders = explode("\n", $responseHeaders); + $responseHeaders = array_map('trim', $responseHeaders); + + return new Response($statusCode, $responseBody, $responseHeaders); + } + + /** + * Retry request. + * + * @param array $responseHeaders headers from rate limited response + * @param string $method the HTTP verb + * @param string $url the final url to call + * @param array $body request body + * @param array $headers original headers + * + * @return Response response object + * + * @throws InvalidRequest + */ + private function retryRequest(array $responseHeaders, $method, $url, $body, $headers) + { + $sleepDurations = $responseHeaders['X-Ratelimit-Reset'] - time(); + sleep($sleepDurations > 0 ? $sleepDurations : 0); + + return $this->makeRequest($method, $url, $body, $headers, false); + } + + /** + * Make the API call and return the response. + * This is separated into it's own function, so we can mock it easily for testing. + * + * @param string $method the HTTP verb + * @param string $url the final url to call + * @param array $body request body + * @param array $headers any additional request headers + * @param bool $retryOnLimit should retry if rate limit is reach? + * + * @return Response object + * + * @throws InvalidRequest + */ + public function makeRequest($method, $url, $body = null, $headers = null, $retryOnLimit = false) + { + $channel = curl_init($url); + + $options = $this->createCurlOptions($method, $body, $headers); + + curl_setopt_array($channel, $options); + $content = curl_exec($channel); + + if ($content === false) { + throw new InvalidRequest(curl_error($channel), curl_errno($channel)); + } + + $response = $this->parseResponse($channel, $content); + + if ($retryOnLimit && $response->statusCode() === self::TOO_MANY_REQUESTS_HTTP_CODE) { + $responseHeaders = $response->headers(true); + + return $this->retryRequest($responseHeaders, $method, $url, $body, $headers); + } + + curl_close($channel); + + return $response; + } + + /** + * Send all saved requests at once. + * + * @param array $requests + * + * @return Response[] + * + * @throws InvalidRequest + */ + public function makeAllRequests(array $requests = []) + { + if (empty($requests)) { + $requests = $this->savedRequests; + } + list($channels, $multiHandle) = $this->createCurlMultiHandle($requests); + + // running all requests + $isRunning = null; + do { + curl_multi_exec($multiHandle, $isRunning); + } while ($isRunning); + + // get response and close all handles + $retryRequests = []; + $responses = []; + $sleepDurations = 0; + foreach ($channels as $id => $channel) { + $content = curl_multi_getcontent($channel); + + if ($content === false) { + throw new InvalidRequest(curl_error($channel), curl_errno($channel)); + } + + $response = $this->parseResponse($channel, $content); + + if ($requests[$id]['retryOnLimit'] && $response->statusCode() === self::TOO_MANY_REQUESTS_HTTP_CODE) { + $headers = $response->headers(true); + $sleepDurations = max($sleepDurations, $headers['X-Ratelimit-Reset'] - time()); + $requestData = [ + 'method' => $requests[$id]['method'], + 'url' => $requests[$id]['url'], + 'body' => $requests[$id]['body'], + 'headers' => $headers, + ]; + $retryRequests[] = $this->createSavedRequest($requestData, false); + } else { + $responses[] = $response; + } + + curl_multi_remove_handle($multiHandle, $channel); + } + curl_multi_close($multiHandle); + + // retry requests + if (!empty($retryRequests)) { + sleep($sleepDurations > 0 ? $sleepDurations : 0); + $responses = array_merge($responses, $this->makeAllRequests($retryRequests)); + } + + return $responses; + } + + /** + * Add variable values to the url. (e.g. /your/api/{variable_value}/call) + * Another example: if you have a PHP reserved word, such as and, in your url, you must use this method. + * + * @param string $name name of the url segment + * + * @return Client object + */ + public function _($name = null) + { + if (isset($name)) { + $this->path[] = $name; + } + $client = new static($this->host, $this->headers, $this->version, $this->path); + $client->setCurlOptions($this->curlOptions); + $client->setVerifySSLCerts($this->verifySSLCerts); + $client->setRetryOnLimit($this->retryOnLimit); + $this->path = []; + + return $client; + } + + /** + * Dynamically add method calls to the url, then call a method. + * (e.g. client.name.name.method()). + * + * @param string $name name of the dynamic method call or HTTP verb + * @param array $args parameters passed with the method call + * + * @return Client|Response|Response[]|null object + * + * @throws InvalidRequest + */ + public function __call($name, $args) + { + $name = mb_strtolower($name); + + if ($name === 'version') { + $this->version = $args[0]; + + return $this->_(); + } + + // send all saved requests + if (($name === 'send') && $this->isConcurrentRequest) { + return $this->makeAllRequests(); + } + + if (\in_array($name, $this->methods, true)) { + $body = isset($args[0]) ? $args[0] : null; + $queryParams = isset($args[1]) ? $args[1] : null; + $url = $this->buildUrl($queryParams); + $headers = isset($args[2]) ? $args[2] : null; + $retryOnLimit = isset($args[3]) ? $args[3] : $this->retryOnLimit; + + if ($this->isConcurrentRequest) { + // save request to be sent later + $requestData = ['method' => $name, 'url' => $url, 'body' => $body, 'headers' => $headers]; + $this->savedRequests[] = $this->createSavedRequest($requestData, $retryOnLimit); + + return null; + } + + return $this->makeRequest($name, $url, $body, $headers, $retryOnLimit); + } + + return $this->_($name); + } +} diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/lib/Exception/InvalidRequest.php b/lib/sendgrid-php/vendor/sendgrid/php-http-client/lib/Exception/InvalidRequest.php new file mode 100644 index 000000000..21ae2acc3 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/lib/Exception/InvalidRequest.php @@ -0,0 +1,21 @@ +statusCode = $statusCode; + $this->body = $body; + $this->headers = $headers; + } + + /** + * The status code. + * + * @return int + */ + public function statusCode() + { + return $this->statusCode; + } + + /** + * The response body. + * + * @return string + */ + public function body() + { + return $this->body; + } + + /** + * The response headers. + * + * @param bool $assoc + * + * @return array + */ + public function headers($assoc = false) + { + if (!$assoc) { + return $this->headers; + } + + return $this->prettifyHeaders($this->headers); + } + + /** + * Returns response headers as associative array. + * + * @param array $headers + * + * @return array + */ + private function prettifyHeaders(array $headers) + { + return array_reduce( + array_filter($headers), + static function ($result, $header) { + if (mb_strpos($header, ':') === false) { + $result['Status'] = trim($header); + + return $result; + } + + list($key, $value) = explode(':', $header, 2); + $result[trim($key)] = trim($value); + + return $result; + }, + [] + ); + } +} diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/static/img/github-fork.png b/lib/sendgrid-php/vendor/sendgrid/php-http-client/static/img/github-fork.png new file mode 100644 index 0000000000000000000000000000000000000000..c84dc16a742d11066fcd355a7dc4b7d15528757c GIT binary patch literal 13855 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
    (|7%%1MgJdRu0mG?$zz8hR<^tSN2 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

    8;tr0mP1Kbr-#LQ-mKj(wv(N%BpQRC=j4 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%0Dk`q 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%0AVpR(J!f@-fh4A~?#h`hQtRRyA83iXH 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 R-rmOm1fH&bF6*2UngCz8r#t`v literal 0 HcmV?d00001 diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/twilio_sendgrid_logo.png b/lib/sendgrid-php/vendor/sendgrid/php-http-client/twilio_sendgrid_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a4c22239ac0cc70e10a51f828390d713edc8a29b GIT binary patch literal 14596 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 literal 0 HcmV?d00001 diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/use_cases/README.md b/lib/sendgrid-php/vendor/sendgrid/php-http-client/use_cases/README.md new file mode 100644 index 000000000..84e4c2f83 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/use_cases/README.md @@ -0,0 +1,4 @@ +This directory provides examples for specific use cases. Please [open an issue](https://github.com/sendgrid/php-http-client/issues) or make a pull request for any use cases you would like us to document here. Thank you! + +# Table of Contents +* [Docker](docker.md) diff --git a/lib/sendgrid-php/vendor/sendgrid/php-http-client/use_cases/docker.md b/lib/sendgrid-php/vendor/sendgrid/php-http-client/use_cases/docker.md new file mode 100644 index 000000000..7051ec056 --- /dev/null +++ b/lib/sendgrid-php/vendor/sendgrid/php-http-client/use_cases/docker.md @@ -0,0 +1,21 @@ +# Docker + +You can run the example code at `examples/example.php` in a Docker container. + +From the root directory: + +```bash +cp examples/.env_sample .env +``` + +Update the `.env` file with your SendGrid API Key. If you don't have one, you can get one [here](https://sendgrid.com/free?source=php-http-client). + +Add the `.env` file to your `.gitignore` file if you are publishing your code publically. + +``` +source .env +docker build --build-arg sendgrid_apikey=$SENDGRID_API_KEY -t client . +docker run client php examples/example.php +``` + +You should see a list of your SendGrid API Keys. diff --git a/lib/sendgrid-php/vendor/starkbank/ecdsa/.travis.yml b/lib/sendgrid-php/vendor/starkbank/ecdsa/.travis.yml new file mode 100644 index 000000000..70a75eb14 --- /dev/null +++ b/lib/sendgrid-php/vendor/starkbank/ecdsa/.travis.yml @@ -0,0 +1,13 @@ +language: php + +matrix: + include: + - php: "5.6" + - php: "7.0" + - php: "7.1" + - php: "7.2" + - php: "7.3" + - php: "7.4" + +script: + - php tests/test.php diff --git a/lib/sendgrid-php/vendor/starkbank/ecdsa/LICENSE b/lib/sendgrid-php/vendor/starkbank/ecdsa/LICENSE new file mode 100644 index 000000000..71df046e2 --- /dev/null +++ b/lib/sendgrid-php/vendor/starkbank/ecdsa/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Stark Bank S.A. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/sendgrid-php/vendor/starkbank/ecdsa/README.md b/lib/sendgrid-php/vendor/starkbank/ecdsa/README.md new file mode 100644 index 000000000..ed2184d1f --- /dev/null +++ b/lib/sendgrid-php/vendor/starkbank/ecdsa/README.md @@ -0,0 +1,161 @@ +## A lightweight and fast PHP ECDSA + +### Overview + +This is a PHP implementation of the Elliptic Curve Digital Signature Algorithm. It is compatible with PHP 5.5+. Please note that this library relies heavily on the openssl package for PHP, so - depending on your PHP installation - you may need to re-compile it with the "–with-openssl" flag. + +### Installation + +#### Composer + +To install the package with Composer, run: + +```sh +composer require starkbank/ecdsa +``` + +To use the bindings, use Composer's autoload: + +```sh +require_once('vendor/autoload.php'); +``` + +### Curves + +The module is wrapped around the builtin openssl functions, so all standar curves should be supported. The default is `secp256k1`. + +### Speed + +We ran a test on a MAC Pro i7 2017. We ran the library 100 times and got the average time displayed bellow: + +| Library | sign | verify | +| ------------------ |:-------------:| -------:| +| starkbank-ecdsa | 0.6ms | 0.4ms | + +### Sample Code + +How to sign a json message for [Stark Bank]: + +```php + +# Generate privateKey from PEM string +$privateKey = EllipticCurve\PrivateKey::fromPem(" + -----BEGIN EC PARAMETERS----- + BgUrgQQACg== + -----END EC PARAMETERS----- + -----BEGIN EC PRIVATE KEY----- + MHQCAQEEIODvZuS34wFbt0X53+P5EnSj6tMjfVK01dD1dgDH02RzoAcGBSuBBAAK + oUQDQgAE/nvHu/SQQaos9TUljQsUuKI15Zr5SabPrbwtbfT/408rkVVzq8vAisbB + RmpeRREXj5aog/Mq8RrdYy75W9q/Ig== + -----END EC PRIVATE KEY----- +"); + + +# Create message from json +$message = array( + "transfers" => array( + array( + "amount" => 100000000, + "taxId" => "594.739.480-42", + "name" => "Daenerys Targaryen Stormborn", + "bankCode" => "341", + "branchCode" => "2201", + "accountNumber" => "76543-8", + "tags" => array("daenerys", "targaryen", "transfer-1-external-id") + ) + ) +); + +$message = json_encode($message, JSON_PRETTY_PRINT); + +$signature = EllipticCurve\Ecdsa::sign($message, $privateKey); + +# Generate Signature in base64. This result can be sent to Stark Bank in header as Digital-Signature parameter +echo "\n" . $signature->toBase64(); + +# To double check if message matches the signature +$publicKey = $privateKey->publicKey(); + +echo "\n" . EllipticCurve\Ecdsa::verify($message, $signature, $publicKey); + +``` + +Simple use: + +```php + +# Generate new Keys +$privateKey = new EllipticCurve\PrivateKey; +$publicKey = $privateKey->publicKey(); + +$message = "My test message"; + +# Generate Signature +$signature = EllipticCurve\Ecdsa::sign($message, $privateKey); + +# Verify if signature is valid +echo "\n" . EllipticCurve\Ecdsa::verify($message, $signature, $publicKey); + +``` + +### OpenSSL + +This library is compatible with OpenSSL, so you can use it to generate keys: + +``` +openssl ecparam -name secp256k1 -genkey -out privateKey.pem +openssl ec -in privateKey.pem -pubout -out publicKey.pem +``` + +Create a message.txt file and sign it: + +``` +openssl dgst -sha256 -sign privateKey.pem -out signatureDer.txt message.txt +``` + +It's time to verify: + +```php + +$publicKeyPem = EllipticCurve\Utils\File::read("publicKey.pem"); +$signatureDer = EllipticCurve\Utils\File::read("signatureDer.txt"); +$message = EllipticCurve\Utils\File::read("message.txt"); + +$publicKey = EllipticCurve\PublicKey::fromPem($publicKeyPem); +$signature = EllipticCurve\Signature::fromDer($signatureDer); + +echo "\n" . EllipticCurve\Ecdsa::verify($message, $signature, $publicKey); + +``` + +You can also verify it on terminal: + +``` +openssl dgst -sha256 -verify publicKey.pem -signature signatureDer.txt message.txt +``` + +NOTE: If you want to create a Digital Signature to use in the [Stark Bank], you need to convert the binary signature to base64. + +``` +openssl base64 -in signatureDer.txt -out signatureBase64.txt +``` + +You can also verify it with this library: + +```php +$signatureDer = EllipticCurve\Utils\File::read("signatureDer.txt"); + +$signature = EllipticCurve\Signature::fromDer($signatureDer); + +echo "\n" . $signature->toBase64(); +``` + +[Stark Bank]: https://starkbank.com + +### Run all unit tests + +```sh +php tests/test.php +``` + +[python-ecdsa]: https://github.com/warner/python-ecdsa diff --git a/lib/sendgrid-php/vendor/starkbank/ecdsa/src/ecdsa.php b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/ecdsa.php new file mode 100644 index 000000000..10e33eb69 --- /dev/null +++ b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/ecdsa.php @@ -0,0 +1,24 @@ +openSslPrivateKey, OPENSSL_ALGO_SHA256); + return new Signature($signature); + } + + public static function verify ($message, $signature, $publicKey) { + $success = openssl_verify($message, $signature->toDer(), $publicKey->openSslPublicKey, OPENSSL_ALGO_SHA256); + if ($success == 1) { + return true; + } + return false; + } +} + +?> \ No newline at end of file diff --git a/lib/sendgrid-php/vendor/starkbank/ecdsa/src/ellipticcurve.php b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/ellipticcurve.php new file mode 100644 index 000000000..d353cec74 --- /dev/null +++ b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/ellipticcurve.php @@ -0,0 +1,9 @@ + diff --git a/lib/sendgrid-php/vendor/starkbank/ecdsa/src/privatekey.php b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/privatekey.php new file mode 100644 index 000000000..8d7797f9a --- /dev/null +++ b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/privatekey.php @@ -0,0 +1,81 @@ + "sha256", + "private_key_bits" => 2048, + "private_key_type" => OPENSSL_KEYTYPE_EC, + "curve_name" => $curve + ); + + $response = openssl_pkey_new($config); + + openssl_pkey_export($response, $openSslPrivateKey, null, $config); + + $openSslPrivateKey = openssl_pkey_get_private($openSslPrivateKey); + } + + $this->openSslPrivateKey = $openSslPrivateKey; + } + + function publicKey() { + $openSslPublicKey = openssl_pkey_get_details($this->openSslPrivateKey)["key"]; + + return new PublicKey($openSslPublicKey); + } + + function toString () { + return base64_encode($this->toDer()); + } + + function toDer () { + $pem = $this->toPem(); + + $lines = array(); + foreach(explode("\n", $pem) as $value) { + if (substr($value, 0, 5) !== "-----") { + array_push($lines, $value); + } + } + + $pem_data = join("", $lines); + + return base64_decode($pem_data); + } + + function toPem () { + openssl_pkey_export($this->openSslPrivateKey, $out, null); + return $out; + } + + static function fromPem ($str) { + $rebuilt = array(); + foreach(explode("\n", $str) as $line) { + $line = trim($line); + if (strlen($line) > 1) { + array_push($rebuilt, $line); + } + }; + $rebuilt = join("\n", $rebuilt) . "\n"; + return new PrivateKey(null, openssl_get_privatekey($rebuilt)); + } + + static function fromDer ($str) { + $pem_data = base64_encode($str); + $pem = "-----BEGIN EC PRIVATE KEY-----\n" . substr($pem_data, 0, 64) . "\n" . substr($pem_data, 64, 64) . "\n" . substr($pem_data, 128, 64) . "\n-----END EC PRIVATE KEY-----\n"; + return new PrivateKey(null, openssl_get_privatekey($pem)); + } + + static function fromString ($str) { + return PrivateKey::fromDer(base64_decode($str)); + } + +} + +?> \ No newline at end of file diff --git a/lib/sendgrid-php/vendor/starkbank/ecdsa/src/publickey.php b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/publickey.php new file mode 100644 index 000000000..bfdcb03d9 --- /dev/null +++ b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/publickey.php @@ -0,0 +1,60 @@ +pem = $pem; + $this->openSslPublicKey = openssl_get_publickey($pem); + } + + function toString () { + return base64_encode($this->toDer()); + } + + function toDer () { + $pem = $this->toPem(); + + $lines = array(); + foreach(explode("\n", $pem) as $value) { + if (substr($value, 0, 5) !== "-----") { + array_push($lines, $value); + } + } + + $pem_data = join("", $lines); + + return base64_decode($pem_data); + } + + function toPem () { + return $this->pem; + } + + static function fromPem ($str) { + $rebuilt = array(); + foreach(explode("\n", $str) as $line) { + $line = trim($line); + if (strlen($line) > 1) { + array_push($rebuilt, $line); + } + }; + $rebuilt = join("\n", $rebuilt) . "\n"; + return new PublicKey($rebuilt); + } + + static function fromDer ($str) { + $pem_data = base64_encode($str); + $pem = "-----BEGIN PUBLIC KEY-----\n" . substr($pem_data, 0, 64) . "\n" . substr($pem_data, 64) . "\n-----END PUBLIC KEY-----\n"; + return new PublicKey($pem); + } + + static function fromString ($str) { + return PublicKey::fromDer(base64_decode($str)); + } + +} + +?> \ No newline at end of file diff --git a/lib/sendgrid-php/vendor/starkbank/ecdsa/src/signature.php b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/signature.php new file mode 100644 index 000000000..aef404166 --- /dev/null +++ b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/signature.php @@ -0,0 +1,29 @@ +der = $der; + } + + function toDer () { + return $this->der; + } + + function toBase64 () { + return base64_encode($this->der); + } + + static function fromDer ($str) { + return new Signature($str); + } + + static function fromBase64 ($str) { + return new Signature(base64_decode($str)); + } +} + +?> \ No newline at end of file diff --git a/lib/sendgrid-php/vendor/starkbank/ecdsa/src/utils/file.php b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/utils/file.php new file mode 100644 index 000000000..2b5a84949 --- /dev/null +++ b/lib/sendgrid-php/vendor/starkbank/ecdsa/src/utils/file.php @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/modules/account/changemail.php b/modules/account/changemail.php index 61c9f8828..dc9226a42 100644 --- a/modules/account/changemail.php +++ b/modules/account/changemail.php @@ -43,10 +43,15 @@ $res = $sth->execute(array($code, $session->account->account_id, $session->account->email, $email, $ip)); if ($res) { - require_once 'Flux/Mailer.php'; + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } $name = $session->loginAthenaGroup->serverName; $link = $this->url('account', 'confirmemail', array('_host' => true, 'code' => $code, 'account' => $session->account->account_id, 'login' => $name)); - $mail = new Flux_Mailer(); $sent = $mail->send($email, 'Change E-mail', 'changemail', array( 'AccountUsername' => $session->account->userid, 'OldEmail' => $session->account->email, diff --git a/modules/account/create.php b/modules/account/create.php index c0df7aa36..d0a7915f8 100644 --- a/modules/account/create.php +++ b/modules/account/create.php @@ -33,13 +33,17 @@ if ($result) { if (Flux::config('RequireEmailConfirm')) { - require_once 'Flux/Mailer.php'; - + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } $user = $username; $code = md5(rand()); $name = $session->loginAthenaGroup->serverName; $link = $this->url('account', 'confirm', array('_host' => true, 'code' => $code, 'user' => $username, 'login' => $name)); - $mail = new Flux_Mailer(); $sent = $mail->send($email, 'Account Confirmation', 'confirm', array('AccountUsername' => $username, 'ConfirmationLink' => htmlspecialchars($link))); $createTable = Flux::config('FluxTables.AccountCreateTable'); diff --git a/modules/account/resend.php b/modules/account/resend.php index 445bc46b3..601f1cfd0 100644 --- a/modules/account/resend.php +++ b/modules/account/resend.php @@ -35,11 +35,16 @@ $row = $sth->fetch(); if ($row) { - require_once 'Flux/Mailer.php'; + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } $code = $row->confirm_code; $name = $loginAthenaGroup->serverName; $link = $this->url('account', 'confirm', array('_host' => true, 'code' => $code, 'user' => $userid, 'login' => $name)); - $mail = new Flux_Mailer(); $sent = $mail->send($email, 'Account Confirmation', 'confirm', array('AccountUsername' => $userid, 'ConfirmationLink' => htmlspecialchars($link))); } diff --git a/modules/account/resetpass.php b/modules/account/resetpass.php index dced8de47..c12b25cd2 100644 --- a/modules/account/resetpass.php +++ b/modules/account/resetpass.php @@ -54,10 +54,15 @@ $res = $sth->execute(array($code, $row->account_id, $row->user_pass, $_SERVER['REMOTE_ADDR'])); if ($res) { - require_once 'Flux/Mailer.php'; + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } $name = $loginAthenaGroup->serverName; $link = $this->url('account', 'resetpw', array('_host' => true, 'code' => $code, 'account' => $row->account_id, 'login' => $name)); - $mail = new Flux_Mailer(); $sent = $mail->send($email, 'Reset Password', 'resetpass', array('AccountUsername' => $userid, 'ResetLink' => htmlspecialchars($link))); } } diff --git a/modules/account/resetpw.php b/modules/account/resetpw.php index 64d18b92e..896c1135f 100644 --- a/modules/account/resetpw.php +++ b/modules/account/resetpw.php @@ -66,8 +66,14 @@ $this->redirect(); } -require_once 'Flux/Mailer.php'; -$mail = new Flux_Mailer(); +if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); +} else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); +} + $sent = $mail->send($acc->email, 'Password Has Been Reset', 'newpass', array('AccountUsername' => $acc->userid, 'NewPassword' => $unhashedNewPassword)); if ($sent) { diff --git a/modules/mail/index.php b/modules/mail/index.php index 0f3f20c54..35863d9f7 100644 --- a/modules/mail/index.php +++ b/modules/mail/index.php @@ -39,8 +39,13 @@ foreach($list as $lrow){ $email = $lrow->email; - require_once 'Flux/Mailer.php'; - $mail = new Flux_Mailer(); + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } $sent = $mail->send($email, $subject, $template, array( 'emailtitle' => $subject, 'username' => $lrow->userid, diff --git a/modules/servicedesk/create.php b/modules/servicedesk/create.php index 1d62043f5..eaef3e15c 100644 --- a/modules/servicedesk/create.php +++ b/modules/servicedesk/create.php @@ -55,9 +55,14 @@ $stlist = $stsql->fetch(); $email = $stlist->email; - require_once 'Flux/Mailer.php'; + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } $name = $session->loginAthenaGroup->serverName; - $mail = new Flux_Mailer(); $sent = $mail->send($email, 'New Ticket Created', 'newticket', array( 'Category' => $catlist->name, 'Subject' => $subject, diff --git a/modules/servicedesk/staffview.php b/modules/servicedesk/staffview.php index 8e1819843..6da6f4b1a 100644 --- a/modules/servicedesk/staffview.php +++ b/modules/servicedesk/staffview.php @@ -35,19 +35,24 @@ $sth->execute(array($ticket_id, $_POST['staff_reply_name'], $text, $_SERVER['REMOTE_ADDR'])); $sth = $server->connection->getStatement("UPDATE {$server->loginDatabase}.$tbl SET lastreply = 'Staff' WHERE ticket_id = ?"); $sth->execute(array($ticket_id)); - require_once 'Flux/Mailer.php'; - $name = $session->loginAthenaGroup->serverName; - $mail = new Flux_Mailer(); - $sent = $mail->send($_POST['curemail'], 'Ticket Reply', 'ticketreply', array( - 'TicketID' => $ticket_id, - 'Staff' => $staffsess->prefered_name - )); - if ($sent) { - $this->redirect($this->url('servicedesk','staffview', array('ticketid' => $ticket_id))); - } - else { - $fail = true; - } + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } + $name = $session->loginAthenaGroup->serverName; + $sent = $mail->send($_POST['curemail'], 'Ticket Reply', 'ticketreply', array( + 'TicketID' => $ticket_id, + 'Staff' => $staffsess->prefered_name + )); + if ($sent) { + $this->redirect($this->url('servicedesk','staffview', array('ticketid' => $ticket_id))); + } + else { + $fail = true; + } }elseif($_POST['secact']=='2'){ if($_POST['response']=='Leave as-is to skip text response.' || $_POST['response'] == '' || $_POST['response'] == NULL || !isset($_POST['response'])){ @@ -59,13 +64,18 @@ $sql .= "VALUES (?, ?, ?, 0, ?, 1)"; $sth = $server->connection->getStatement($sql); $sth->execute(array($ticket_id, $_POST['staff_reply_name'], $text, $_SERVER['REMOTE_ADDR'])); - require_once 'Flux/Mailer.php'; - $name = $session->loginAthenaGroup->serverName; - $mail = new Flux_Mailer(); - $sent = $mail->send($email, 'Ticket Reply', 'ticketreply', array( - 'TicketID' => $ticket_id, - 'Staff' => $staffsess->prefered_name - )); + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } + $name = $session->loginAthenaGroup->serverName; + $sent = $mail->send($email, 'Ticket Reply', 'ticketreply', array( + 'TicketID' => $ticket_id, + 'Staff' => $staffsess->prefered_name + )); $sth = $server->connection->getStatement("UPDATE {$server->loginDatabase}.$tbl SET lastreply = 'Staff' WHERE ticket_id = ?"); $sth->execute(array($ticket_id)); $this->redirect($this->url('servicedesk','staffindex')); @@ -85,13 +95,18 @@ $sql .= "VALUES (?, ?, ?, ?, ?, 1)"; $sth = $server->connection->getStatement($sql); $sth->execute(array($ticket_id, $_POST['staff_reply_name'], $text, $action, $_SERVER['REMOTE_ADDR'])); - require_once 'Flux/Mailer.php'; - $name = $session->loginAthenaGroup->serverName; - $mail = new Flux_Mailer(); - $sent = $mail->send($email, 'Ticket Reply', 'ticketreply', array( - 'TicketID' => $ticket_id, - 'Staff' => $staffsess->prefered_name - )); + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } + $name = $session->loginAthenaGroup->serverName; + $sent = $mail->send($email, 'Ticket Reply', 'ticketreply', array( + 'TicketID' => $ticket_id, + 'Staff' => $staffsess->prefered_name + )); $sth = $server->connection->getStatement("UPDATE {$server->loginDatabase}.$tbl SET lastreply = 'Staff' WHERE ticket_id = ?"); $sth->execute(array($ticket_id)); $this->redirect($this->url('servicedesk','staffindex')); @@ -152,13 +167,18 @@ $sql .= "VALUES (?, ?, ?, ?, ?, 1)"; $sth = $server->connection->getStatement($sql); $sth->execute(array($ticket_id, $_POST['staff_reply_name'], $text, $action, $_SERVER['REMOTE_ADDR'])); - require_once 'Flux/Mailer.php'; - $name = $session->loginAthenaGroup->serverName; - $mail = new Flux_Mailer(); - $sent = $mail->send($email, 'Ticket Reply', 'ticketreply', array( - 'TicketID' => $ticket_id, - 'Staff' => $staffsess->prefered_name - )); + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } + $name = $session->loginAthenaGroup->serverName; + $sent = $mail->send($email, 'Ticket Reply', 'ticketreply', array( + 'TicketID' => $ticket_id, + 'Staff' => $staffsess->prefered_name + )); $sth = $server->connection->getStatement("UPDATE {$server->loginDatabase}.$tbl SET lastreply = 'Staff' WHERE ticket_id = ?"); $sth->execute(array($ticket_id)); $this->redirect($this->url('servicedesk','staffindex')); @@ -180,13 +200,18 @@ $sth = $server->connection->getStatement($sql); $res = $server->loginServer->depositCredits($_POST['account_id'], $give_credits); $sth->execute(array($ticket_id, $_POST['staff_reply_name'], $text, $action, $_SERVER['REMOTE_ADDR'])); - require_once 'Flux/Mailer.php'; - $name = $session->loginAthenaGroup->serverName; - $mail = new Flux_Mailer(); - $sent = $mail->send($email, 'Ticket Reply', 'ticketreply', array( - 'TicketID' => $ticket_id, - 'Staff' => $staffsess->prefered_name - )); + if(Flux::config('SendGridAPIKey')){ + require_once 'Flux/MailerSendGrid.php'; + $mail = new Flux_Mailer_SendGrid(); + } else { + require_once 'Flux/Mailer.php'; + $mail = new Flux_Mailer(); + } + $name = $session->loginAthenaGroup->serverName; + $sent = $mail->send($email, 'Ticket Reply', 'ticketreply', array( + 'TicketID' => $ticket_id, + 'Staff' => $staffsess->prefered_name + )); $sth = $server->connection->getStatement("UPDATE {$server->loginDatabase}.$tbl SET lastreply = 'Staff' WHERE ticket_id = ?"); $sth->execute(array($ticket_id)); $this->redirect($this->url('servicedesk','staffindex'));