Overview

Namespaces

  • Crunchmail
    • Collections
    • Entities
    • Exception
    • PHPUnit
    • Resources

Classes

  • Crunchmail\Client
  • Crunchmail\Collections\GenericCollection
  • Crunchmail\Entities\AttachmentEntity
  • Crunchmail\Entities\ContactEntity
  • Crunchmail\Entities\ContactListEntity
  • Crunchmail\Entities\ContactQueueEntity
  • Crunchmail\Entities\DomainEntity
  • Crunchmail\Entities\GenericEntity
  • Crunchmail\Entities\MessageEntity
  • Crunchmail\Entities\RecipientEntity
  • Crunchmail\PHPUnit\IsEntityConstraint
  • Crunchmail\PHPUnit\IsGenericCollectionConstraint
  • Crunchmail\PHPUnit\IsGenericEntityConstraint
  • Crunchmail\PHPUnit\IsGenericResourceConstraint
  • Crunchmail\PHPUnit\IsResourceConstraint
  • Crunchmail\PHPUnit\TestCase
  • Crunchmail\Resources\DomainsResource
  • Crunchmail\Resources\GenericResource
  • Crunchmail\Resources\PreviewSendResource

Exceptions

  • Crunchmail\Exception\ApiException
  • Overview
  • Namespace
  • Class
  1: <?php
  2: /**
  3:  * Handle crunchmail REST API in php
  4:  *
  5:  * PHP version 5.5+
  6:  *
  7:  * @author    Yannick Huerre <dev@sheoak.fr>
  8:  * @copyright 2015 (c) Oasiswork
  9:  * @license   https://opensource.org/licenses/MIT MIT
 10:  *
 11:  * @link https://github.com/crunchmail/crunchmail-client-php
 12:  * @link http://docs.guzzlephp.org/en/latest/
 13:  *
 14:  * @todo check $message->bounces (bounce resource)
 15:  * @todo check $message->spam (spam resource)
 16:  * @todo check $message->stats (stat resource)
 17:  * @todo implements $message->archive (archive_url resource)
 18:  * @todo implements forbidden resources list for entities
 19:  */
 20: 
 21: namespace Crunchmail;
 22: 
 23: /**
 24:  * Crunchmail\Client main class
 25:  */
 26: class Client extends \GuzzleHttp\Client
 27: {
 28:     /**
 29:      * Allowed paths and mapping to api resource path
 30:      * ex: $client->recipients will access path /mails
 31:      *
 32:      * @var array
 33:      */
 34:     public static $paths = [
 35:         'recipients'  => 'mails',
 36:         "optouts"     => 'opt-outs'
 37:     ];
 38: 
 39:     /**
 40:      * Plural / Singular names of entites
 41:      * This is used to generate class name that need singular form
 42:      *
 43:      * @var array
 44:      */
 45:     public static $entities = [
 46:         'categories'   => 'category',
 47:         'preview'      => 'preview',
 48:         'lists'        => 'contactList'
 49:     ];
 50: 
 51:     /**
 52:      * @var array
 53:      */
 54:     private $config;
 55: 
 56:     /**
 57:      * List of authorized methods on client.
 58:      * ex: $client->get($url);
 59:      *
 60:      * @var array
 61:      */
 62:     public static $methods = [
 63:         'get',
 64:         'delete',
 65:         'head',
 66:         'options',
 67:         'patch',
 68:         'post',
 69:         'put'
 70:         //'request' // request is disable for now, not implemented
 71:     ];
 72: 
 73:     /**
 74:      * Default request format
 75:      *
 76:      * @param string
 77:      */
 78:     public $format = 'json';
 79: 
 80:     /**
 81:      * Default headers
 82:      *
 83:      * @param array
 84:      */
 85:     public $headers = [];
 86: 
 87:     /**
 88:       * Initilialize the client, extends guzzle constructor
 89:       *
 90:       * @param array $config API configuration
 91:       *
 92:       * @return object
 93:      */
 94:     public function __construct(array $config = [])
 95:     {
 96:         if (!isset($config['base_uri']))
 97:         {
 98:             throw new \RuntimeException('base_uri is missing in configuration');
 99:         }
100: 
101:         if (!isset($config['token_uri']))
102:         {
103:             throw new \RuntimeException('token_uri is missing in configuration');
104:         }
105: 
106:         $this->config = $config;
107: 
108:         return parent::__construct($config);
109:     }
110: 
111:     /**
112:      * Create a resource when accessing client properties and returns it
113:      *
114:      * Example:
115:      * $client->messages
116:      * $messageEntity->recipients
117:      *
118:      * @param string $name property
119:      *
120:      * @return Crunchmail\Resources\GenericResource
121:      */
122:     public function __get($name)
123:     {
124:         return $this->$name = $this->createResource($name);
125:     }
126: 
127:     /**
128:      * Translate resource to class name, camelcased
129:      *
130:      * @param string $str resource name
131:      *
132:      * @return string
133:      */
134:     private function toCamelCase($str)
135:     {
136:         return str_replace(' ', '', ucwords(str_replace('_', ' ', $str)));
137:     }
138: 
139:     /**
140:      * Create a resource depending on name
141:      *
142:      * If a specific class exists for this type of ressource (ie:
143:      * attachmentResource) then it will be used.
144:      *
145:      * Forcing an url is usefull when creating a sub-resource from an
146:      * entity object, because the base url is then specific
147:      *
148:      * @param string $name name of the resource (ie: attachments)
149:      * @param string $url  force an url for the resource
150:      *
151:      * @return mixed
152:      */
153:     public function createResource($name, $url = '')
154:     {
155:         $camelCase = $this->toCamelCase($name);
156: 
157:         // TODO: find a way to make namespace "use" works with this
158:         $classPrefix = '\\Crunchmail\\Resources\\';
159:         $className = $classPrefix . $camelCase . 'Resource';
160: 
161:         if (!class_exists($className))
162:         {
163:             $className = $classPrefix . 'GenericResource';
164:         }
165: 
166:         return new $className($this, $name, $url);
167:     }
168: 
169:     /**
170:      * Convert resource path if map is found, path otherwise
171:      *
172:      * @param string $path
173:      *
174:      * @return string
175:      */
176:     public function mapPath($path)
177:     {
178:         return isset(self::$paths[$path]) ? self::$paths[$path] : $path;
179:     }
180: 
181:     /**
182:      * Request the API with the given method and params.
183:      *
184:      * This will execute a guzzle call and catch any guzzle exception.
185:      * In that case the values must be in the format expected from guzzle
186:      *
187:      * @param string  $method    method to test
188:      * @param string  $url       url id
189:      * @param array   $values    data
190:      * @param array   $filters   filters to apply
191:      *
192:      * @return stdClass
193:      *
194:      * @link http://docs.guzzlephp.org/en/latest/quickstart.html?highlight=multipart#sending-form-files
195:      * @link http://docs.guzzlephp.org/en/latest/request-options.html?highlight=query#query
196:      *
197:      * @todo Refactor to match guzzle format ( request() )
198:      */
199:     //public function apiRequest($method, $url = '', $values = [], $filters = [])
200:     public function apiRequest($method, $url = '', $values = [], $filters = [])
201:     {
202:         $parse = parse_url($url);
203: 
204:         // if url contains a query string, we have to merge it to avoid
205:         // any conflict with filters
206:         if (isset($parse['query']))
207:         {
208:             $query = $parse['query'];
209:             parse_str($query, $output);
210:             $filters = array_merge($filters, $output);
211:         }
212: 
213:         try
214:         {
215:             // TODO: merge headers or use guzzle default on client?
216:             // making the guzzle call, json or multipart
217:             $result = $this->$method($url, [
218:                 $this->format   => $values,
219:                 'query'         => $filters,
220:                 'headers'       => $this->headers
221:             ]);
222:         }
223:         catch (\Exception $e)
224:         {
225:             $this->catchGuzzleException($e);
226:         }
227: 
228:         // TODO: handle non JSON response
229:         return json_decode((string) $result->getBody());
230:     }
231: 
232:     /**
233:      * Return an auth token from credentials
234:      *
235:      * @param string $identifier login
236:      * @param string $password   password
237:      * @return string
238:      */
239:     public function getTokenFromCredentials($identifier, $password)
240:     {
241:         return $this->getToken([
242:             'identifier' => $identifier,
243:             'password'   => $password
244:         ]);
245:     }
246: 
247:     /**
248:      * Return an auth token from given parameters
249:      *
250:      * @param string $params parameters to post
251:      * @return string
252:      */
253:     public function getToken(array $params = null)
254:     {
255:         if (is_null($params))
256:         {
257:             if (!isset($this->config['auth']) || count($this->config['auth']) < 2)
258:             {
259:                 throw new \RuntimeException('auth parameters are missing');
260:             }
261: 
262:             $params = ['api_key' => $this->config['auth'][1] ];
263:         }
264: 
265:         $result = $this->apiRequest('post', $this->config['token_uri'], $params);
266:         return isset($result->token) ? $result->token : null;
267:     }
268: 
269:     /**
270:      * Catch all guzzle exception types and execute proper action
271:      *
272:      * @param mixed $e guzzle exception
273:      *
274:      * @return null
275:      */
276:     protected function catchGuzzleException($exc)
277:     {
278:         // not a guzzle exception
279:         if (strpos(get_class($exc), 'GuzzleHttp\\') !== 0)
280:         {
281:             throw $exc;
282:         }
283: 
284:         // guzzle exceptions
285:         throw new Exception\ApiException($exc->getMessage(), $exc->getCode(), $exc);
286:     }
287: }
288: 
API documentation generated by ApiGen