|
1 <?php |
|
2 |
|
3 /** |
|
4 * Zend Framework |
|
5 * |
|
6 * LICENSE |
|
7 * |
|
8 * This source file is subject to the new BSD license that is bundled |
|
9 * with this package in the file LICENSE.txt. |
|
10 * It is also available through the world-wide-web at this URL: |
|
11 * http://framework.zend.com/license/new-bsd |
|
12 * If you did not receive a copy of the license and are unable to |
|
13 * obtain it through the world-wide-web, please send an email |
|
14 * to license@zend.com so we can send you a copy immediately. |
|
15 * |
|
16 * @category Zend |
|
17 * @package Zend_Service |
|
18 * @subpackage Flickr |
|
19 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
20 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
21 * @version $Id: Flickr.php 22598 2010-07-16 21:24:14Z mikaelkael $ |
|
22 */ |
|
23 |
|
24 |
|
25 /** |
|
26 * @category Zend |
|
27 * @package Zend_Service |
|
28 * @subpackage Flickr |
|
29 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
30 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
31 */ |
|
32 class Zend_Service_Flickr |
|
33 { |
|
34 /** |
|
35 * Base URI for the REST client |
|
36 */ |
|
37 const URI_BASE = 'http://www.flickr.com'; |
|
38 |
|
39 /** |
|
40 * Your Flickr API key |
|
41 * |
|
42 * @var string |
|
43 */ |
|
44 public $apiKey; |
|
45 |
|
46 /** |
|
47 * Reference to REST client object |
|
48 * |
|
49 * @var Zend_Rest_Client |
|
50 */ |
|
51 protected $_restClient = null; |
|
52 |
|
53 |
|
54 /** |
|
55 * Performs object initializations |
|
56 * |
|
57 * # Sets up character encoding |
|
58 * # Saves the API key |
|
59 * |
|
60 * @param string $apiKey Your Flickr API key |
|
61 * @return void |
|
62 */ |
|
63 public function __construct($apiKey) |
|
64 { |
|
65 $this->apiKey = (string) $apiKey; |
|
66 } |
|
67 |
|
68 |
|
69 /** |
|
70 * Find Flickr photos by tag. |
|
71 * |
|
72 * Query options include: |
|
73 * |
|
74 * # per_page: how many results to return per query |
|
75 * # page: the starting page offset. first result will be (page - 1) * per_page + 1 |
|
76 * # tag_mode: Either 'any' for an OR combination of tags, |
|
77 * or 'all' for an AND combination. Default is 'any'. |
|
78 * # min_upload_date: Minimum upload date to search on. Date should be a unix timestamp. |
|
79 * # max_upload_date: Maximum upload date to search on. Date should be a unix timestamp. |
|
80 * # min_taken_date: Minimum upload date to search on. Date should be a MySQL datetime. |
|
81 * # max_taken_date: Maximum upload date to search on. Date should be a MySQL datetime. |
|
82 * |
|
83 * @param string|array $query A single tag or an array of tags. |
|
84 * @param array $options Additional parameters to refine your query. |
|
85 * @return Zend_Service_Flickr_ResultSet |
|
86 * @throws Zend_Service_Exception |
|
87 */ |
|
88 public function tagSearch($query, array $options = array()) |
|
89 { |
|
90 static $method = 'flickr.photos.search'; |
|
91 static $defaultOptions = array('per_page' => 10, |
|
92 'page' => 1, |
|
93 'tag_mode' => 'or', |
|
94 'extras' => 'license, date_upload, date_taken, owner_name, icon_server'); |
|
95 |
|
96 $options['tags'] = is_array($query) ? implode(',', $query) : $query; |
|
97 |
|
98 $options = $this->_prepareOptions($method, $options, $defaultOptions); |
|
99 |
|
100 $this->_validateTagSearch($options); |
|
101 |
|
102 // now search for photos |
|
103 $restClient = $this->getRestClient(); |
|
104 $restClient->getHttpClient()->resetParameters(); |
|
105 $response = $restClient->restGet('/services/rest/', $options); |
|
106 |
|
107 if ($response->isError()) { |
|
108 /** |
|
109 * @see Zend_Service_Exception |
|
110 */ |
|
111 require_once 'Zend/Service/Exception.php'; |
|
112 throw new Zend_Service_Exception('An error occurred sending request. Status code: ' |
|
113 . $response->getStatus()); |
|
114 } |
|
115 |
|
116 $dom = new DOMDocument(); |
|
117 $dom->loadXML($response->getBody()); |
|
118 |
|
119 self::_checkErrors($dom); |
|
120 |
|
121 /** |
|
122 * @see Zend_Service_Flickr_ResultSet |
|
123 */ |
|
124 require_once 'Zend/Service/Flickr/ResultSet.php'; |
|
125 return new Zend_Service_Flickr_ResultSet($dom, $this); |
|
126 } |
|
127 |
|
128 |
|
129 /** |
|
130 * Finds photos by a user's username or email. |
|
131 * |
|
132 * Additional query options include: |
|
133 * |
|
134 * # per_page: how many results to return per query |
|
135 * # page: the starting page offset. first result will be (page - 1) * per_page + 1 |
|
136 * # min_upload_date: Minimum upload date to search on. Date should be a unix timestamp. |
|
137 * # max_upload_date: Maximum upload date to search on. Date should be a unix timestamp. |
|
138 * # min_taken_date: Minimum upload date to search on. Date should be a MySQL datetime. |
|
139 * # max_taken_date: Maximum upload date to search on. Date should be a MySQL datetime. |
|
140 * |
|
141 * @param string $query username or email |
|
142 * @param array $options Additional parameters to refine your query. |
|
143 * @return Zend_Service_Flickr_ResultSet |
|
144 * @throws Zend_Service_Exception |
|
145 */ |
|
146 public function userSearch($query, array $options = null) |
|
147 { |
|
148 static $method = 'flickr.people.getPublicPhotos'; |
|
149 static $defaultOptions = array('per_page' => 10, |
|
150 'page' => 1, |
|
151 'extras' => 'license, date_upload, date_taken, owner_name, icon_server'); |
|
152 |
|
153 |
|
154 // can't access by username, must get ID first |
|
155 if (strchr($query, '@')) { |
|
156 // optimistically hope this is an email |
|
157 $options['user_id'] = $this->getIdByEmail($query); |
|
158 } else { |
|
159 // we can safely ignore this exception here |
|
160 $options['user_id'] = $this->getIdByUsername($query); |
|
161 } |
|
162 |
|
163 $options = $this->_prepareOptions($method, $options, $defaultOptions); |
|
164 $this->_validateUserSearch($options); |
|
165 |
|
166 // now search for photos |
|
167 $restClient = $this->getRestClient(); |
|
168 $restClient->getHttpClient()->resetParameters(); |
|
169 $response = $restClient->restGet('/services/rest/', $options); |
|
170 |
|
171 if ($response->isError()) { |
|
172 /** |
|
173 * @see Zend_Service_Exception |
|
174 */ |
|
175 require_once 'Zend/Service/Exception.php'; |
|
176 throw new Zend_Service_Exception('An error occurred sending request. Status code: ' |
|
177 . $response->getStatus()); |
|
178 } |
|
179 |
|
180 $dom = new DOMDocument(); |
|
181 $dom->loadXML($response->getBody()); |
|
182 |
|
183 self::_checkErrors($dom); |
|
184 |
|
185 /** |
|
186 * @see Zend_Service_Flickr_ResultSet |
|
187 */ |
|
188 require_once 'Zend/Service/Flickr/ResultSet.php'; |
|
189 return new Zend_Service_Flickr_ResultSet($dom, $this); |
|
190 } |
|
191 |
|
192 /** |
|
193 * Finds photos in a group's pool. |
|
194 * |
|
195 * @param string $query group id |
|
196 * @param array $options Additional parameters to refine your query. |
|
197 * @return Zend_Service_Flickr_ResultSet |
|
198 * @throws Zend_Service_Exception |
|
199 */ |
|
200 public function groupPoolGetPhotos($query, array $options = array()) |
|
201 { |
|
202 static $method = 'flickr.groups.pools.getPhotos'; |
|
203 static $defaultOptions = array('per_page' => 10, |
|
204 'page' => 1, |
|
205 'extras' => 'license, date_upload, date_taken, owner_name, icon_server'); |
|
206 |
|
207 if (empty($query) || !is_string($query)) { |
|
208 /** |
|
209 * @see Zend_Service_Exception |
|
210 */ |
|
211 require_once 'Zend/Service/Exception.php'; |
|
212 throw new Zend_Service_Exception('You must supply a group id'); |
|
213 } |
|
214 |
|
215 $options['group_id'] = $query; |
|
216 |
|
217 $options = $this->_prepareOptions($method, $options, $defaultOptions); |
|
218 |
|
219 $this->_validateGroupPoolGetPhotos($options); |
|
220 |
|
221 // now search for photos |
|
222 $restClient = $this->getRestClient(); |
|
223 $restClient->getHttpClient()->resetParameters(); |
|
224 $response = $restClient->restGet('/services/rest/', $options); |
|
225 |
|
226 if ($response->isError()) { |
|
227 /** |
|
228 * @see Zend_Service_Exception |
|
229 */ |
|
230 require_once 'Zend/Service/Exception.php'; |
|
231 throw new Zend_Service_Exception('An error occurred sending request. Status code: ' |
|
232 . $response->getStatus()); |
|
233 } |
|
234 |
|
235 $dom = new DOMDocument(); |
|
236 $dom->loadXML($response->getBody()); |
|
237 |
|
238 self::_checkErrors($dom); |
|
239 |
|
240 /** |
|
241 * @see Zend_Service_Flickr_ResultSet |
|
242 */ |
|
243 require_once 'Zend/Service/Flickr/ResultSet.php'; |
|
244 return new Zend_Service_Flickr_ResultSet($dom, $this); |
|
245 } |
|
246 |
|
247 |
|
248 |
|
249 /** |
|
250 * Utility function to find Flickr User IDs for usernames. |
|
251 * |
|
252 * (You can only find a user's photo with their NSID.) |
|
253 * |
|
254 * @param string $username the username |
|
255 * @return string the NSID (userid) |
|
256 * @throws Zend_Service_Exception |
|
257 */ |
|
258 public function getIdByUsername($username) |
|
259 { |
|
260 static $method = 'flickr.people.findByUsername'; |
|
261 |
|
262 $options = array('api_key' => $this->apiKey, 'method' => $method, 'username' => (string) $username); |
|
263 |
|
264 if (empty($username)) { |
|
265 /** |
|
266 * @see Zend_Service_Exception |
|
267 */ |
|
268 require_once 'Zend/Service/Exception.php'; |
|
269 throw new Zend_Service_Exception('You must supply a username'); |
|
270 } |
|
271 |
|
272 $restClient = $this->getRestClient(); |
|
273 $restClient->getHttpClient()->resetParameters(); |
|
274 $response = $restClient->restGet('/services/rest/', $options); |
|
275 |
|
276 if ($response->isError()) { |
|
277 /** |
|
278 * @see Zend_Service_Exception |
|
279 */ |
|
280 require_once 'Zend/Service/Exception.php'; |
|
281 throw new Zend_Service_Exception('An error occurred sending request. Status code: ' |
|
282 . $response->getStatus()); |
|
283 } |
|
284 |
|
285 $dom = new DOMDocument(); |
|
286 $dom->loadXML($response->getBody()); |
|
287 self::_checkErrors($dom); |
|
288 $xpath = new DOMXPath($dom); |
|
289 return (string) $xpath->query('//user')->item(0)->getAttribute('id'); |
|
290 } |
|
291 |
|
292 |
|
293 /** |
|
294 * Utility function to find Flickr User IDs for emails. |
|
295 * |
|
296 * (You can only find a user's photo with their NSID.) |
|
297 * |
|
298 * @param string $email the email |
|
299 * @return string the NSID (userid) |
|
300 * @throws Zend_Service_Exception |
|
301 */ |
|
302 public function getIdByEmail($email) |
|
303 { |
|
304 static $method = 'flickr.people.findByEmail'; |
|
305 |
|
306 if (empty($email)) { |
|
307 /** |
|
308 * @see Zend_Service_Exception |
|
309 */ |
|
310 require_once 'Zend/Service/Exception.php'; |
|
311 throw new Zend_Service_Exception('You must supply an e-mail address'); |
|
312 } |
|
313 |
|
314 $options = array('api_key' => $this->apiKey, 'method' => $method, 'find_email' => (string) $email); |
|
315 |
|
316 $restClient = $this->getRestClient(); |
|
317 $restClient->getHttpClient()->resetParameters(); |
|
318 $response = $restClient->restGet('/services/rest/', $options); |
|
319 |
|
320 if ($response->isError()) { |
|
321 /** |
|
322 * @see Zend_Service_Exception |
|
323 */ |
|
324 require_once 'Zend/Service/Exception.php'; |
|
325 throw new Zend_Service_Exception('An error occurred sending request. Status code: ' |
|
326 . $response->getStatus()); |
|
327 } |
|
328 |
|
329 $dom = new DOMDocument(); |
|
330 $dom->loadXML($response->getBody()); |
|
331 self::_checkErrors($dom); |
|
332 $xpath = new DOMXPath($dom); |
|
333 return (string) $xpath->query('//user')->item(0)->getAttribute('id'); |
|
334 } |
|
335 |
|
336 |
|
337 /** |
|
338 * Returns Flickr photo details by for the given photo ID |
|
339 * |
|
340 * @param string $id the NSID |
|
341 * @return array of Zend_Service_Flickr_Image, details for the specified image |
|
342 * @throws Zend_Service_Exception |
|
343 */ |
|
344 public function getImageDetails($id) |
|
345 { |
|
346 static $method = 'flickr.photos.getSizes'; |
|
347 |
|
348 if (empty($id)) { |
|
349 /** |
|
350 * @see Zend_Service_Exception |
|
351 */ |
|
352 require_once 'Zend/Service/Exception.php'; |
|
353 throw new Zend_Service_Exception('You must supply a photo ID'); |
|
354 } |
|
355 |
|
356 $options = array('api_key' => $this->apiKey, 'method' => $method, 'photo_id' => $id); |
|
357 |
|
358 $restClient = $this->getRestClient(); |
|
359 $restClient->getHttpClient()->resetParameters(); |
|
360 $response = $restClient->restGet('/services/rest/', $options); |
|
361 |
|
362 $dom = new DOMDocument(); |
|
363 $dom->loadXML($response->getBody()); |
|
364 $xpath = new DOMXPath($dom); |
|
365 self::_checkErrors($dom); |
|
366 $retval = array(); |
|
367 /** |
|
368 * @see Zend_Service_Flickr_Image |
|
369 */ |
|
370 require_once 'Zend/Service/Flickr/Image.php'; |
|
371 foreach ($xpath->query('//size') as $size) { |
|
372 $label = (string) $size->getAttribute('label'); |
|
373 $retval[$label] = new Zend_Service_Flickr_Image($size); |
|
374 } |
|
375 |
|
376 return $retval; |
|
377 } |
|
378 |
|
379 |
|
380 /** |
|
381 * Returns a reference to the REST client, instantiating it if necessary |
|
382 * |
|
383 * @return Zend_Rest_Client |
|
384 */ |
|
385 public function getRestClient() |
|
386 { |
|
387 if (null === $this->_restClient) { |
|
388 /** |
|
389 * @see Zend_Rest_Client |
|
390 */ |
|
391 require_once 'Zend/Rest/Client.php'; |
|
392 $this->_restClient = new Zend_Rest_Client(self::URI_BASE); |
|
393 } |
|
394 |
|
395 return $this->_restClient; |
|
396 } |
|
397 |
|
398 |
|
399 /** |
|
400 * Validate User Search Options |
|
401 * |
|
402 * @param array $options |
|
403 * @return void |
|
404 * @throws Zend_Service_Exception |
|
405 */ |
|
406 protected function _validateUserSearch(array $options) |
|
407 { |
|
408 $validOptions = array('api_key', 'method', 'user_id', 'per_page', 'page', 'extras', 'min_upload_date', |
|
409 'min_taken_date', 'max_upload_date', 'max_taken_date', 'safe_search'); |
|
410 |
|
411 $this->_compareOptions($options, $validOptions); |
|
412 |
|
413 /** |
|
414 * @see Zend_Validate_Between |
|
415 */ |
|
416 require_once 'Zend/Validate/Between.php'; |
|
417 $between = new Zend_Validate_Between(1, 500, true); |
|
418 if (!$between->isValid($options['per_page'])) { |
|
419 /** |
|
420 * @see Zend_Service_Exception |
|
421 */ |
|
422 require_once 'Zend/Service/Exception.php'; |
|
423 throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option'); |
|
424 } |
|
425 |
|
426 /** |
|
427 * @see Zend_Validate_Int |
|
428 */ |
|
429 require_once 'Zend/Validate/Int.php'; |
|
430 $int = new Zend_Validate_Int(); |
|
431 if (!$int->isValid($options['page'])) { |
|
432 /** |
|
433 * @see Zend_Service_Exception |
|
434 */ |
|
435 require_once 'Zend/Service/Exception.php'; |
|
436 throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option'); |
|
437 } |
|
438 |
|
439 // validate extras, which are delivered in csv format |
|
440 if ($options['extras']) { |
|
441 $extras = explode(',', $options['extras']); |
|
442 $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server'); |
|
443 foreach($extras as $extra) { |
|
444 /** |
|
445 * @todo The following does not do anything [yet], so it is commented out. |
|
446 */ |
|
447 //in_array(trim($extra), $validExtras); |
|
448 } |
|
449 } |
|
450 } |
|
451 |
|
452 |
|
453 /** |
|
454 * Validate Tag Search Options |
|
455 * |
|
456 * @param array $options |
|
457 * @return void |
|
458 * @throws Zend_Service_Exception |
|
459 */ |
|
460 protected function _validateTagSearch(array $options) |
|
461 { |
|
462 $validOptions = array('method', 'api_key', 'user_id', 'tags', 'tag_mode', 'text', 'min_upload_date', |
|
463 'max_upload_date', 'min_taken_date', 'max_taken_date', 'license', 'sort', |
|
464 'privacy_filter', 'bbox', 'accuracy', 'safe_search', 'content_type', 'machine_tags', |
|
465 'machine_tag_mode', 'group_id', 'contacts', 'woe_id', 'place_id', 'media', 'has_geo', |
|
466 'geo_context', 'lat', 'lon', 'radius', 'radius_units', 'is_commons', 'is_gallery', |
|
467 'extras', 'per_page', 'page'); |
|
468 |
|
469 $this->_compareOptions($options, $validOptions); |
|
470 |
|
471 /** |
|
472 * @see Zend_Validate_Between |
|
473 */ |
|
474 require_once 'Zend/Validate/Between.php'; |
|
475 $between = new Zend_Validate_Between(1, 500, true); |
|
476 if (!$between->isValid($options['per_page'])) { |
|
477 /** |
|
478 * @see Zend_Service_Exception |
|
479 */ |
|
480 require_once 'Zend/Service/Exception.php'; |
|
481 throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option'); |
|
482 } |
|
483 |
|
484 /** |
|
485 * @see Zend_Validate_Int |
|
486 */ |
|
487 require_once 'Zend/Validate/Int.php'; |
|
488 $int = new Zend_Validate_Int(); |
|
489 if (!$int->isValid($options['page'])) { |
|
490 /** |
|
491 * @see Zend_Service_Exception |
|
492 */ |
|
493 require_once 'Zend/Service/Exception.php'; |
|
494 throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option'); |
|
495 } |
|
496 |
|
497 // validate extras, which are delivered in csv format |
|
498 if ($options['extras']) { |
|
499 $extras = explode(',', $options['extras']); |
|
500 $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server'); |
|
501 foreach($extras as $extra) { |
|
502 /** |
|
503 * @todo The following does not do anything [yet], so it is commented out. |
|
504 */ |
|
505 //in_array(trim($extra), $validExtras); |
|
506 } |
|
507 } |
|
508 |
|
509 } |
|
510 |
|
511 |
|
512 /** |
|
513 * Validate Group Search Options |
|
514 * |
|
515 * @param array $options |
|
516 * @throws Zend_Service_Exception |
|
517 * @return void |
|
518 */ |
|
519 protected function _validateGroupPoolGetPhotos(array $options) |
|
520 { |
|
521 $validOptions = array('api_key', 'tags', 'method', 'group_id', 'per_page', 'page', 'extras', 'user_id'); |
|
522 |
|
523 $this->_compareOptions($options, $validOptions); |
|
524 |
|
525 /** |
|
526 * @see Zend_Validate_Between |
|
527 */ |
|
528 require_once 'Zend/Validate/Between.php'; |
|
529 $between = new Zend_Validate_Between(1, 500, true); |
|
530 if (!$between->isValid($options['per_page'])) { |
|
531 /** |
|
532 * @see Zend_Service_Exception |
|
533 */ |
|
534 require_once 'Zend/Service/Exception.php'; |
|
535 throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option'); |
|
536 } |
|
537 |
|
538 /** |
|
539 * @see Zend_Validate_Int |
|
540 */ |
|
541 require_once 'Zend/Validate/Int.php'; |
|
542 $int = new Zend_Validate_Int(); |
|
543 |
|
544 if (!$int->isValid($options['page'])) { |
|
545 /** |
|
546 * @see Zend_Service_Exception |
|
547 */ |
|
548 require_once 'Zend/Service/Exception.php'; |
|
549 throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option'); |
|
550 } |
|
551 |
|
552 // validate extras, which are delivered in csv format |
|
553 if (isset($options['extras'])) { |
|
554 $extras = explode(',', $options['extras']); |
|
555 $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server'); |
|
556 foreach($extras as $extra) { |
|
557 /** |
|
558 * @todo The following does not do anything [yet], so it is commented out. |
|
559 */ |
|
560 //in_array(trim($extra), $validExtras); |
|
561 } |
|
562 } |
|
563 } |
|
564 |
|
565 |
|
566 /** |
|
567 * Throws an exception if and only if the response status indicates a failure |
|
568 * |
|
569 * @param DOMDocument $dom |
|
570 * @return void |
|
571 * @throws Zend_Service_Exception |
|
572 */ |
|
573 protected static function _checkErrors(DOMDocument $dom) |
|
574 { |
|
575 if ($dom->documentElement->getAttribute('stat') === 'fail') { |
|
576 $xpath = new DOMXPath($dom); |
|
577 $err = $xpath->query('//err')->item(0); |
|
578 /** |
|
579 * @see Zend_Service_Exception |
|
580 */ |
|
581 require_once 'Zend/Service/Exception.php'; |
|
582 throw new Zend_Service_Exception('Search failed due to error: ' . $err->getAttribute('msg') |
|
583 . ' (error #' . $err->getAttribute('code') . ')'); |
|
584 } |
|
585 } |
|
586 |
|
587 |
|
588 /** |
|
589 * Prepare options for the request |
|
590 * |
|
591 * @param string $method Flickr Method to call |
|
592 * @param array $options User Options |
|
593 * @param array $defaultOptions Default Options |
|
594 * @return array Merged array of user and default/required options |
|
595 */ |
|
596 protected function _prepareOptions($method, array $options, array $defaultOptions) |
|
597 { |
|
598 $options['method'] = (string) $method; |
|
599 $options['api_key'] = $this->apiKey; |
|
600 |
|
601 return array_merge($defaultOptions, $options); |
|
602 } |
|
603 |
|
604 |
|
605 /** |
|
606 * Throws an exception if and only if any user options are invalid |
|
607 * |
|
608 * @param array $options User options |
|
609 * @param array $validOptions Valid options |
|
610 * @return void |
|
611 * @throws Zend_Service_Exception |
|
612 */ |
|
613 protected function _compareOptions(array $options, array $validOptions) |
|
614 { |
|
615 $difference = array_diff(array_keys($options), $validOptions); |
|
616 if ($difference) { |
|
617 /** |
|
618 * @see Zend_Service_Exception |
|
619 */ |
|
620 require_once 'Zend/Service/Exception.php'; |
|
621 throw new Zend_Service_Exception('The following parameters are invalid: ' . implode(',', $difference)); |
|
622 } |
|
623 } |
|
624 } |
|
625 |