source: trunk/www.guidonia.net/wp/wp-content/plugins/webtv/Drivers/Zend/Gdata/App.php@ 44

Last change on this file since 44 was 44, checked in by luciano, 14 years ago
File size: 42.1 KB
Line 
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_Gdata
18 * @subpackage App
19 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
20 * @license http://framework.zend.com/license/new-bsd New BSD License
21 */
22
23/**
24 * Zend_Gdata_Feed
25 */
26require_once 'Zend/Gdata/Feed.php';
27
28/**
29 * Zend_Gdata_Http_Client
30 */
31require_once 'Zend/Http/Client.php';
32
33/**
34 * Zend_Version
35 */
36require_once 'Zend/Version.php';
37
38/**
39 * Zend_Gdata_App_MediaSource
40 */
41require_once 'Zend/Gdata/App/MediaSource.php';
42
43/**
44 * Provides Atom Publishing Protocol (APP) functionality. This class and all
45 * other components of Zend_Gdata_App are designed to work independently from
46 * other Zend_Gdata components in order to interact with generic APP services.
47 *
48 * @category Zend
49 * @package Zend_Gdata
50 * @subpackage App
51 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
52 * @license http://framework.zend.com/license/new-bsd New BSD License
53 */
54class Zend_Gdata_App
55{
56
57 /** Default major protocol version.
58 *
59 * @see _majorProtocolVersion
60 */
61 const DEFAULT_MAJOR_PROTOCOL_VERSION = 1;
62
63 /** Default minor protocol version.
64 *
65 * @see _minorProtocolVersion
66 */
67 const DEFAULT_MINOR_PROTOCOL_VERSION = null;
68
69 /**
70 * Client object used to communicate
71 *
72 * @var Zend_Http_Client
73 */
74 protected $_httpClient;
75
76 /**
77 * Client object used to communicate in static context
78 *
79 * @var Zend_Http_Client
80 */
81 protected static $_staticHttpClient = null;
82
83 /**
84 * Override HTTP PUT and DELETE request methods?
85 *
86 * @var boolean
87 */
88 protected static $_httpMethodOverride = false;
89
90 /**
91 * Enable gzipped responses?
92 *
93 * @var boolean
94 */
95 protected static $_gzipEnabled = false;
96
97 /**
98 * Use verbose exception messages. In the case of HTTP errors,
99 * use the body of the HTTP response in the exception message.
100 *
101 * @var boolean
102 */
103 protected static $_verboseExceptionMessages = true;
104
105 /**
106 * Default URI to which to POST.
107 *
108 * @var string
109 */
110 protected $_defaultPostUri = null;
111
112 /**
113 * Packages to search for classes when using magic __call method, in order.
114 *
115 * @var array
116 */
117 protected $_registeredPackages = array(
118 'Zend_Gdata_App_Extension',
119 'Zend_Gdata_App');
120
121 /**
122 * Maximum number of redirects to follow during HTTP operations
123 *
124 * @var int
125 */
126 protected static $_maxRedirects = 5;
127
128 /**
129 * Indicates the major protocol version that should be used.
130 * At present, recognized values are either 1 or 2. However, any integer
131 * value >= 1 is considered valid.
132 *
133 * Under most circumtances, this will be automatically set by
134 * Zend_Gdata_App subclasses.
135 *
136 * @see setMajorProtocolVersion()
137 * @see getMajorProtocolVersion()
138 */
139 protected $_majorProtocolVersion;
140
141 /**
142 * Indicates the minor protocol version that should be used. Can be set
143 * to either an integer >= 0, or NULL if no minor version should be sent
144 * to the server.
145 *
146 * At present, this field is not used by any Google services, but may be
147 * used in the future.
148 *
149 * Under most circumtances, this will be automatically set by
150 * Zend_Gdata_App subclasses.
151 *
152 * @see setMinorProtocolVersion()
153 * @see getMinorProtocolVersion()
154 */
155 protected $_minorProtocolVersion;
156
157 /**
158 * Whether we want to use XML to object mapping when fetching data.
159 *
160 * @var boolean
161 */
162 protected $_useObjectMapping = true;
163
164 /**
165 * Create Gdata object
166 *
167 * @param Zend_Http_Client $client
168 * @param string $applicationId
169 */
170 public function __construct($client = null, $applicationId = 'MyCompany-MyApp-1.0')
171 {
172 $this->setHttpClient($client, $applicationId);
173 // Set default protocol version. Subclasses should override this as
174 // needed once a given service supports a new version.
175 $this->setMajorProtocolVersion(self::DEFAULT_MAJOR_PROTOCOL_VERSION);
176 $this->setMinorProtocolVersion(self::DEFAULT_MINOR_PROTOCOL_VERSION);
177 }
178
179 /**
180 * Adds a Zend Framework package to the $_registeredPackages array.
181 * This array is searched when using the magic __call method below
182 * to instantiante new objects.
183 *
184 * @param string $name The name of the package (eg Zend_Gdata_App)
185 * @return void
186 */
187 public function registerPackage($name)
188 {
189 array_unshift($this->_registeredPackages, $name);
190 }
191
192 /**
193 * Retrieve feed as string or object
194 *
195 * @param string $uri The uri from which to retrieve the feed
196 * @param string $className The class which is used as the return type
197 * @return string|Zend_Gdata_App_Feed Returns string only if the object
198 * mapping has been disabled explicitly
199 * by passing false to the
200 * useObjectMapping() function.
201 */
202 public function getFeed($uri, $className='Zend_Gdata_App_Feed')
203 {
204 return $this->importUrl($uri, $className, null);
205 }
206
207 /**
208 * Retrieve entry as string or object
209 *
210 * @param string $uri
211 * @param string $className The class which is used as the return type
212 * @return string|Zend_Gdata_App_Entry Returns string only if the object
213 * mapping has been disabled explicitly
214 * by passing false to the
215 * useObjectMapping() function.
216 */
217 public function getEntry($uri, $className='Zend_Gdata_App_Entry')
218 {
219 return $this->importUrl($uri, $className, null);
220 }
221
222 /**
223 * Get the Zend_Http_Client object used for communication
224 *
225 * @return Zend_Http_Client
226 */
227 public function getHttpClient()
228 {
229 return $this->_httpClient;
230 }
231
232 /**
233 * Set the Zend_Http_Client object used for communication
234 *
235 * @param Zend_Http_Client $client The client to use for communication
236 * @throws Zend_Gdata_App_HttpException
237 * @return Zend_Gdata_App Provides a fluent interface
238 */
239 public function setHttpClient($client,
240 $applicationId = 'MyCompany-MyApp-1.0')
241 {
242 if ($client === null) {
243 $client = new Zend_Http_Client();
244 }
245 if (!$client instanceof Zend_Http_Client) {
246 require_once 'Zend/Gdata/App/HttpException.php';
247 throw new Zend_Gdata_App_HttpException(
248 'Argument is not an instance of Zend_Http_Client.');
249 }
250 $userAgent = $applicationId . ' Zend_Framework_Gdata/' .
251 Zend_Version::VERSION;
252 $client->setHeaders('User-Agent', $userAgent);
253 $client->setConfig(array(
254 'strictredirects' => true
255 )
256 );
257 $this->_httpClient = $client;
258 Zend_Gdata::setStaticHttpClient($client);
259 return $this;
260 }
261
262 /**
263 * Set the static HTTP client instance
264 *
265 * Sets the static HTTP client object to use for retrieving the feed.
266 *
267 * @param Zend_Http_Client $httpClient
268 * @return void
269 */
270 public static function setStaticHttpClient(Zend_Http_Client $httpClient)
271 {
272 self::$_staticHttpClient = $httpClient;
273 }
274
275
276 /**
277 * Gets the HTTP client object. If none is set, a new Zend_Http_Client will be used.
278 *
279 * @return Zend_Http_Client
280 */
281 public static function getStaticHttpClient()
282 {
283 if (!self::$_staticHttpClient instanceof Zend_Http_Client) {
284 $client = new Zend_Http_Client();
285 $userAgent = 'Zend_Framework_Gdata/' . Zend_Version::VERSION;
286 $client->setHeaders('User-Agent', $userAgent);
287 $client->setConfig(array(
288 'strictredirects' => true
289 )
290 );
291 self::$_staticHttpClient = $client;
292 }
293 return self::$_staticHttpClient;
294 }
295
296 /**
297 * Toggle using POST instead of PUT and DELETE HTTP methods
298 *
299 * Some feed implementations do not accept PUT and DELETE HTTP
300 * methods, or they can't be used because of proxies or other
301 * measures. This allows turning on using POST where PUT and
302 * DELETE would normally be used; in addition, an
303 * X-Method-Override header will be sent with a value of PUT or
304 * DELETE as appropriate.
305 *
306 * @param boolean $override Whether to override PUT and DELETE with POST.
307 * @return void
308 */
309 public static function setHttpMethodOverride($override = true)
310 {
311 self::$_httpMethodOverride = $override;
312 }
313
314 /**
315 * Get the HTTP override state
316 *
317 * @return boolean
318 */
319 public static function getHttpMethodOverride()
320 {
321 return self::$_httpMethodOverride;
322 }
323
324 /**
325 * Toggle requesting gzip encoded responses
326 *
327 * @param boolean $enabled Whether or not to enable gzipped responses
328 * @return void
329 */
330 public static function setGzipEnabled($enabled = false)
331 {
332 if ($enabled && !function_exists('gzinflate')) {
333 require_once 'Zend/Gdata/App/InvalidArgumentException.php';
334 throw new Zend_Gdata_App_InvalidArgumentException(
335 'You cannot enable gzipped responses if the zlib module ' .
336 'is not enabled in your PHP installation.');
337
338 }
339 self::$_gzipEnabled = $enabled;
340 }
341
342 /**
343 * Get the HTTP override state
344 *
345 * @return boolean
346 */
347 public static function getGzipEnabled()
348 {
349 return self::$_gzipEnabled;
350 }
351
352 /**
353 * Get whether to use verbose exception messages
354 *
355 * In the case of HTTP errors, use the body of the HTTP response
356 * in the exception message.
357 *
358 * @return boolean
359 */
360 public static function getVerboseExceptionMessages()
361 {
362 return self::$_verboseExceptionMessages;
363 }
364
365 /**
366 * Set whether to use verbose exception messages
367 *
368 * In the case of HTTP errors, use the body of the HTTP response
369 * in the exception message.
370 *
371 * @param boolean $verbose Whether to use verbose exception messages
372 */
373 public static function setVerboseExceptionMessages($verbose)
374 {
375 self::$_verboseExceptionMessages = $verbose;
376 }
377
378 /**
379 * Set the maximum number of redirects to follow during HTTP operations
380 *
381 * @param int $maxRedirects Maximum number of redirects to follow
382 * @return void
383 */
384 public static function setMaxRedirects($maxRedirects)
385 {
386 self::$_maxRedirects = $maxRedirects;
387 }
388
389 /**
390 * Get the maximum number of redirects to follow during HTTP operations
391 *
392 * @return int Maximum number of redirects to follow
393 */
394 public static function getMaxRedirects()
395 {
396 return self::$_maxRedirects;
397 }
398
399 /**
400 * Set the major protocol version that should be used. Values < 1 will
401 * cause a Zend_Gdata_App_InvalidArgumentException to be thrown.
402 *
403 * @see _majorProtocolVersion
404 * @param int $value The major protocol version to use.
405 * @throws Zend_Gdata_App_InvalidArgumentException
406 */
407 public function setMajorProtocolVersion($value)
408 {
409 if (!($value >= 1)) {
410 require_once('Zend/Gdata/App/InvalidArgumentException.php');
411 throw new Zend_Gdata_App_InvalidArgumentException(
412 'Major protocol version must be >= 1');
413 }
414 $this->_majorProtocolVersion = $value;
415 }
416
417 /**
418 * Get the major protocol version that is in use.
419 *
420 * @see _majorProtocolVersion
421 * @return int The major protocol version in use.
422 */
423 public function getMajorProtocolVersion()
424 {
425 return $this->_majorProtocolVersion;
426 }
427
428 /**
429 * Set the minor protocol version that should be used. If set to NULL, no
430 * minor protocol version will be sent to the server. Values < 0 will
431 * cause a Zend_Gdata_App_InvalidArgumentException to be thrown.
432 *
433 * @see _minorProtocolVersion
434 * @param (int|NULL) $value The minor protocol version to use.
435 * @throws Zend_Gdata_App_InvalidArgumentException
436 */
437 public function setMinorProtocolVersion($value)
438 {
439 if (!($value >= 0)) {
440 require_once('Zend/Gdata/App/InvalidArgumentException.php');
441 throw new Zend_Gdata_App_InvalidArgumentException(
442 'Minor protocol version must be >= 0');
443 }
444 $this->_minorProtocolVersion = $value;
445 }
446
447 /**
448 * Get the minor protocol version that is in use.
449 *
450 * @see _minorProtocolVersion
451 * @return (int|NULL) The major protocol version in use, or NULL if no
452 * minor version is specified.
453 */
454 public function getMinorProtocolVersion()
455 {
456 return $this->_minorProtocolVersion;
457 }
458
459 /**
460 * Provides pre-processing for HTTP requests to APP services.
461 *
462 * 1. Checks the $data element and, if it's an entry, extracts the XML,
463 * multipart data, edit link (PUT,DELETE), etc.
464 * 2. If $data is a string, sets the default content-type header as
465 * 'application/atom+xml' if it's not already been set.
466 * 3. Adds a x-http-method override header and changes the HTTP method
467 * to 'POST' if necessary as per getHttpMethodOverride()
468 *
469 * @param string $method The HTTP method for the request - 'GET', 'POST',
470 * 'PUT', 'DELETE'
471 * @param string $url The URL to which this request is being performed,
472 * or null if found in $data
473 * @param array $headers An associative array of HTTP headers for this
474 * request
475 * @param mixed $data The Zend_Gdata_App_Entry or XML for the
476 * body of the request
477 * @param string $contentTypeOverride The override value for the
478 * content type of the request body
479 * @return array An associative array containing the determined
480 * 'method', 'url', 'data', 'headers', 'contentType'
481 */
482 public function prepareRequest($method,
483 $url = null,
484 $headers = array(),
485 $data = null,
486 $contentTypeOverride = null)
487 {
488 // As a convenience, if $headers is null, we'll convert it back to
489 // an empty array.
490 if ($headers === null) {
491 $headers = array();
492 }
493
494 $rawData = null;
495 $finalContentType = null;
496 if ($url == null) {
497 $url = $this->_defaultPostUri;
498 }
499
500 if (is_string($data)) {
501 $rawData = $data;
502 if ($contentTypeOverride === null) {
503 $finalContentType = 'application/atom+xml';
504 }
505 } elseif ($data instanceof Zend_Gdata_App_MediaEntry) {
506 $rawData = $data->encode();
507 if ($data->getMediaSource() !== null) {
508 $finalContentType = $rawData->getContentType();
509 $headers['MIME-version'] = '1.0';
510 $headers['Slug'] = $data->getMediaSource()->getSlug();
511 } else {
512 $finalContentType = 'application/atom+xml';
513 }
514 if ($method == 'PUT' || $method == 'DELETE') {
515 $editLink = $data->getEditLink();
516 if ($editLink != null) {
517 $url = $editLink->getHref();
518 }
519 }
520 } elseif ($data instanceof Zend_Gdata_App_Entry) {
521 $rawData = $data->saveXML();
522 $finalContentType = 'application/atom+xml';
523 if ($method == 'PUT' || $method == 'DELETE') {
524 $editLink = $data->getEditLink();
525 if ($editLink != null) {
526 $url = $editLink->getHref();
527 }
528 }
529 } elseif ($data instanceof Zend_Gdata_App_MediaSource) {
530 $rawData = $data->encode();
531 if ($data->getSlug() !== null) {
532 $headers['Slug'] = $data->getSlug();
533 }
534 $finalContentType = $data->getContentType();
535 }
536
537 if ($method == 'DELETE') {
538 $rawData = null;
539 }
540
541 // Set an If-Match header if:
542 // - This isn't a DELETE
543 // - If this isn't a GET, the Etag isn't weak
544 // - A similar header (If-Match/If-None-Match) hasn't already been
545 // set.
546 if ($method != 'DELETE' && (
547 !array_key_exists('If-Match', $headers) &&
548 !array_key_exists('If-None-Match', $headers)
549 ) ) {
550 $allowWeak = $method == 'GET';
551 if ($ifMatchHeader = $this->generateIfMatchHeaderData(
552 $data, $allowWeak)) {
553 $headers['If-Match'] = $ifMatchHeader;
554 }
555 }
556
557 if ($method != 'POST' && $method != 'GET' && Zend_Gdata_App::getHttpMethodOverride()) {
558 $headers['x-http-method-override'] = $method;
559 $method = 'POST';
560 } else {
561 $headers['x-http-method-override'] = null;
562 }
563
564 if ($contentTypeOverride != null) {
565 $finalContentType = $contentTypeOverride;
566 }
567
568 return array('method' => $method, 'url' => $url,
569 'data' => $rawData, 'headers' => $headers,
570 'contentType' => $finalContentType);
571 }
572
573 /**
574 * Performs a HTTP request using the specified method
575 *
576 * @param string $method The HTTP method for the request - 'GET', 'POST',
577 * 'PUT', 'DELETE'
578 * @param string $url The URL to which this request is being performed
579 * @param array $headers An associative array of HTTP headers
580 * for this request
581 * @param string $body The body of the HTTP request
582 * @param string $contentType The value for the content type
583 * of the request body
584 * @param int $remainingRedirects Number of redirects to follow if request
585 * s results in one
586 * @return Zend_Http_Response The response object
587 */
588 public function performHttpRequest($method, $url, $headers = null,
589 $body = null, $contentType = null, $remainingRedirects = null)
590 {
591 require_once 'Zend/Http/Client/Exception.php';
592 if ($remainingRedirects === null) {
593 $remainingRedirects = self::getMaxRedirects();
594 }
595 if ($headers === null) {
596 $headers = array();
597 }
598 // Append a Gdata version header if protocol v2 or higher is in use.
599 // (Protocol v1 does not use this header.)
600 $major = $this->getMajorProtocolVersion();
601 $minor = $this->getMinorProtocolVersion();
602 if ($major >= 2) {
603 $headers['GData-Version'] = $major +
604 (($minor === null) ? '.' + $minor : '');
605 }
606
607 // check the overridden method
608 if (($method == 'POST' || $method == 'PUT') && $body === null &&
609 $headers['x-http-method-override'] != 'DELETE') {
610 require_once 'Zend/Gdata/App/InvalidArgumentException.php';
611 throw new Zend_Gdata_App_InvalidArgumentException(
612 'You must specify the data to post as either a ' .
613 'string or a child of Zend_Gdata_App_Entry');
614 }
615 if ($url === null) {
616 require_once 'Zend/Gdata/App/InvalidArgumentException.php';
617 throw new Zend_Gdata_App_InvalidArgumentException(
618 'You must specify an URI to which to post.');
619 }
620 $headers['Content-Type'] = $contentType;
621 if (Zend_Gdata_App::getGzipEnabled()) {
622 // some services require the word 'gzip' to be in the user-agent
623 // header in addition to the accept-encoding header
624 if (strpos($this->_httpClient->getHeader('User-Agent'),
625 'gzip') === false) {
626 $headers['User-Agent'] =
627 $this->_httpClient->getHeader('User-Agent') . ' (gzip)';
628 }
629 $headers['Accept-encoding'] = 'gzip, deflate';
630 } else {
631 $headers['Accept-encoding'] = 'identity';
632 }
633
634 // Make sure the HTTP client object is 'clean' before making a request
635 // In addition to standard headers to reset via resetParameters(),
636 // also reset the Slug header
637 $this->_httpClient->resetParameters();
638 $this->_httpClient->setHeaders('Slug', null);
639
640 // Set the params for the new request to be performed
641 $this->_httpClient->setHeaders($headers);
642 $this->_httpClient->setUri($url);
643 $this->_httpClient->setConfig(array('maxredirects' => 0));
644
645 // Set the proper adapter if we are handling a streaming upload
646 $usingMimeStream = false;
647 $oldHttpAdapter = null;
648
649 if ($body instanceof Zend_Gdata_MediaMimeStream) {
650 $usingMimeStream = true;
651 $this->_httpClient->setRawDataStream($body, $contentType);
652 $oldHttpAdapter = $this->_httpClient->getAdapter();
653
654 if ($oldHttpAdapter instanceof Zend_Http_Client_Adapter_Proxy) {
655 require_once 'Zend/Gdata/HttpAdapterStreamingProxy.php';
656 $newAdapter = new Zend_Gdata_HttpAdapterStreamingProxy();
657 } else {
658 require_once 'Zend/Gdata/HttpAdapterStreamingSocket.php';
659 $newAdapter = new Zend_Gdata_HttpAdapterStreamingSocket();
660 }
661 $this->_httpClient->setAdapter($newAdapter);
662 } else {
663 $this->_httpClient->setRawData($body, $contentType);
664 }
665
666 try {
667 $response = $this->_httpClient->request($method);
668 // reset adapter
669 if ($usingMimeStream) {
670 $this->_httpClient->setAdapter($oldHttpAdapter);
671 }
672 } catch (Zend_Http_Client_Exception $e) {
673 // reset adapter
674 if ($usingMimeStream) {
675 $this->_httpClient->setAdapter($oldHttpAdapter);
676 }
677 require_once 'Zend/Gdata/App/HttpException.php';
678 throw new Zend_Gdata_App_HttpException($e->getMessage(), $e);
679 }
680 if ($response->isRedirect() && $response->getStatus() != '304') {
681 if ($remainingRedirects > 0) {
682 $newUrl = $response->getHeader('Location');
683 $response = $this->performHttpRequest(
684 $method, $newUrl, $headers, $body,
685 $contentType, $remainingRedirects);
686 } else {
687 require_once 'Zend/Gdata/App/HttpException.php';
688 throw new Zend_Gdata_App_HttpException(
689 'Number of redirects exceeds maximum', null, $response);
690 }
691 }
692 if (!$response->isSuccessful()) {
693 require_once 'Zend/Gdata/App/HttpException.php';
694 $exceptionMessage = 'Expected response code 200, got ' .
695 $response->getStatus();
696 if (self::getVerboseExceptionMessages()) {
697 $exceptionMessage .= "\n" . $response->getBody();
698 }
699 $exception = new Zend_Gdata_App_HttpException($exceptionMessage);
700 $exception->setResponse($response);
701 throw $exception;
702 }
703 return $response;
704 }
705
706 /**
707 * Imports a feed located at $uri.
708 *
709 * @param string $uri
710 * @param Zend_Http_Client $client The client used for communication
711 * @param string $className The class which is used as the return type
712 * @throws Zend_Gdata_App_Exception
713 * @return string|Zend_Gdata_App_Feed Returns string only if the object
714 * mapping has been disabled explicitly
715 * by passing false to the
716 * useObjectMapping() function.
717 */
718 public static function import($uri, $client = null,
719 $className='Zend_Gdata_App_Feed')
720 {
721 $app = new Zend_Gdata_App($client);
722 $requestData = $app->prepareRequest('GET', $uri);
723 $response = $app->performHttpRequest(
724 $requestData['method'], $requestData['url']);
725
726 $feedContent = $response->getBody();
727 if (!$this->_useObjectMapping) {
728 return $feedContent;
729 }
730 $feed = self::importString($feedContent, $className);
731 if ($client != null) {
732 $feed->setHttpClient($client);
733 }
734 return $feed;
735 }
736
737 /**
738 * Imports the specified URL (non-statically).
739 *
740 * @param string $url The URL to import
741 * @param string $className The class which is used as the return type
742 * @param array $extraHeaders Extra headers to add to the request, as an
743 * array of string-based key/value pairs.
744 * @throws Zend_Gdata_App_Exception
745 * @return string|Zend_Gdata_App_Feed Returns string only if the object
746 * mapping has been disabled explicitly
747 * by passing false to the
748 * useObjectMapping() function.
749 */
750 public function importUrl($url, $className='Zend_Gdata_App_Feed',
751 $extraHeaders = array())
752 {
753 $response = $this->get($url, $extraHeaders);
754
755 $feedContent = $response->getBody();
756 if (!$this->_useObjectMapping) {
757 return $feedContent;
758 }
759
760 $protocolVersionStr = $response->getHeader('GData-Version');
761 $majorProtocolVersion = null;
762 $minorProtocolVersion = null;
763 if ($protocolVersionStr !== null) {
764 // Extract protocol major and minor version from header
765 $delimiterPos = strpos($protocolVersionStr, '.');
766 $length = strlen($protocolVersionStr);
767 $major = substr($protocolVersionStr, 0, $delimiterPos);
768 $minor = substr($protocolVersionStr, $delimiterPos + 1, $length);
769 $majorProtocolVersion = $major;
770 $minorProtocolVersion = $minor;
771 }
772
773 $feed = self::importString($feedContent, $className,
774 $majorProtocolVersion, $minorProtocolVersion);
775 if ($this->getHttpClient() != null) {
776 $feed->setHttpClient($this->getHttpClient());
777 }
778 $etag = $response->getHeader('ETag');
779 if ($etag !== null) {
780 $feed->setEtag($etag);
781 }
782 return $feed;
783 }
784
785
786 /**
787 * Imports a feed represented by $string.
788 *
789 * @param string $string
790 * @param string $className The class which is used as the return type
791 * @param integer $majorProcolVersion (optional) The major protocol version
792 * of the data model object that is to be created.
793 * @param integer $minorProcolVersion (optional) The minor protocol version
794 * of the data model object that is to be created.
795 * @throws Zend_Gdata_App_Exception
796 * @return Zend_Gdata_App_Feed
797 */
798 public static function importString($string,
799 $className='Zend_Gdata_App_Feed', $majorProtocolVersion = null,
800 $minorProtocolVersion = null)
801 {
802 // Load the feed as an XML DOMDocument object
803 @ini_set('track_errors', 1);
804 $doc = new DOMDocument();
805 $success = @$doc->loadXML($string);
806 @ini_restore('track_errors');
807
808 if (!$success) {
809 require_once 'Zend/Gdata/App/Exception.php';
810 throw new Zend_Gdata_App_Exception(
811 "DOMDocument cannot parse XML: $php_errormsg");
812 }
813
814 $feed = new $className();
815 $feed->setMajorProtocolVersion($majorProtocolVersion);
816 $feed->setMinorProtocolVersion($minorProtocolVersion);
817 $feed->transferFromXML($string);
818 $feed->setHttpClient(self::getstaticHttpClient());
819 return $feed;
820 }
821
822
823 /**
824 * Imports a feed from a file located at $filename.
825 *
826 * @param string $filename
827 * @param string $className The class which is used as the return type
828 * @param string $useIncludePath Whether the include_path should be searched
829 * @throws Zend_Gdata_App_Exception
830 * @return Zend_Gdata_Feed
831 */
832 public static function importFile($filename,
833 $className='Zend_Gdata_App_Feed', $useIncludePath = false)
834 {
835 @ini_set('track_errors', 1);
836 $feed = @file_get_contents($filename, $useIncludePath);
837 @ini_restore('track_errors');
838 if ($feed === false) {
839 require_once 'Zend/Gdata/App/Exception.php';
840 throw new Zend_Gdata_App_Exception(
841 "File could not be loaded: $php_errormsg");
842 }
843 return self::importString($feed, $className);
844 }
845
846 /**
847 * GET a URI using client object.
848 *
849 * @param string $uri GET URI
850 * @param array $extraHeaders Extra headers to add to the request, as an
851 * array of string-based key/value pairs.
852 * @throws Zend_Gdata_App_HttpException
853 * @return Zend_Http_Response
854 */
855 public function get($uri, $extraHeaders = array())
856 {
857 $requestData = $this->prepareRequest('GET', $uri, $extraHeaders);
858 return $this->performHttpRequest(
859 $requestData['method'], $requestData['url'],
860 $requestData['headers']);
861 }
862
863 /**
864 * POST data with client object
865 *
866 * @param mixed $data The Zend_Gdata_App_Entry or XML to post
867 * @param string $uri POST URI
868 * @param array $headers Additional HTTP headers to insert.
869 * @param string $contentType Content-type of the data
870 * @param array $extraHeaders Extra headers to add to the request, as an
871 * array of string-based key/value pairs.
872 * @return Zend_Http_Response
873 * @throws Zend_Gdata_App_Exception
874 * @throws Zend_Gdata_App_HttpException
875 * @throws Zend_Gdata_App_InvalidArgumentException
876 */
877 public function post($data, $uri = null, $remainingRedirects = null,
878 $contentType = null, $extraHeaders = null)
879 {
880 $requestData = $this->prepareRequest(
881 'POST', $uri, $extraHeaders, $data, $contentType);
882 return $this->performHttpRequest(
883 $requestData['method'], $requestData['url'],
884 $requestData['headers'], $requestData['data'],
885 $requestData['contentType']);
886 }
887
888 /**
889 * PUT data with client object
890 *
891 * @param mixed $data The Zend_Gdata_App_Entry or XML to post
892 * @param string $uri PUT URI
893 * @param array $headers Additional HTTP headers to insert.
894 * @param string $contentType Content-type of the data
895 * @param array $extraHeaders Extra headers to add to the request, as an
896 * array of string-based key/value pairs.
897 * @return Zend_Http_Response
898 * @throws Zend_Gdata_App_Exception
899 * @throws Zend_Gdata_App_HttpException
900 * @throws Zend_Gdata_App_InvalidArgumentException
901 */
902 public function put($data, $uri = null, $remainingRedirects = null,
903 $contentType = null, $extraHeaders = null)
904 {
905 $requestData = $this->prepareRequest(
906 'PUT', $uri, $extraHeaders, $data, $contentType);
907 return $this->performHttpRequest(
908 $requestData['method'], $requestData['url'],
909 $requestData['headers'], $requestData['data'],
910 $requestData['contentType']);
911 }
912
913 /**
914 * DELETE entry with client object
915 *
916 * @param mixed $data The Zend_Gdata_App_Entry or URL to delete
917 * @return void
918 * @throws Zend_Gdata_App_Exception
919 * @throws Zend_Gdata_App_HttpException
920 * @throws Zend_Gdata_App_InvalidArgumentException
921 */
922 public function delete($data, $remainingRedirects = null)
923 {
924 if (is_string($data)) {
925 $requestData = $this->prepareRequest('DELETE', $data);
926 } else {
927 $headers = array();
928
929 $requestData = $this->prepareRequest(
930 'DELETE', null, $headers, $data);
931 }
932 return $this->performHttpRequest($requestData['method'],
933 $requestData['url'],
934 $requestData['headers'],
935 '',
936 $requestData['contentType'],
937 $remainingRedirects);
938 }
939
940 /**
941 * Inserts an entry to a given URI and returns the response as a
942 * fully formed Entry.
943 *
944 * @param mixed $data The Zend_Gdata_App_Entry or XML to post
945 * @param string $uri POST URI
946 * @param string $className The class of entry to be returned.
947 * @param array $extraHeaders Extra headers to add to the request, as an
948 * array of string-based key/value pairs.
949 * @return Zend_Gdata_App_Entry The entry returned by the service after
950 * insertion.
951 */
952 public function insertEntry($data, $uri, $className='Zend_Gdata_App_Entry',
953 $extraHeaders = array())
954 {
955 $response = $this->post($data, $uri, null, null, $extraHeaders);
956
957 $returnEntry = new $className($response->getBody());
958 $returnEntry->setHttpClient(self::getstaticHttpClient());
959
960 $etag = $response->getHeader('ETag');
961 if ($etag !== null) {
962 $returnEntry->setEtag($etag);
963 }
964
965 return $returnEntry;
966 }
967
968 /**
969 * Update an entry
970 *
971 * @param mixed $data Zend_Gdata_App_Entry or XML (w/ID and link rel='edit')
972 * @param string|null The URI to send requests to, or null if $data
973 * contains the URI.
974 * @param string|null The name of the class that should be deserialized
975 * from the server response. If null, then 'Zend_Gdata_App_Entry'
976 * will be used.
977 * @param array $extraHeaders Extra headers to add to the request, as an
978 * array of string-based key/value pairs.
979 * @return Zend_Gdata_App_Entry The entry returned from the server
980 * @throws Zend_Gdata_App_Exception
981 */
982 public function updateEntry($data, $uri = null, $className = null,
983 $extraHeaders = array())
984 {
985 if ($className === null && $data instanceof Zend_Gdata_App_Entry) {
986 $className = get_class($data);
987 } elseif ($className === null) {
988 $className = 'Zend_Gdata_App_Entry';
989 }
990
991 $response = $this->put($data, $uri, null, null, $extraHeaders);
992 $returnEntry = new $className($response->getBody());
993 $returnEntry->setHttpClient(self::getstaticHttpClient());
994
995 $etag = $response->getHeader('ETag');
996 if ($etag !== null) {
997 $returnEntry->setEtag($etag);
998 }
999
1000 return $returnEntry;
1001 }
1002
1003 /**
1004 * Provides a magic factory method to instantiate new objects with
1005 * shorter syntax than would otherwise be required by the Zend Framework
1006 * naming conventions. For instance, to construct a new
1007 * Zend_Gdata_Calendar_Extension_Color, a developer simply needs to do
1008 * $gCal->newColor(). For this magic constructor, packages are searched
1009 * in the same order as which they appear in the $_registeredPackages
1010 * array
1011 *
1012 * @param string $method The method name being called
1013 * @param array $args The arguments passed to the call
1014 * @throws Zend_Gdata_App_Exception
1015 */
1016 public function __call($method, $args)
1017 {
1018 if (preg_match('/^new(\w+)/', $method, $matches)) {
1019 $class = $matches[1];
1020 $foundClassName = null;
1021 foreach ($this->_registeredPackages as $name) {
1022 try {
1023 @Zend_Loader::loadClass("${name}_${class}");
1024 $foundClassName = "${name}_${class}";
1025 break;
1026 } catch (Zend_Exception $e) {
1027 // package wasn't here- continue searching
1028 }
1029 }
1030 if ($foundClassName != null) {
1031 $reflectionObj = new ReflectionClass($foundClassName);
1032 $instance = $reflectionObj->newInstanceArgs($args);
1033 if ($instance instanceof Zend_Gdata_App_FeedEntryParent) {
1034 $instance->setHttpClient($this->_httpClient);
1035
1036 // Propogate version data
1037 $instance->setMajorProtocolVersion(
1038 $this->_majorProtocolVersion);
1039 $instance->setMinorProtocolVersion(
1040 $this->_minorProtocolVersion);
1041 }
1042 return $instance;
1043 } else {
1044 require_once 'Zend/Gdata/App/Exception.php';
1045 throw new Zend_Gdata_App_Exception(
1046 "Unable to find '${class}' in registered packages");
1047 }
1048 } else {
1049 require_once 'Zend/Gdata/App/Exception.php';
1050 throw new Zend_Gdata_App_Exception("No such method ${method}");
1051 }
1052 }
1053
1054 /**
1055 * Retrieve all entries for a feed, iterating through pages as necessary.
1056 * Be aware that calling this function on a large dataset will take a
1057 * significant amount of time to complete. In some cases this may cause
1058 * execution to timeout without proper precautions in place.
1059 *
1060 * @param $feed The feed to iterate through.
1061 * @return mixed A new feed of the same type as the one originally
1062 * passed in, containing all relevent entries.
1063 */
1064 public function retrieveAllEntriesForFeed($feed) {
1065 $feedClass = get_class($feed);
1066 $reflectionObj = new ReflectionClass($feedClass);
1067 $result = $reflectionObj->newInstance();
1068 do {
1069 foreach ($feed as $entry) {
1070 $result->addEntry($entry);
1071 }
1072
1073 $next = $feed->getLink('next');
1074 if ($next !== null) {
1075 $feed = $this->getFeed($next->href, $feedClass);
1076 } else {
1077 $feed = null;
1078 }
1079 }
1080 while ($feed != null);
1081 return $result;
1082 }
1083
1084 /**
1085 * This method enables logging of requests by changing the
1086 * Zend_Http_Client_Adapter used for performing the requests.
1087 * NOTE: This will not work if you have customized the adapter
1088 * already to use a proxy server or other interface.
1089 *
1090 * @param $logfile The logfile to use when logging the requests
1091 */
1092 public function enableRequestDebugLogging($logfile)
1093 {
1094 $this->_httpClient->setConfig(array(
1095 'adapter' => 'Zend_Gdata_App_LoggingHttpClientAdapterSocket',
1096 'logfile' => $logfile
1097 ));
1098 }
1099
1100 /**
1101 * Retrieve next set of results based on a given feed.
1102 *
1103 * @param Zend_Gdata_App_Feed $feed The feed from which to
1104 * retreive the next set of results.
1105 * @param string $className (optional) The class of feed to be returned.
1106 * If null, the next feed (if found) will be the same class as
1107 * the feed that was given as the first argument.
1108 * @return Zend_Gdata_App_Feed|null Returns a
1109 * Zend_Gdata_App_Feed or null if no next set of results
1110 * exists.
1111 */
1112 public function getNextFeed($feed, $className = null)
1113 {
1114 $nextLink = $feed->getNextLink();
1115 if (!$nextLink) {
1116 return null;
1117 }
1118 $nextLinkHref = $nextLink->getHref();
1119
1120 if ($className === null) {
1121 $className = get_class($feed);
1122 }
1123
1124 return $this->getFeed($nextLinkHref, $className);
1125 }
1126
1127 /**
1128 * Retrieve previous set of results based on a given feed.
1129 *
1130 * @param Zend_Gdata_App_Feed $feed The feed from which to
1131 * retreive the previous set of results.
1132 * @param string $className (optional) The class of feed to be returned.
1133 * If null, the previous feed (if found) will be the same class as
1134 * the feed that was given as the first argument.
1135 * @return Zend_Gdata_App_Feed|null Returns a
1136 * Zend_Gdata_App_Feed or null if no previous set of results
1137 * exists.
1138 */
1139 public function getPreviousFeed($feed, $className = null)
1140 {
1141 $previousLink = $feed->getPreviousLink();
1142 if (!$previousLink) {
1143 return null;
1144 }
1145 $previousLinkHref = $previousLink->getHref();
1146
1147 if ($className === null) {
1148 $className = get_class($feed);
1149 }
1150
1151 return $this->getFeed($previousLinkHref, $className);
1152 }
1153
1154 /**
1155 * Returns the data for an If-Match header based on the current Etag
1156 * property. If Etags are not supported by the server or cannot be
1157 * extracted from the data, then null will be returned.
1158 *
1159 * @param boolean $allowWeak If false, then if a weak Etag is detected,
1160 * then return null rather than the Etag.
1161 * @return string|null $data
1162 */
1163 public function generateIfMatchHeaderData($data, $allowWeek)
1164 {
1165 $result = '';
1166 // Set an If-Match header if an ETag has been set (version >= 2 only)
1167 if ($this->_majorProtocolVersion >= 2 &&
1168 $data instanceof Zend_Gdata_App_Entry) {
1169 $etag = $data->getEtag();
1170 if (($etag !== null) &&
1171 ($allowWeek || substr($etag, 0, 2) != 'W/')) {
1172 $result = $data->getEtag();
1173 }
1174 }
1175 return $result;
1176 }
1177
1178 /**
1179 * Determine whether service object is using XML to object mapping.
1180 *
1181 * @return boolean True if service object is using XML to object mapping,
1182 * false otherwise.
1183 */
1184 public function usingObjectMapping()
1185 {
1186 return $this->_useObjectMapping;
1187 }
1188
1189 /**
1190 * Enable/disable the use of XML to object mapping.
1191 *
1192 * @param boolean $value Pass in true to use the XML to object mapping.
1193 * Pass in false or null to disable it.
1194 * @return void
1195 */
1196 public function useObjectMapping($value)
1197 {
1198 if ($value === True) {
1199 $this->_useObjectMapping = true;
1200 } else {
1201 $this->_useObjectMapping = false;
1202 }
1203 }
1204
1205}
Note: See TracBrowser for help on using the repository browser.