1: <?php
2: 3: 4: 5: 6: 7: 8: 9:
10:
11: namespace OpenCloud\Common\Service;
12:
13: use OpenCloud\Common\Base;
14: use OpenCloud\Common\Exceptions;
15: use OpenCloud\Common\Collection\PaginatedIterator;
16: use OpenCloud\Common\Http\Client;
17: use OpenCloud\Common\PersistentObject;
18: use OpenCloud\Common\Http\Message\Formatter;
19: use Guzzle\Http\Url;
20: use Guzzle\Http\Exception\BadResponseException;
21: use Symfony\Component\EventDispatcher\EventSubscriberInterface;
22:
23: 24: 25: 26: 27: 28: 29: 30:
31: abstract class AbstractService extends Base
32: {
33: const DEFAULT_REGION = 'DFW';
34: const DEFAULT_URL_TYPE = 'publicURL';
35:
36: 37: 38:
39: protected $client;
40:
41: 42: 43:
44: private $type;
45:
46: 47: 48:
49: private $name;
50:
51: 52: 53:
54: private $region;
55:
56: 57: 58:
59: private $urlType;
60:
61: 62: 63:
64: private $endpoint;
65:
66: 67: 68:
69: protected $namespaces = array();
70:
71: 72: 73:
74: protected $resources = array();
75:
76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89:
90: public function __construct(Client $client, $type = null, $name = null, $region = null, $urlType = null)
91: {
92: $this->setClient($client);
93:
94: $this->type = $type ?: static::DEFAULT_TYPE;
95: $this->name = $name ?: static::DEFAULT_NAME;
96: $this->region = $region ?: static::DEFAULT_REGION;
97: $this->urlType = $urlType ?: static::DEFAULT_URL_TYPE;
98:
99: $this->endpoint = $this->findEndpoint();
100: $this->client->setBaseUrl($this->getBaseUrl());
101:
102: if ($this instanceof EventSubscriberInterface) {
103: $this->client->getEventDispatcher()->addSubscriber($this);
104: }
105: }
106:
107: 108: 109:
110: public function setClient(Client $client)
111: {
112: $this->client = $client;
113: }
114:
115: 116: 117:
118: public function getClient()
119: {
120: return $this->client;
121: }
122:
123: 124: 125:
126: public function getType()
127: {
128: return $this->type;
129: }
130:
131: 132: 133:
134: public function getRegion()
135: {
136: return $this->region;
137: }
138:
139: 140: 141:
142: public function getName()
143: {
144: return $this->name;
145: }
146:
147: 148: 149:
150: public function getUrlType()
151: {
152: return $this->urlType;
153: }
154:
155: 156: 157:
158: public function getEndpoint()
159: {
160: return $this->endpoint;
161: }
162:
163: 164: 165:
166: public function region()
167: {
168: return $this->getRegion();
169: }
170:
171: 172: 173:
174: public function name()
175: {
176: return $this->name;
177: }
178:
179: 180: 181: 182: 183: 184: 185:
186: public function getUrl($path = null, array $query = array())
187: {
188: return Url::factory($this->getBaseUrl())
189: ->addPath($path)
190: ->setQuery($query);
191: }
192:
193: 194: 195:
196: public function url($path = null, array $query = array())
197: {
198: return $this->getUrl($path, $query);
199: }
200:
201: 202: 203: 204: 205: 206:
207: public function getExtensions()
208: {
209: $ext = $this->getMetaUrl('extensions');
210: return (is_object($ext) && isset($ext->extensions)) ? $ext->extensions : array();
211: }
212:
213: 214: 215: 216: 217:
218: public function limits()
219: {
220: $limits = $this->getMetaUrl('limits');
221: return (is_object($limits)) ? $limits->limits : array();
222: }
223:
224: 225: 226: 227: 228:
229: public function namespaces()
230: {
231: return (isset($this->namespaces) && is_array($this->namespaces))
232: ? $this->namespaces
233: : array();
234: }
235:
236: 237: 238: 239: 240: 241: 242:
243: private function findEndpoint()
244: {
245: if (!$this->getClient()->getCatalog()) {
246: $this->getClient()->authenticate();
247: }
248:
249: $catalog = $this->getClient()->getCatalog();
250:
251:
252: foreach ($catalog->getItems() as $service) {
253: if ($service->hasType($this->type) && $service->hasName($this->name)) {
254: return Endpoint::factory($service->getEndpointFromRegion($this->region));
255: }
256: }
257:
258: throw new Exceptions\EndpointError(sprintf(
259: 'No endpoints for service type [%s], name [%s], region [%s] and urlType [%s]',
260: $this->type,
261: $this->name,
262: $this->region,
263: $this->urlType
264: ));
265: }
266:
267: 268: 269: 270: 271: 272: 273: 274: 275: 276:
277: private function getMetaUrl($resource)
278: {
279: $url = clone $this->getBaseUrl();
280: $url->addPath($resource);
281: try {
282: $response = $this->getClient()->get($url)->send();
283: return Formatter::decode($response);
284: } catch (BadResponseException $e) {
285:
286: return array();
287:
288: }
289: }
290:
291: 292: 293: 294: 295: 296:
297: public function getResources()
298: {
299: return $this->resources;
300: }
301:
302: 303: 304: 305: 306:
307: protected function getCurrentNamespace()
308: {
309: $namespace = get_class($this);
310: return substr($namespace, 0, strrpos($namespace, '\\'));
311: }
312:
313: 314: 315: 316: 317: 318: 319:
320: protected function resolveResourceClass($resourceName)
321: {
322: $className = substr_count($resourceName, '\\')
323: ? $resourceName
324: : $this->getCurrentNamespace() . '\\Resource\\' . ucfirst($resourceName);
325:
326: if (!class_exists($className)) {
327: throw new Exceptions\UnrecognizedServiceError(sprintf(
328: '%s resource does not exist, please try one of the following: %s',
329: $resourceName,
330: implode(', ', $this->getResources())
331: ));
332: }
333:
334: return $className;
335: }
336:
337: 338: 339: 340: 341: 342: 343: 344:
345: public function resource($resourceName, $info = null, $parent = null)
346: {
347: $className = $this->resolveResourceClass($resourceName);
348:
349: $resource = new $className($this);
350: if ($parent) {
351: $resource->setParent($parent);
352: }
353:
354: $resource->populate($info);
355:
356: return $resource;
357: }
358:
359: 360: 361: 362: 363: 364: 365: 366:
367: public function resourceList($resourceName, $url = null, $parent = null)
368: {
369: $className = $this->resolveResourceClass($resourceName);
370: return $this->collection($className, $url, $parent);
371: }
372:
373: 374: 375: 376: 377:
378: public function getBaseUrl()
379: {
380: $url = ($this->urlType == 'publicURL')
381: ? $this->endpoint->getPublicUrl()
382: : $this->endpoint->getPrivateUrl();
383:
384: if ($url === null) {
385: throw new Exceptions\ServiceException(sprintf(
386: 'The base %s could not be found. Perhaps the service '
387: . 'you are using requires a different URL type, or does '
388: . 'not support this region.',
389: $this->urlType
390: ));
391: }
392:
393: return $url;
394: }
395:
396: 397: 398:
399: public function collection($class, $url = null, $parent = null, $data = null)
400: {
401: if (!$parent) {
402: $parent = $this;
403: }
404:
405: $resource = $this->resolveResourceClass($class);
406:
407: if (!$url) {
408: $url = $parent->getUrl($resource::resourceName());
409: }
410:
411: $options = $this->makeResourceIteratorOptions($this->resolveResourceClass($class));
412: $options['baseUrl'] = $url;
413:
414: return PaginatedIterator::factory($parent, $options, $data);
415: }
416:
417: }