1: <?php
2:
3: namespace Guzzle\Service\Builder;
4:
5: use Guzzle\Common\AbstractHasDispatcher;
6: use Guzzle\Http\ClientInterface;
7: use Guzzle\Service\Exception\ServiceBuilderException;
8: use Guzzle\Service\Exception\ServiceNotFoundException;
9: use Symfony\Component\EventDispatcher\EventSubscriberInterface;
10:
11: /**
12: * Service builder to generate service builders and service clients from configuration settings
13: */
14: class ServiceBuilder extends AbstractHasDispatcher implements ServiceBuilderInterface, \ArrayAccess, \Serializable
15: {
16: /**
17: * @var array Service builder configuration data
18: */
19: protected $builderConfig = array();
20:
21: /**
22: * @var array Instantiated client objects
23: */
24: protected $clients = array();
25:
26: /**
27: * @var ServiceBuilderLoader Cached instance of the service builder loader
28: */
29: protected static $cachedFactory;
30:
31: /**
32: * @var array Plugins to attach to each client created by the service builder
33: */
34: protected $plugins = array();
35:
36: /**
37: * Create a new ServiceBuilder using configuration data sourced from an
38: * array, .js|.json or .php file.
39: *
40: * @param array|string $config The full path to an .json|.js or .php file, or an associative array
41: * @param array $globalParameters Array of global parameters to pass to every service as it is instantiated.
42: *
43: * @return ServiceBuilderInterface
44: * @throws ServiceBuilderException if a file cannot be opened
45: * @throws ServiceNotFoundException when trying to extend a missing client
46: */
47: public static function factory($config = null, array $globalParameters = array())
48: {
49: // @codeCoverageIgnoreStart
50: if (!static::$cachedFactory) {
51: static::$cachedFactory = new ServiceBuilderLoader();
52: }
53: // @codeCoverageIgnoreEnd
54:
55: return self::$cachedFactory->load($config, $globalParameters);
56: }
57:
58: /**
59: * Construct a new service builder
60: *
61: * @param array $serviceBuilderConfig Service configuration settings:
62: * - name: Name of the service
63: * - class: Client class to instantiate using a factory method
64: * - params: array of key value pair configuration settings for the builder
65: */
66: public function __construct(array $serviceBuilderConfig)
67: {
68: $this->builderConfig = $serviceBuilderConfig;
69: }
70:
71: /**
72: * {@inheritdoc}
73: */
74: public static function getAllEvents()
75: {
76: return array('service_builder.create_client');
77: }
78:
79: /**
80: * Restores the service builder from JSON
81: *
82: * @param string $serialized JSON data to restore from
83: */
84: public function unserialize($serialized)
85: {
86: $this->builderConfig = json_decode($serialized, true);
87: }
88:
89: /**
90: * Represents the service builder as a string
91: *
92: * @return array
93: */
94: public function serialize()
95: {
96: return json_encode($this->builderConfig);
97: }
98:
99: /**
100: * Attach a plugin to every client created by the builder
101: *
102: * @param EventSubscriberInterface $plugin Plugin to attach to each client
103: *
104: * @return self
105: */
106: public function addGlobalPlugin(EventSubscriberInterface $plugin)
107: {
108: $this->plugins[] = $plugin;
109:
110: return $this;
111: }
112:
113: /**
114: * Get data from the service builder without triggering the building of a service
115: *
116: * @param string $name Name of the service to retrieve
117: *
118: * @return array|null
119: */
120: public function getData($name)
121: {
122: return isset($this->builderConfig[$name]) ? $this->builderConfig[$name] : null;
123: }
124:
125: /**
126: * {@inheritdoc}
127: */
128: public function get($name, $throwAway = false)
129: {
130: if (!isset($this->builderConfig[$name])) {
131: // Check aliases and return a match if found
132: foreach ($this->builderConfig as $actualName => $config) {
133: if (isset($config['alias']) && $config['alias'] == $name) {
134: return $this->get($actualName, $throwAway);
135: }
136: }
137: throw new ServiceNotFoundException('No service is registered as ' . $name);
138: }
139:
140: if (!$throwAway && isset($this->clients[$name])) {
141: return $this->clients[$name];
142: }
143:
144: // Convert references to the actual client
145: foreach ($this->builderConfig[$name]['params'] as &$v) {
146: if (is_string($v) && substr($v, 0, 1) == '{' && substr($v, -1) == '}') {
147: $v = $this->get(trim(trim($v, '{}')));
148: }
149: }
150:
151: // Get the configured parameters and merge in any parameters provided for throw-away clients
152: $config = $this->builderConfig[$name]['params'];
153: if (is_array($throwAway)) {
154: $config = $throwAway + $config;
155: }
156:
157: $class = $this->builderConfig[$name]['class'];
158: $client = $class::factory($config);
159:
160: if (!$throwAway) {
161: $this->clients[$name] = $client;
162: }
163:
164: foreach ($this->plugins as $plugin) {
165: $client->addSubscriber($plugin);
166: }
167:
168: // Dispatch an event letting listeners know a client was created
169: $this->dispatch('service_builder.create_client', array('client' => $client));
170:
171: return $client;
172: }
173:
174: /**
175: * {@inheritdoc}
176: */
177: public function set($key, $service)
178: {
179: $this->builderConfig[$key] = $service;
180:
181: return $this;
182: }
183:
184: /**
185: * Register a client by name with the service builder
186: *
187: * @param string $offset Name of the client to register
188: * @param ClientInterface $value Client to register
189: */
190: public function offsetSet($offset, $value)
191: {
192: $this->set($offset, $value);
193: }
194:
195: /**
196: * Remove a registered client by name
197: *
198: * @param string $offset Client to remove by name
199: */
200: public function offsetUnset($offset)
201: {
202: unset($this->builderConfig[$offset]);
203: }
204:
205: /**
206: * Check if a client is registered with the service builder by name
207: *
208: * @param string $offset Name to check to see if a client exists
209: *
210: * @return bool
211: */
212: public function offsetExists($offset)
213: {
214: return isset($this->builderConfig[$offset]);
215: }
216:
217: /**
218: * Get a registered client by name
219: *
220: * @param string $offset Registered client name to retrieve
221: *
222: * @return ClientInterface
223: */
224: public function offsetGet($offset)
225: {
226: return $this->get($offset);
227: }
228: }
229: