├── .github └── workflows │ ├── php-cs-fixer.yml │ └── phpstan.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── LICENSE ├── README.md ├── composer.json ├── phpspec.yml ├── phpstan-baseline.neon ├── phpstan.neon.dist └── src ├── Mailchimp.php ├── MailchimpFacade.php ├── MailchimpServiceProvider.php └── config └── mailchimp.php /.github/workflows/php-cs-fixer.yml: -------------------------------------------------------------------------------- 1 | name: Check & fix styling 2 | 3 | on: [push] 4 | 5 | jobs: 6 | php-cs-fixer: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v3 12 | with: 13 | ref: ${{ github.head_ref }} 14 | 15 | - name: Run PHP CS Fixer 16 | uses: docker://oskarstark/php-cs-fixer-ga 17 | with: 18 | args: --config=.php-cs-fixer.dist.php --allow-risky=yes 19 | 20 | - name: Commit changes 21 | uses: stefanzweifel/git-auto-commit-action@v4 22 | with: 23 | commit_message: Fix styling 24 | -------------------------------------------------------------------------------- /.github/workflows/phpstan.yml: -------------------------------------------------------------------------------- 1 | name: Phpstan 2 | 3 | on: 4 | push: 5 | paths: 6 | - "**.php" 7 | - "phpstan.neon.dist" 8 | - "phpstan-baseline.neon" 9 | 10 | jobs: 11 | phpstan: 12 | name: phpstan 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | 17 | - name: Setup PHP 18 | uses: shivammathur/setup-php@v2 19 | with: 20 | php-version: "8.0" 21 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick 22 | coverage: none 23 | 24 | - name: Cache composer dependencies 25 | uses: actions/cache@v3 26 | with: 27 | path: vendor 28 | key: composer-${{ hashFiles('composer.lock') }} 29 | 30 | - name: Run composer install 31 | run: composer install -n --prefer-dist 32 | 33 | - name: Run phpstan 34 | run: ./vendor/bin/phpstan analyse --error-format=github 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /vendor 3 | .idea 4 | composer.lock 5 | client.php 6 | .php-cs-fixer.cache 7 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | notPath('bootstrap/*') 5 | ->notPath('storage/*') 6 | ->notPath('resources/view/mail/*') 7 | ->in([ 8 | __DIR__ . '/src', 9 | ]) 10 | ->name('*.php') 11 | ->notName('*.blade.php') 12 | ->ignoreDotFiles(true) 13 | ->ignoreVCS(true); 14 | 15 | return (new PhpCsFixer\Config()) 16 | ->setRules([ 17 | '@PSR2' => true, 18 | 'array_syntax' => ['syntax' => 'short'], 19 | 'ordered_imports' => ['sort_algorithm' => 'alpha'], 20 | 'no_unused_imports' => true, 21 | 'not_operator_with_successor_space' => true, 22 | 'trailing_comma_in_multiline' => true, 23 | 'phpdoc_scalar' => true, 24 | 'unary_operator_spaces' => true, 25 | 'binary_operator_spaces' => true, 26 | 'blank_line_before_statement' => [ 27 | 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], 28 | ], 29 | 'phpdoc_single_line_var_spacing' => true, 30 | 'phpdoc_var_without_name' => true, 31 | 'class_attributes_separation' => [ 32 | 'elements' => [ 33 | 'method' => 'one', 34 | ], 35 | ], 36 | 'method_argument_space' => [ 37 | 'on_multiline' => 'ensure_fully_multiline', 38 | 'keep_multiple_spaces_after_comma' => true, 39 | ], 40 | 'single_trait_insert_per_statement' => true, 41 | ]) 42 | ->setFinder($finder); 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Chris Magnussen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mailchimp API v3 - PHP Wrapper [![Build Status](https://travis-ci.org/pacely/mailchimp-api-v3.svg?branch=master)](https://travis-ci.org/pacely/mailchimp-api-v3) 2 | 3 | * [Installation](#installation) 4 | * [Laravel Users](#laravel-users) 5 | * [Service Provider](#service-provider) 6 | * [Facade](#facade) 7 | * [Configuration](#configuration) 8 | * [Usage](#usage) 9 | * [Pagination](#pagination) 10 | * [Filtering](#filtering) 11 | * [Partial Response](#partial-response) 12 | * [Behind Proxy](#behind-proxy) 13 | * [Examples](#examples) 14 | * [Collection object](#collection-object) 15 | * [Create lists](#create-lists) 16 | * [Subresources](#subresources) 17 | * [Proxy](#proxy) 18 | * [Further documentation](#further-documentation) 19 | 20 | # Installation 21 | Add the following to your composer.json 22 | 23 | ```json 24 | { 25 | "require": { 26 | "pacely/mailchimp-apiv3": "dev-master" 27 | } 28 | } 29 | ``` 30 | 31 | # Laravel Users 32 | We've added some classes to help Laravel 5 users make use of the library with ease. 33 | 34 | #### Service Provider 35 | You can register our [service provider](http://laravel.com/docs/5.1/providers) in your `app.php` config file. 36 | 37 | ```php 38 | // config/app.php 39 | 'providers' => [ 40 | ... 41 | Mailchimp\MailchimpServiceProvider::class 42 | ] 43 | ``` 44 | 45 | #### Facade 46 | If you prefer [facades](http://laravel.com/docs/5.1/facades), make sure you add this as well: 47 | 48 | ```php 49 | // config/app.php 50 | 'aliases' => [ 51 | ... 52 | 'MC' => Mailchimp\MailchimpFacade::class 53 | ] 54 | ``` 55 | 56 | *NOTE*: Make sure not to register the facade with the `Mailchimp` alias, as that could potentially clash with the base class. 57 | 58 | #### Configuration 59 | There are only one configuration option you need to fill in. Publish the config by running: 60 | 61 | php artisan vendor:publish 62 | 63 | Now, the config file will be located under `config/mailchimp.php`: 64 | 65 | ```php 66 | API keys. Paste the key below. 77 | | 78 | */ 79 | 'apikey' => '' 80 | ]; 81 | ``` 82 | 83 | # Usage 84 | There's one method to rule them all: 85 | 86 | ```php 87 | request($resource, $arguments = [], $method = 'GET') // $arguments is used as POST data or GET parameters, depending on the method used. 88 | ``` 89 | 90 | But its clever enough to map these calls aswell: 91 | 92 | ```php 93 | get($resource, array $options = []) 94 | head($resource, array $options = []) 95 | put($resource, array $options = []) 96 | post($resource, array $options = []) 97 | patch($resource, array $options = []) 98 | delete($resource, array $options = []) 99 | ``` 100 | 101 | ### Pagination 102 | _We use `offset` and `count` in the query string to paginate data, because it provides greater control over how you view your data. Offset defaults to 0, so if you use offset=1, you'll miss the first element in the dataset. Count defaults to 10._ 103 | 104 | Source: http://kb.mailchimp.com/api/article/api-3-overview 105 | 106 | ### Filtering 107 | _Most endpoints don't currently support filtering, but we plan to add these capabilities over time. Schemas will tell you which collections can be filtered, and what to include in your query string._ 108 | 109 | Source: http://kb.mailchimp.com/api/article/api-3-overview 110 | 111 | ### Partial Response 112 | _To cut down on data transfers, pass a comma separated list of fields to include or exclude from a certain response in the query string. The parameters `fields` and `exclude_fields` are mutually exclusive and will throw an error if a field isn't valid in your request._ 113 | 114 | Source: http://kb.mailchimp.com/api/article/api-3-overview 115 | 116 | ### Behind Proxy 117 | If you are behind a proxy, you can use `setProxy` directly on the class. 118 | 119 | `setProxy(host : string, port : int, [ssl : bool = false], [username = null], [password = null]);` 120 | 121 | See the [example](#proxy). 122 | 123 | # Examples 124 | 125 | ### Collection object 126 | All queries will return an instance of the [Illuminate\Support\Collection](http://laravel.com/api/master/Illuminate/Support/Collection.html) object, which is really easy to work with. If you don't want to use the Collection object however, you can transform it into an array using `$result->toArray()`. 127 | 128 | ```php 129 | $mc = new Mailchimp('', ''); 130 | 131 | // Get 10 lists starting from offset 10 and include only a specific set of fields 132 | $result = $mc->request('lists', [ 133 | 'fields' => 'lists.id,lists.name,lists.stats.member_count', 134 | 'offset' => 10, 135 | 'count' => 10 136 | ]); 137 | 138 | // Will fire this query: 139 | // GET https://us1.api.mailchimp.com/3.0/lists?fields=lists.id,lists.name,lists.stats.member_count&count=10 140 | 141 | // Returns object(Illuminate\Support\Collection) 142 | var_dump($result); 143 | 144 | // Returns the first item 145 | var_dump($result->first()); 146 | 147 | // Returns 3 items 148 | var_dump($result->take(3)); 149 | 150 | // Returns a JSON string 151 | var_dump($result->toJson()); 152 | 153 | // Returns an array 154 | var_dump($result->toArray()); 155 | ``` 156 | 157 | You can use a simple foreach/for loop or use the built in `each(callable $callback)` provided by our Collection object to loop through your items. 158 | 159 | ```php 160 | $result->each(function ($item) { 161 | echo $item['name'].' ('.$item['stats']['member_count'].')'.PHP_EOL; 162 | }); 163 | ``` 164 | There's alot more you can do with the [Collection](http://laravel.com/api/master/Illuminate/Support/Collection.html) object. 165 | 166 | ### Create lists 167 | 168 | ```php 169 | // All these fields are required to create a new list. 170 | $result = $mc->post('lists', [ 171 | 'name' => 'New list', 172 | 'permission_reminder' => 'You signed up for updates on Greeks economy.', 173 | 'email_type_option' => false, 174 | 'contact' => [ 175 | 'company' => 'Doe Ltd.', 176 | 'address1' => 'DoeStreet 1', 177 | 'address2' => '', 178 | 'city' => 'Doesy', 179 | 'state' => 'Doedoe', 180 | 'zip' => '1672-12', 181 | 'country' => 'US', 182 | 'phone' => '55533344412' 183 | ], 184 | 'campaign_defaults' => [ 185 | 'from_name' => 'John Doe', 186 | 'from_email' => 'john@doe.com', 187 | 'subject' => 'My new campaign!', 188 | 'language' => 'US' 189 | ] 190 | ]); 191 | ``` 192 | 193 | ### Subresources 194 | 195 | ```php 196 | $result = $mc->get('lists/e04d611199', [ 197 | 'fields' => 'id,name,stats.member_count' 198 | ]); 199 | ``` 200 | 201 | ### Proxy 202 | 203 | ```php 204 | $mc->setProxy('https://127.0.0.1', 10, true, 'username', 'password'); 205 | 206 | $result = $mc->get('lists/e04d611199', [ 207 | 'fields' => 'id,name,stats.member_count' 208 | ]); 209 | ``` 210 | 211 | # Further documentation 212 | You should read through Mailchimp's API v3 [documentation](http://kb.mailchimp.com/api/) (I know, it's pretty rough. Should get better soon.). To find out which resources is available, take a look at the [JSON API Schema for Mailchimp](https://us10.api.mailchimp.com/schema/3.0/). -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pacely/mailchimp-apiv3", 3 | "description": "Simple API wrapper for Mailchimp API V3", 4 | "type": "library", 5 | "keywords": [ 6 | "mailchimp", 7 | "api", 8 | "php", 9 | "v3" 10 | ], 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Chris Magnussen", 15 | "email": "chris@hirvi.no" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "Mailchimp\\": "src/" 21 | } 22 | }, 23 | "require": { 24 | "php": "^8.0", 25 | "guzzlehttp/guzzle": "^7.0", 26 | "laravel/framework": "^10.0" 27 | }, 28 | "require-dev": { 29 | "nunomaduro/larastan": "^2.5.1", 30 | "orchestra/testbench": "^8.0.7" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /phpspec.yml: -------------------------------------------------------------------------------- 1 | formatter.name: pretty 2 | suites: 3 | main: 4 | namespace: Mailchimp 5 | psr4_prefix: Mailchimp 6 | src_path: src 7 | -------------------------------------------------------------------------------- /phpstan-baseline.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | - 4 | message: "#^Method Mailchimp\\\\Mailchimp\\:\\:__call\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" 5 | count: 1 6 | path: src/Mailchimp.php 7 | 8 | - 9 | message: "#^Method Mailchimp\\\\Mailchimp\\:\\:getOptions\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" 10 | count: 1 11 | path: src/Mailchimp.php 12 | 13 | - 14 | message: "#^Method Mailchimp\\\\Mailchimp\\:\\:makeRequest\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" 15 | count: 1 16 | path: src/Mailchimp.php 17 | 18 | - 19 | message: "#^Method Mailchimp\\\\Mailchimp\\:\\:request\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" 20 | count: 1 21 | path: src/Mailchimp.php 22 | 23 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | includes: 2 | - ./vendor/nunomaduro/larastan/extension.neon 3 | - phpstan-baseline.neon 4 | 5 | parameters: 6 | paths: 7 | - src 8 | level: 8 9 | checkGenericClassInNonGenericObjectType: false 10 | -------------------------------------------------------------------------------- /src/Mailchimp.php: -------------------------------------------------------------------------------- 1 | 32 | */ 33 | public array $options = []; 34 | 35 | /** 36 | * @param array $clientOptions 37 | */ 38 | public function __construct(string $apikey = '', array $clientOptions = []) 39 | { 40 | $this->apikey = $apikey; 41 | $this->client = new Client($clientOptions); 42 | 43 | $this->detectEndpoint($this->apikey); 44 | 45 | $this->options['headers'] = [ 46 | 'Authorization' => 'apikey '.$this->apikey, 47 | ]; 48 | } 49 | 50 | /** 51 | * @throws Exception 52 | */ 53 | public function request(string $resource, array $arguments = [], string $method = 'GET'): Collection 54 | { 55 | if (! $this->apikey) { 56 | throw new Exception('Please provide an API key.'); 57 | } 58 | 59 | return $this->makeRequest($resource, $arguments, strtolower($method)); 60 | } 61 | 62 | /** 63 | * Enable proxy if needed. 64 | */ 65 | public function setProxy( 66 | string $host, 67 | int $port, 68 | bool $ssl = false, 69 | ?string $username = null, 70 | ?string $password = null 71 | ): string { 72 | $scheme = ($ssl ? 'https://' : 'http://'); 73 | 74 | if (! is_null($username)) { 75 | return $this->options['proxy'] = sprintf('%s%s:%s@%s:%s', $scheme, $username, $password, $host, $port); 76 | } 77 | 78 | return $this->options['proxy'] = sprintf('%s%s:%s', $scheme, $host, $port); 79 | } 80 | 81 | public function getEndpoint(): string 82 | { 83 | return $this->endpoint; 84 | } 85 | 86 | public function detectEndpoint(string $apikey): void 87 | { 88 | if (! strstr($apikey, '-')) { 89 | throw new InvalidArgumentException('There seems to be an issue with your apikey. Please consult Mailchimp'); 90 | } 91 | 92 | list(, $dc) = explode('-', $apikey); 93 | $this->endpoint = str_replace('us1', $dc, $this->endpoint); 94 | } 95 | 96 | public function setApiKey(string $apikey): void 97 | { 98 | $this->detectEndpoint($apikey); 99 | 100 | $this->apikey = $apikey; 101 | } 102 | 103 | /** 104 | * @throws Exception 105 | */ 106 | private function makeRequest(string $resource, array $arguments, string $method): Collection 107 | { 108 | try { 109 | $options = $this->getOptions($method, $arguments); 110 | $response = $this->client->{$method}($this->endpoint.$resource, $options); 111 | 112 | $collection = new Collection( 113 | json_decode($response->getBody()) 114 | ); 115 | 116 | if ($collection->count() == 1) { 117 | return $collection->collapse(); 118 | } 119 | 120 | return $collection; 121 | } catch (ClientException $e) { 122 | throw new Exception($e->getResponse()->getBody()); 123 | } catch (RequestException $e) { 124 | $response = $e->getResponse(); 125 | 126 | if ($response instanceof ResponseInterface) { 127 | throw new Exception($response->getBody()); 128 | } 129 | 130 | throw new Exception($e->getMessage()); 131 | } 132 | } 133 | 134 | /** 135 | * @return array 136 | */ 137 | private function getOptions(string $method, array $arguments): array 138 | { 139 | unset($this->options['json'], $this->options['query']); 140 | 141 | if (count($arguments) < 1) { 142 | return $this->options; 143 | } 144 | 145 | if ($method == 'get') { 146 | $this->options['query'] = $arguments; 147 | } else { 148 | $this->options['json'] = $arguments; 149 | } 150 | 151 | return $this->options; 152 | } 153 | 154 | /** 155 | * @throws Exception 156 | */ 157 | public function __call(string $method, array $arguments): Collection 158 | { 159 | if (count($arguments) < 1) { 160 | throw new InvalidArgumentException('Magic request methods require a URI and optional options array'); 161 | } 162 | 163 | if (! in_array($method, $this->allowedMethods)) { 164 | throw new BadMethodCallException('Method "'.$method.'" is not supported.'); 165 | } 166 | 167 | $resource = $arguments[0]; 168 | $options = $arguments[1] ?? []; 169 | 170 | return $this->request($resource, $options, $method); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/MailchimpFacade.php: -------------------------------------------------------------------------------- 1 | publishes([ 17 | __DIR__.'/config/mailchimp.php' => config_path('mailchimp.php'), 18 | ]); 19 | } 20 | 21 | /** 22 | * Register bindings in the container. 23 | * 24 | * @return void 25 | */ 26 | public function register() 27 | { 28 | $this->app->bind('Mailchimp\Mailchimp', function ($app) { 29 | $config = $app['config']['mailchimp']; 30 | 31 | return new Mailchimp($config['apikey']); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/config/mailchimp.php: -------------------------------------------------------------------------------- 1 | API keys. Paste the key below. 12 | | 13 | */ 14 | 'apikey' => '', 15 | ]; 16 | --------------------------------------------------------------------------------