File indexing completed on 2024-12-22 05:36:46
0001 <?php 0002 0003 /** 0004 * Zend Framework 0005 * 0006 * LICENSE 0007 * 0008 * This source file is subject to the new BSD license that is bundled 0009 * with this package in the file LICENSE.txt. 0010 * It is also available through the world-wide-web at this URL: 0011 * http://framework.zend.com/license/new-bsd 0012 * If you did not receive a copy of the license and are unable to 0013 * obtain it through the world-wide-web, please send an email 0014 * to license@zend.com so we can send you a copy immediately. 0015 * 0016 * @category Zend 0017 * @package Zend_Gdata 0018 * @subpackage App 0019 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0020 * @license http://framework.zend.com/license/new-bsd New BSD License 0021 * @version $Id$ 0022 */ 0023 0024 /** 0025 * Zend_Gdata_Feed 0026 */ 0027 // require_once 'Zend/Gdata/App/Feed.php'; 0028 0029 /** 0030 * Zend_Gdata_Http_Client 0031 */ 0032 // require_once 'Zend/Http/Client.php'; 0033 0034 /** 0035 * Zend_Version 0036 */ 0037 // require_once 'Zend/Version.php'; 0038 0039 /** 0040 * Zend_Gdata_App_MediaSource 0041 */ 0042 // require_once 'Zend/Gdata/App/MediaSource.php'; 0043 0044 /** 0045 * Zend_Uri/Http 0046 */ 0047 // require_once 'Zend/Uri/Http.php'; 0048 0049 /** @see Zend_Xml_Security */ 0050 // require_once 'Zend/Xml/Security.php'; 0051 0052 /** 0053 * Provides Atom Publishing Protocol (APP) functionality. This class and all 0054 * other components of Zend_Gdata_App are designed to work independently from 0055 * other Zend_Gdata components in order to interact with generic APP services. 0056 * 0057 * @category Zend 0058 * @package Zend_Gdata 0059 * @subpackage App 0060 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0061 * @license http://framework.zend.com/license/new-bsd New BSD License 0062 */ 0063 class Zend_Gdata_App 0064 { 0065 0066 /** Default major protocol version. 0067 * 0068 * @see _majorProtocolVersion 0069 */ 0070 const DEFAULT_MAJOR_PROTOCOL_VERSION = 1; 0071 0072 /** Default minor protocol version. 0073 * 0074 * @see _minorProtocolVersion 0075 */ 0076 const DEFAULT_MINOR_PROTOCOL_VERSION = null; 0077 0078 /** 0079 * Client object used to communicate 0080 * 0081 * @var Zend_Http_Client 0082 */ 0083 protected $_httpClient; 0084 0085 /** 0086 * Client object used to communicate in static context 0087 * 0088 * @var Zend_Http_Client 0089 */ 0090 protected static $_staticHttpClient = null; 0091 0092 /** 0093 * Override HTTP PUT and DELETE request methods? 0094 * 0095 * @var boolean 0096 */ 0097 protected static $_httpMethodOverride = false; 0098 0099 /** 0100 * Enable gzipped responses? 0101 * 0102 * @var boolean 0103 */ 0104 protected static $_gzipEnabled = false; 0105 0106 /** 0107 * Use verbose exception messages. In the case of HTTP errors, 0108 * use the body of the HTTP response in the exception message. 0109 * 0110 * @var boolean 0111 */ 0112 protected static $_verboseExceptionMessages = true; 0113 0114 /** 0115 * Default URI to which to POST. 0116 * 0117 * @var string 0118 */ 0119 protected $_defaultPostUri = null; 0120 0121 /** 0122 * Packages to search for classes when using magic __call method, in order. 0123 * 0124 * @var array 0125 */ 0126 protected $_registeredPackages = array( 0127 'Zend_Gdata_App_Extension', 0128 'Zend_Gdata_App'); 0129 0130 /** 0131 * Maximum number of redirects to follow during HTTP operations 0132 * 0133 * @var int 0134 */ 0135 protected static $_maxRedirects = 5; 0136 0137 /** 0138 * Indicates the major protocol version that should be used. 0139 * At present, recognized values are either 1 or 2. However, any integer 0140 * value >= 1 is considered valid. 0141 * 0142 * Under most circumtances, this will be automatically set by 0143 * Zend_Gdata_App subclasses. 0144 * 0145 * @see setMajorProtocolVersion() 0146 * @see getMajorProtocolVersion() 0147 */ 0148 protected $_majorProtocolVersion; 0149 0150 /** 0151 * Indicates the minor protocol version that should be used. Can be set 0152 * to either an integer >= 0, or NULL if no minor version should be sent 0153 * to the server. 0154 * 0155 * At present, this field is not used by any Google services, but may be 0156 * used in the future. 0157 * 0158 * Under most circumtances, this will be automatically set by 0159 * Zend_Gdata_App subclasses. 0160 * 0161 * @see setMinorProtocolVersion() 0162 * @see getMinorProtocolVersion() 0163 */ 0164 protected $_minorProtocolVersion; 0165 0166 /** 0167 * Whether we want to use XML to object mapping when fetching data. 0168 * 0169 * @var boolean 0170 */ 0171 protected $_useObjectMapping = true; 0172 0173 /** 0174 * Create Gdata object 0175 * 0176 * @param Zend_Http_Client $client 0177 * @param string $applicationId 0178 */ 0179 public function __construct($client = null, $applicationId = 'MyCompany-MyApp-1.0') 0180 { 0181 $this->setHttpClient($client, $applicationId); 0182 // Set default protocol version. Subclasses should override this as 0183 // needed once a given service supports a new version. 0184 $this->setMajorProtocolVersion(self::DEFAULT_MAJOR_PROTOCOL_VERSION); 0185 $this->setMinorProtocolVersion(self::DEFAULT_MINOR_PROTOCOL_VERSION); 0186 } 0187 0188 /** 0189 * Adds a Zend Framework package to the $_registeredPackages array. 0190 * This array is searched when using the magic __call method below 0191 * to instantiante new objects. 0192 * 0193 * @param string $name The name of the package (eg Zend_Gdata_App) 0194 * @return void 0195 */ 0196 public function registerPackage($name) 0197 { 0198 array_unshift($this->_registeredPackages, $name); 0199 } 0200 0201 /** 0202 * Retrieve feed as string or object 0203 * 0204 * @param string $uri The uri from which to retrieve the feed 0205 * @param string $className The class which is used as the return type 0206 * @return string|Zend_Gdata_App_Feed Returns string only if the object 0207 * mapping has been disabled explicitly 0208 * by passing false to the 0209 * useObjectMapping() function. 0210 */ 0211 public function getFeed($uri, $className='Zend_Gdata_App_Feed') 0212 { 0213 return $this->importUrl($uri, $className, null); 0214 } 0215 0216 /** 0217 * Retrieve entry as string or object 0218 * 0219 * @param string $uri 0220 * @param string $className The class which is used as the return type 0221 * @return string|Zend_Gdata_App_Entry Returns string only if the object 0222 * mapping has been disabled explicitly 0223 * by passing false to the 0224 * useObjectMapping() function. 0225 */ 0226 public function getEntry($uri, $className='Zend_Gdata_App_Entry') 0227 { 0228 return $this->importUrl($uri, $className, null); 0229 } 0230 0231 /** 0232 * Get the Zend_Http_Client object used for communication 0233 * 0234 * @return Zend_Http_Client 0235 */ 0236 public function getHttpClient() 0237 { 0238 return $this->_httpClient; 0239 } 0240 0241 /** 0242 * Set the Zend_Http_Client object used for communication 0243 * 0244 * @param Zend_Http_Client $client The client to use for communication 0245 * @throws Zend_Gdata_App_HttpException 0246 * @return Zend_Gdata_App Provides a fluent interface 0247 */ 0248 public function setHttpClient($client, 0249 $applicationId = 'MyCompany-MyApp-1.0') 0250 { 0251 if ($client === null) { 0252 $client = new Zend_Http_Client(); 0253 } 0254 if (!$client instanceof Zend_Http_Client) { 0255 // require_once 'Zend/Gdata/App/HttpException.php'; 0256 throw new Zend_Gdata_App_HttpException( 0257 'Argument is not an instance of Zend_Http_Client.'); 0258 } 0259 $userAgent = $applicationId . ' Zend_Framework_Gdata/' . 0260 Zend_Version::VERSION; 0261 $client->setHeaders('User-Agent', $userAgent); 0262 $client->setConfig(array( 0263 'strictredirects' => true 0264 ) 0265 ); 0266 $this->_httpClient = $client; 0267 self::setStaticHttpClient($client); 0268 return $this; 0269 } 0270 0271 /** 0272 * Set the static HTTP client instance 0273 * 0274 * Sets the static HTTP client object to use for retrieving the feed. 0275 * 0276 * @param Zend_Http_Client $httpClient 0277 * @return void 0278 */ 0279 public static function setStaticHttpClient(Zend_Http_Client $httpClient) 0280 { 0281 self::$_staticHttpClient = $httpClient; 0282 } 0283 0284 0285 /** 0286 * Gets the HTTP client object. If none is set, a new Zend_Http_Client will be used. 0287 * 0288 * @return Zend_Http_Client 0289 */ 0290 public static function getStaticHttpClient() 0291 { 0292 if (!self::$_staticHttpClient instanceof Zend_Http_Client) { 0293 $client = new Zend_Http_Client(); 0294 $userAgent = 'Zend_Framework_Gdata/' . Zend_Version::VERSION; 0295 $client->setHeaders('User-Agent', $userAgent); 0296 $client->setConfig(array( 0297 'strictredirects' => true 0298 ) 0299 ); 0300 self::$_staticHttpClient = $client; 0301 } 0302 return self::$_staticHttpClient; 0303 } 0304 0305 /** 0306 * Toggle using POST instead of PUT and DELETE HTTP methods 0307 * 0308 * Some feed implementations do not accept PUT and DELETE HTTP 0309 * methods, or they can't be used because of proxies or other 0310 * measures. This allows turning on using POST where PUT and 0311 * DELETE would normally be used; in addition, an 0312 * X-Method-Override header will be sent with a value of PUT or 0313 * DELETE as appropriate. 0314 * 0315 * @param boolean $override Whether to override PUT and DELETE with POST. 0316 * @return void 0317 */ 0318 public static function setHttpMethodOverride($override = true) 0319 { 0320 self::$_httpMethodOverride = $override; 0321 } 0322 0323 /** 0324 * Get the HTTP override state 0325 * 0326 * @return boolean 0327 */ 0328 public static function getHttpMethodOverride() 0329 { 0330 return self::$_httpMethodOverride; 0331 } 0332 0333 /** 0334 * Toggle requesting gzip encoded responses 0335 * 0336 * @param boolean $enabled Whether or not to enable gzipped responses 0337 * @return void 0338 */ 0339 public static function setGzipEnabled($enabled = false) 0340 { 0341 if ($enabled && !function_exists('gzinflate')) { 0342 // require_once 'Zend/Gdata/App/InvalidArgumentException.php'; 0343 throw new Zend_Gdata_App_InvalidArgumentException( 0344 'You cannot enable gzipped responses if the zlib module ' . 0345 'is not enabled in your PHP installation.'); 0346 0347 } 0348 self::$_gzipEnabled = $enabled; 0349 } 0350 0351 /** 0352 * Get the HTTP override state 0353 * 0354 * @return boolean 0355 */ 0356 public static function getGzipEnabled() 0357 { 0358 return self::$_gzipEnabled; 0359 } 0360 0361 /** 0362 * Get whether to use verbose exception messages 0363 * 0364 * In the case of HTTP errors, use the body of the HTTP response 0365 * in the exception message. 0366 * 0367 * @return boolean 0368 */ 0369 public static function getVerboseExceptionMessages() 0370 { 0371 return self::$_verboseExceptionMessages; 0372 } 0373 0374 /** 0375 * Set whether to use verbose exception messages 0376 * 0377 * In the case of HTTP errors, use the body of the HTTP response 0378 * in the exception message. 0379 * 0380 * @param boolean $verbose Whether to use verbose exception messages 0381 */ 0382 public static function setVerboseExceptionMessages($verbose) 0383 { 0384 self::$_verboseExceptionMessages = $verbose; 0385 } 0386 0387 /** 0388 * Set the maximum number of redirects to follow during HTTP operations 0389 * 0390 * @param int $maxRedirects Maximum number of redirects to follow 0391 * @return void 0392 */ 0393 public static function setMaxRedirects($maxRedirects) 0394 { 0395 self::$_maxRedirects = $maxRedirects; 0396 } 0397 0398 /** 0399 * Get the maximum number of redirects to follow during HTTP operations 0400 * 0401 * @return int Maximum number of redirects to follow 0402 */ 0403 public static function getMaxRedirects() 0404 { 0405 return self::$_maxRedirects; 0406 } 0407 0408 /** 0409 * Set the major protocol version that should be used. Values < 1 will 0410 * cause a Zend_Gdata_App_InvalidArgumentException to be thrown. 0411 * 0412 * @see _majorProtocolVersion 0413 * @param int $value The major protocol version to use. 0414 * @throws Zend_Gdata_App_InvalidArgumentException 0415 */ 0416 public function setMajorProtocolVersion($value) 0417 { 0418 if (!($value >= 1)) { 0419 // require_once('Zend/Gdata/App/InvalidArgumentException.php'); 0420 throw new Zend_Gdata_App_InvalidArgumentException( 0421 'Major protocol version must be >= 1'); 0422 } 0423 $this->_majorProtocolVersion = $value; 0424 } 0425 0426 /** 0427 * Get the major protocol version that is in use. 0428 * 0429 * @see _majorProtocolVersion 0430 * @return int The major protocol version in use. 0431 */ 0432 public function getMajorProtocolVersion() 0433 { 0434 return $this->_majorProtocolVersion; 0435 } 0436 0437 /** 0438 * Set the minor protocol version that should be used. If set to NULL, no 0439 * minor protocol version will be sent to the server. Values < 0 will 0440 * cause a Zend_Gdata_App_InvalidArgumentException to be thrown. 0441 * 0442 * @see _minorProtocolVersion 0443 * @param (int|NULL) $value The minor protocol version to use. 0444 * @throws Zend_Gdata_App_InvalidArgumentException 0445 */ 0446 public function setMinorProtocolVersion($value) 0447 { 0448 if (!($value >= 0)) { 0449 // require_once('Zend/Gdata/App/InvalidArgumentException.php'); 0450 throw new Zend_Gdata_App_InvalidArgumentException( 0451 'Minor protocol version must be >= 0'); 0452 } 0453 $this->_minorProtocolVersion = $value; 0454 } 0455 0456 /** 0457 * Get the minor protocol version that is in use. 0458 * 0459 * @see _minorProtocolVersion 0460 * @return (int|NULL) The major protocol version in use, or NULL if no 0461 * minor version is specified. 0462 */ 0463 public function getMinorProtocolVersion() 0464 { 0465 return $this->_minorProtocolVersion; 0466 } 0467 0468 /** 0469 * Provides pre-processing for HTTP requests to APP services. 0470 * 0471 * 1. Checks the $data element and, if it's an entry, extracts the XML, 0472 * multipart data, edit link (PUT,DELETE), etc. 0473 * 2. If $data is a string, sets the default content-type header as 0474 * 'application/atom+xml' if it's not already been set. 0475 * 3. Adds a x-http-method override header and changes the HTTP method 0476 * to 'POST' if necessary as per getHttpMethodOverride() 0477 * 0478 * @param string $method The HTTP method for the request - 'GET', 'POST', 0479 * 'PUT', 'DELETE' 0480 * @param string $url The URL to which this request is being performed, 0481 * or null if found in $data 0482 * @param array $headers An associative array of HTTP headers for this 0483 * request 0484 * @param mixed $data The Zend_Gdata_App_Entry or XML for the 0485 * body of the request 0486 * @param string $contentTypeOverride The override value for the 0487 * content type of the request body 0488 * @return array An associative array containing the determined 0489 * 'method', 'url', 'data', 'headers', 'contentType' 0490 */ 0491 public function prepareRequest($method, 0492 $url = null, 0493 $headers = array(), 0494 $data = null, 0495 $contentTypeOverride = null) 0496 { 0497 // As a convenience, if $headers is null, we'll convert it back to 0498 // an empty array. 0499 if ($headers === null) { 0500 $headers = array(); 0501 } 0502 0503 $rawData = null; 0504 $finalContentType = null; 0505 if ($url == null) { 0506 $url = $this->_defaultPostUri; 0507 } 0508 0509 if (is_string($data)) { 0510 $rawData = $data; 0511 if ($contentTypeOverride === null) { 0512 $finalContentType = 'application/atom+xml'; 0513 } 0514 } elseif ($data instanceof Zend_Gdata_App_MediaEntry) { 0515 $rawData = $data->encode(); 0516 if ($data->getMediaSource() !== null) { 0517 $finalContentType = $rawData->getContentType(); 0518 $headers['MIME-version'] = '1.0'; 0519 $headers['Slug'] = $data->getMediaSource()->getSlug(); 0520 } else { 0521 $finalContentType = 'application/atom+xml'; 0522 } 0523 if ($method == 'PUT' || $method == 'DELETE') { 0524 $editLink = $data->getEditLink(); 0525 if ($editLink != null && $url == null) { 0526 $url = $editLink->getHref(); 0527 } 0528 } 0529 } elseif ($data instanceof Zend_Gdata_App_Entry) { 0530 $rawData = $data->saveXML(); 0531 $finalContentType = 'application/atom+xml'; 0532 if ($method == 'PUT' || $method == 'DELETE') { 0533 $editLink = $data->getEditLink(); 0534 if ($editLink != null) { 0535 $url = $editLink->getHref(); 0536 } 0537 } 0538 } elseif ($data instanceof Zend_Gdata_App_MediaSource) { 0539 $rawData = $data->encode(); 0540 if ($data->getSlug() !== null) { 0541 $headers['Slug'] = $data->getSlug(); 0542 } 0543 $finalContentType = $data->getContentType(); 0544 } 0545 0546 if ($method == 'DELETE') { 0547 $rawData = null; 0548 } 0549 0550 // Set an If-Match header if: 0551 // - This isn't a DELETE 0552 // - If this isn't a GET, the Etag isn't weak 0553 // - A similar header (If-Match/If-None-Match) hasn't already been 0554 // set. 0555 if ($method != 'DELETE' && ( 0556 !array_key_exists('If-Match', $headers) && 0557 !array_key_exists('If-None-Match', $headers) 0558 ) ) { 0559 $allowWeak = $method == 'GET'; 0560 if ($ifMatchHeader = $this->generateIfMatchHeaderData( 0561 $data, $allowWeak)) { 0562 $headers['If-Match'] = $ifMatchHeader; 0563 } 0564 } 0565 0566 if ($method != 'POST' && $method != 'GET' && Zend_Gdata_App::getHttpMethodOverride()) { 0567 $headers['x-http-method-override'] = $method; 0568 $method = 'POST'; 0569 } else { 0570 $headers['x-http-method-override'] = null; 0571 } 0572 0573 if ($contentTypeOverride != null) { 0574 $finalContentType = $contentTypeOverride; 0575 } 0576 0577 return array('method' => $method, 'url' => $url, 0578 'data' => $rawData, 'headers' => $headers, 0579 'contentType' => $finalContentType); 0580 } 0581 0582 /** 0583 * Performs a HTTP request using the specified method 0584 * 0585 * @param string $method The HTTP method for the request - 'GET', 'POST', 0586 * 'PUT', 'DELETE' 0587 * @param string $url The URL to which this request is being performed 0588 * @param array $headers An associative array of HTTP headers 0589 * for this request 0590 * @param string $body The body of the HTTP request 0591 * @param string $contentType The value for the content type 0592 * of the request body 0593 * @param int $remainingRedirects Number of redirects to follow if request 0594 * s results in one 0595 * @return Zend_Http_Response The response object 0596 */ 0597 public function performHttpRequest($method, $url, $headers = null, 0598 $body = null, $contentType = null, $remainingRedirects = null) 0599 { 0600 // require_once 'Zend/Http/Client/Exception.php'; 0601 if ($remainingRedirects === null) { 0602 $remainingRedirects = self::getMaxRedirects(); 0603 } 0604 if ($headers === null) { 0605 $headers = array(); 0606 } 0607 // Append a Gdata version header if protocol v2 or higher is in use. 0608 // (Protocol v1 does not use this header.) 0609 $major = $this->getMajorProtocolVersion(); 0610 $minor = $this->getMinorProtocolVersion(); 0611 if ($major >= 2) { 0612 $headers['GData-Version'] = $major + 0613 (($minor === null) ? '.' + $minor : ''); 0614 } 0615 0616 // check the overridden method 0617 if (($method == 'POST' || $method == 'PUT') && $body === null && 0618 $headers['x-http-method-override'] != 'DELETE') { 0619 // require_once 'Zend/Gdata/App/InvalidArgumentException.php'; 0620 throw new Zend_Gdata_App_InvalidArgumentException( 0621 'You must specify the data to post as either a ' . 0622 'string or a child of Zend_Gdata_App_Entry'); 0623 } 0624 if ($url === null) { 0625 // require_once 'Zend/Gdata/App/InvalidArgumentException.php'; 0626 throw new Zend_Gdata_App_InvalidArgumentException( 0627 'You must specify an URI to which to post.'); 0628 } 0629 $headers['Content-Type'] = $contentType; 0630 if (Zend_Gdata_App::getGzipEnabled()) { 0631 // some services require the word 'gzip' to be in the user-agent 0632 // header in addition to the accept-encoding header 0633 if (strpos($this->_httpClient->getHeader('User-Agent'), 0634 'gzip') === false) { 0635 $headers['User-Agent'] = 0636 $this->_httpClient->getHeader('User-Agent') . ' (gzip)'; 0637 } 0638 $headers['Accept-encoding'] = 'gzip, deflate'; 0639 } else { 0640 $headers['Accept-encoding'] = 'identity'; 0641 } 0642 0643 // Make sure the HTTP client object is 'clean' before making a request 0644 // In addition to standard headers to reset via resetParameters(), 0645 // also reset the Slug and If-Match headers 0646 $this->_httpClient->resetParameters(); 0647 $this->_httpClient->setHeaders(array('Slug', 'If-Match')); 0648 0649 // Set the params for the new request to be performed 0650 $this->_httpClient->setHeaders($headers); 0651 // require_once 'Zend/Uri/Http.php'; 0652 $uri = Zend_Uri_Http::fromString($url); 0653 preg_match("/^(.*?)(\?.*)?$/", $url, $matches); 0654 $this->_httpClient->setUri($matches[1]); 0655 $queryArray = $uri->getQueryAsArray(); 0656 foreach ($queryArray as $name => $value) { 0657 $this->_httpClient->setParameterGet($name, $value); 0658 } 0659 0660 0661 $this->_httpClient->setConfig(array('maxredirects' => 0)); 0662 0663 // Set the proper adapter if we are handling a streaming upload 0664 $usingMimeStream = false; 0665 $oldHttpAdapter = null; 0666 0667 if ($body instanceof Zend_Gdata_MediaMimeStream) { 0668 $usingMimeStream = true; 0669 $this->_httpClient->setRawDataStream($body, $contentType); 0670 $oldHttpAdapter = $this->_httpClient->getAdapter(); 0671 0672 if ($oldHttpAdapter instanceof Zend_Http_Client_Adapter_Proxy) { 0673 // require_once 'Zend/Gdata/HttpAdapterStreamingProxy.php'; 0674 $newAdapter = new Zend_Gdata_HttpAdapterStreamingProxy(); 0675 } else { 0676 // require_once 'Zend/Gdata/HttpAdapterStreamingSocket.php'; 0677 $newAdapter = new Zend_Gdata_HttpAdapterStreamingSocket(); 0678 } 0679 $this->_httpClient->setAdapter($newAdapter); 0680 } else { 0681 $this->_httpClient->setRawData($body, $contentType); 0682 } 0683 0684 try { 0685 $response = $this->_httpClient->request($method); 0686 // reset adapter 0687 if ($usingMimeStream) { 0688 $this->_httpClient->setAdapter($oldHttpAdapter); 0689 } 0690 } catch (Zend_Http_Client_Exception $e) { 0691 // reset adapter 0692 if ($usingMimeStream) { 0693 $this->_httpClient->setAdapter($oldHttpAdapter); 0694 } 0695 // require_once 'Zend/Gdata/App/HttpException.php'; 0696 throw new Zend_Gdata_App_HttpException($e->getMessage(), $e); 0697 } 0698 if ($response->isRedirect() && $response->getStatus() != '304') { 0699 if ($remainingRedirects > 0) { 0700 $newUrl = $response->getHeader('Location'); 0701 $response = $this->performHttpRequest( 0702 $method, $newUrl, $headers, $body, 0703 $contentType, $remainingRedirects); 0704 } else { 0705 // require_once 'Zend/Gdata/App/HttpException.php'; 0706 throw new Zend_Gdata_App_HttpException( 0707 'Number of redirects exceeds maximum', null, $response); 0708 } 0709 } 0710 if (!$response->isSuccessful()) { 0711 // require_once 'Zend/Gdata/App/HttpException.php'; 0712 $exceptionMessage = 'Expected response code 200, got ' . 0713 $response->getStatus(); 0714 if (self::getVerboseExceptionMessages()) { 0715 $exceptionMessage .= "\n" . $response->getBody(); 0716 } 0717 $exception = new Zend_Gdata_App_HttpException($exceptionMessage); 0718 $exception->setResponse($response); 0719 throw $exception; 0720 } 0721 return $response; 0722 } 0723 0724 /** 0725 * Imports a feed located at $uri. 0726 * 0727 * @param string $uri 0728 * @param Zend_Http_Client $client The client used for communication 0729 * @param string $className The class which is used as the return type 0730 * @param bool $useObjectMapping Enable/disable the use of XML to object mapping. 0731 * @throws Zend_Gdata_App_Exception 0732 * @return string|Zend_Gdata_App_Feed Returns string only if the fourth 0733 * parameter ($useObjectMapping) is set 0734 * to false. 0735 */ 0736 public static function import($uri, $client = null, 0737 $className='Zend_Gdata_App_Feed', $useObjectMapping = true) 0738 { 0739 $app = new Zend_Gdata_App($client); 0740 $requestData = $app->prepareRequest('GET', $uri); 0741 $response = $app->performHttpRequest( 0742 $requestData['method'], $requestData['url']); 0743 0744 $feedContent = $response->getBody(); 0745 if (false === $useObjectMapping) { 0746 return $feedContent; 0747 } 0748 $feed = self::importString($feedContent, $className); 0749 if ($client != null) { 0750 $feed->setHttpClient($client); 0751 } 0752 return $feed; 0753 } 0754 0755 /** 0756 * Imports the specified URL (non-statically). 0757 * 0758 * @param string $url The URL to import 0759 * @param string $className The class which is used as the return type 0760 * @param array $extraHeaders Extra headers to add to the request, as an 0761 * array of string-based key/value pairs. 0762 * @throws Zend_Gdata_App_Exception 0763 * @return string|Zend_Gdata_App_Feed Returns string only if the object 0764 * mapping has been disabled explicitly 0765 * by passing false to the 0766 * useObjectMapping() function. 0767 */ 0768 public function importUrl($url, $className='Zend_Gdata_App_Feed', 0769 $extraHeaders = array()) 0770 { 0771 $response = $this->get($url, $extraHeaders); 0772 0773 $feedContent = $response->getBody(); 0774 if (!$this->_useObjectMapping) { 0775 return $feedContent; 0776 } 0777 0778 $protocolVersionStr = $response->getHeader('GData-Version'); 0779 $majorProtocolVersion = null; 0780 $minorProtocolVersion = null; 0781 if ($protocolVersionStr !== null) { 0782 // Extract protocol major and minor version from header 0783 $delimiterPos = strpos($protocolVersionStr, '.'); 0784 $length = strlen($protocolVersionStr); 0785 $major = substr($protocolVersionStr, 0, $delimiterPos); 0786 $minor = substr($protocolVersionStr, $delimiterPos + 1, $length); 0787 $majorProtocolVersion = $major; 0788 $minorProtocolVersion = $minor; 0789 } 0790 0791 $feed = self::importString($feedContent, $className, 0792 $majorProtocolVersion, $minorProtocolVersion); 0793 if ($this->getHttpClient() != null) { 0794 $feed->setHttpClient($this->getHttpClient()); 0795 } 0796 $etag = $response->getHeader('ETag'); 0797 if ($etag !== null) { 0798 $feed->setEtag($etag); 0799 } 0800 return $feed; 0801 } 0802 0803 0804 /** 0805 * Imports a feed represented by $string. 0806 * 0807 * @param string $string 0808 * @param string $className The class which is used as the return type 0809 * @param integer $majorProcolVersion (optional) The major protocol version 0810 * of the data model object that is to be created. 0811 * @param integer $minorProcolVersion (optional) The minor protocol version 0812 * of the data model object that is to be created. 0813 * @throws Zend_Gdata_App_Exception 0814 * @return Zend_Gdata_App_Feed 0815 */ 0816 public static function importString($string, 0817 $className='Zend_Gdata_App_Feed', $majorProtocolVersion = null, 0818 $minorProtocolVersion = null) 0819 { 0820 if (!class_exists($className, false)) { 0821 // require_once 'Zend/Loader.php'; 0822 @Zend_Loader::loadClass($className); 0823 } 0824 0825 // Load the feed as an XML DOMDocument object 0826 @ini_set('track_errors', 1); 0827 $doc = new DOMDocument(); 0828 $doc = @Zend_Xml_Security::scan($string, $doc); 0829 @ini_restore('track_errors'); 0830 0831 if (!$doc) { 0832 // require_once 'Zend/Gdata/App/Exception.php'; 0833 throw new Zend_Gdata_App_Exception( 0834 "DOMDocument cannot parse XML: $php_errormsg"); 0835 } 0836 0837 $feed = new $className(); 0838 $feed->setMajorProtocolVersion($majorProtocolVersion); 0839 $feed->setMinorProtocolVersion($minorProtocolVersion); 0840 $feed->transferFromXML($string); 0841 $feed->setHttpClient(self::getstaticHttpClient()); 0842 return $feed; 0843 } 0844 0845 0846 /** 0847 * Imports a feed from a file located at $filename. 0848 * 0849 * @param string $filename 0850 * @param string $className The class which is used as the return type 0851 * @param string $useIncludePath Whether the include_path should be searched 0852 * @throws Zend_Gdata_App_Exception 0853 * @return Zend_Gdata_App_Feed 0854 */ 0855 public static function importFile($filename, 0856 $className='Zend_Gdata_App_Feed', $useIncludePath = false) 0857 { 0858 @ini_set('track_errors', 1); 0859 $feed = @file_get_contents($filename, $useIncludePath); 0860 @ini_restore('track_errors'); 0861 if ($feed === false) { 0862 // require_once 'Zend/Gdata/App/Exception.php'; 0863 throw new Zend_Gdata_App_Exception( 0864 "File could not be loaded: $php_errormsg"); 0865 } 0866 return self::importString($feed, $className); 0867 } 0868 0869 /** 0870 * GET a URI using client object. 0871 * 0872 * @param string $uri GET URI 0873 * @param array $extraHeaders Extra headers to add to the request, as an 0874 * array of string-based key/value pairs. 0875 * @throws Zend_Gdata_App_HttpException 0876 * @return Zend_Http_Response 0877 */ 0878 public function get($uri, $extraHeaders = array()) 0879 { 0880 $requestData = $this->prepareRequest('GET', $uri, $extraHeaders); 0881 return $this->performHttpRequest( 0882 $requestData['method'], $requestData['url'], 0883 $requestData['headers']); 0884 } 0885 0886 /** 0887 * POST data with client object 0888 * 0889 * @param mixed $data The Zend_Gdata_App_Entry or XML to post 0890 * @param string $uri POST URI 0891 * @param array $headers Additional HTTP headers to insert. 0892 * @param string $contentType Content-type of the data 0893 * @param array $extraHeaders Extra headers to add to the request, as an 0894 * array of string-based key/value pairs. 0895 * @return Zend_Http_Response 0896 * @throws Zend_Gdata_App_Exception 0897 * @throws Zend_Gdata_App_HttpException 0898 * @throws Zend_Gdata_App_InvalidArgumentException 0899 */ 0900 public function post($data, $uri = null, $remainingRedirects = null, 0901 $contentType = null, $extraHeaders = null) 0902 { 0903 $requestData = $this->prepareRequest( 0904 'POST', $uri, $extraHeaders, $data, $contentType); 0905 return $this->performHttpRequest( 0906 $requestData['method'], $requestData['url'], 0907 $requestData['headers'], $requestData['data'], 0908 $requestData['contentType']); 0909 } 0910 0911 /** 0912 * PUT data with client object 0913 * 0914 * @param mixed $data The Zend_Gdata_App_Entry or XML to post 0915 * @param string $uri PUT URI 0916 * @param array $headers Additional HTTP headers to insert. 0917 * @param string $contentType Content-type of the data 0918 * @param array $extraHeaders Extra headers to add to the request, as an 0919 * array of string-based key/value pairs. 0920 * @return Zend_Http_Response 0921 * @throws Zend_Gdata_App_Exception 0922 * @throws Zend_Gdata_App_HttpException 0923 * @throws Zend_Gdata_App_InvalidArgumentException 0924 */ 0925 public function put($data, $uri = null, $remainingRedirects = null, 0926 $contentType = null, $extraHeaders = null) 0927 { 0928 $requestData = $this->prepareRequest( 0929 'PUT', $uri, $extraHeaders, $data, $contentType); 0930 return $this->performHttpRequest( 0931 $requestData['method'], $requestData['url'], 0932 $requestData['headers'], $requestData['data'], 0933 $requestData['contentType']); 0934 } 0935 0936 /** 0937 * DELETE entry with client object 0938 * 0939 * @param mixed $data The Zend_Gdata_App_Entry or URL to delete 0940 * @return void 0941 * @throws Zend_Gdata_App_Exception 0942 * @throws Zend_Gdata_App_HttpException 0943 * @throws Zend_Gdata_App_InvalidArgumentException 0944 */ 0945 public function delete($data, $remainingRedirects = null) 0946 { 0947 if (is_string($data)) { 0948 $requestData = $this->prepareRequest('DELETE', $data); 0949 } else { 0950 $headers = array(); 0951 0952 $requestData = $this->prepareRequest( 0953 'DELETE', null, $headers, $data); 0954 } 0955 return $this->performHttpRequest($requestData['method'], 0956 $requestData['url'], 0957 $requestData['headers'], 0958 '', 0959 $requestData['contentType'], 0960 $remainingRedirects); 0961 } 0962 0963 /** 0964 * Inserts an entry to a given URI and returns the response as a 0965 * fully formed Entry. 0966 * 0967 * @param mixed $data The Zend_Gdata_App_Entry or XML to post 0968 * @param string $uri POST URI 0969 * @param string $className The class of entry to be returned. 0970 * @param array $extraHeaders Extra headers to add to the request, as an 0971 * array of string-based key/value pairs. 0972 * @return Zend_Gdata_App_Entry The entry returned by the service after 0973 * insertion. 0974 */ 0975 public function insertEntry($data, $uri, $className='Zend_Gdata_App_Entry', 0976 $extraHeaders = array()) 0977 { 0978 if (!class_exists($className, false)) { 0979 // require_once 'Zend/Loader.php'; 0980 @Zend_Loader::loadClass($className); 0981 } 0982 0983 $response = $this->post($data, $uri, null, null, $extraHeaders); 0984 0985 $returnEntry = new $className($response->getBody()); 0986 $returnEntry->setHttpClient(self::getstaticHttpClient()); 0987 0988 $etag = $response->getHeader('ETag'); 0989 if ($etag !== null) { 0990 $returnEntry->setEtag($etag); 0991 } 0992 0993 return $returnEntry; 0994 } 0995 0996 /** 0997 * Update an entry 0998 * 0999 * @param mixed $data Zend_Gdata_App_Entry or XML (w/ID and link rel='edit') 1000 * @param string|null The URI to send requests to, or null if $data 1001 * contains the URI. 1002 * @param string|null The name of the class that should be deserialized 1003 * from the server response. If null, then 'Zend_Gdata_App_Entry' 1004 * will be used. 1005 * @param array $extraHeaders Extra headers to add to the request, as an 1006 * array of string-based key/value pairs. 1007 * @return Zend_Gdata_App_Entry The entry returned from the server 1008 * @throws Zend_Gdata_App_Exception 1009 */ 1010 public function updateEntry($data, $uri = null, $className = null, 1011 $extraHeaders = array()) 1012 { 1013 if ($className === null && $data instanceof Zend_Gdata_App_Entry) { 1014 $className = get_class($data); 1015 } elseif ($className === null) { 1016 $className = 'Zend_Gdata_App_Entry'; 1017 } 1018 1019 if (!class_exists($className, false)) { 1020 // require_once 'Zend/Loader.php'; 1021 @Zend_Loader::loadClass($className); 1022 } 1023 1024 $response = $this->put($data, $uri, null, null, $extraHeaders); 1025 $returnEntry = new $className($response->getBody()); 1026 $returnEntry->setHttpClient(self::getstaticHttpClient()); 1027 1028 $etag = $response->getHeader('ETag'); 1029 if ($etag !== null) { 1030 $returnEntry->setEtag($etag); 1031 } 1032 1033 return $returnEntry; 1034 } 1035 1036 /** 1037 * Provides a magic factory method to instantiate new objects with 1038 * shorter syntax than would otherwise be required by the Zend Framework 1039 * naming conventions. For instance, to construct a new 1040 * Zend_Gdata_Calendar_Extension_Color, a developer simply needs to do 1041 * $gCal->newColor(). For this magic constructor, packages are searched 1042 * in the same order as which they appear in the $_registeredPackages 1043 * array 1044 * 1045 * @param string $method The method name being called 1046 * @param array $args The arguments passed to the call 1047 * @throws Zend_Gdata_App_Exception 1048 */ 1049 public function __call($method, $args) 1050 { 1051 if (preg_match('/^new(\w+)/', $method, $matches)) { 1052 $class = $matches[1]; 1053 $foundClassName = null; 1054 foreach ($this->_registeredPackages as $name) { 1055 try { 1056 // Autoloading disabled on next line for compatibility 1057 // with magic factories. See ZF-6660. 1058 if (!class_exists($name . '_' . $class, false)) { 1059 // require_once 'Zend/Loader.php'; 1060 @Zend_Loader::loadClass($name . '_' . $class); 1061 } 1062 $foundClassName = $name . '_' . $class; 1063 break; 1064 } catch (Zend_Exception $e) { 1065 // package wasn't here- continue searching 1066 } catch (ErrorException $e) { 1067 // package wasn't here- continue searching 1068 // @see ZF-7013 and ZF-11959 1069 } 1070 } 1071 if ($foundClassName != null) { 1072 $reflectionObj = new ReflectionClass($foundClassName); 1073 $instance = $reflectionObj->newInstanceArgs($args); 1074 if ($instance instanceof Zend_Gdata_App_FeedEntryParent) { 1075 $instance->setHttpClient($this->_httpClient); 1076 1077 // Propogate version data 1078 $instance->setMajorProtocolVersion( 1079 $this->_majorProtocolVersion); 1080 $instance->setMinorProtocolVersion( 1081 $this->_minorProtocolVersion); 1082 } 1083 return $instance; 1084 } else { 1085 // require_once 'Zend/Gdata/App/Exception.php'; 1086 throw new Zend_Gdata_App_Exception( 1087 "Unable to find '${class}' in registered packages"); 1088 } 1089 } else { 1090 // require_once 'Zend/Gdata/App/Exception.php'; 1091 throw new Zend_Gdata_App_Exception("No such method ${method}"); 1092 } 1093 } 1094 1095 /** 1096 * Retrieve all entries for a feed, iterating through pages as necessary. 1097 * Be aware that calling this function on a large dataset will take a 1098 * significant amount of time to complete. In some cases this may cause 1099 * execution to timeout without proper precautions in place. 1100 * 1101 * @param object $feed The feed to iterate through. 1102 * @return mixed A new feed of the same type as the one originally 1103 * passed in, containing all relevent entries. 1104 */ 1105 public function retrieveAllEntriesForFeed($feed) { 1106 $feedClass = get_class($feed); 1107 $reflectionObj = new ReflectionClass($feedClass); 1108 $result = $reflectionObj->newInstance(); 1109 do { 1110 foreach ($feed as $entry) { 1111 $result->addEntry($entry); 1112 } 1113 1114 $next = $feed->getLink('next'); 1115 if ($next !== null) { 1116 $feed = $this->getFeed($next->href, $feedClass); 1117 } else { 1118 $feed = null; 1119 } 1120 } 1121 while ($feed != null); 1122 return $result; 1123 } 1124 1125 /** 1126 * This method enables logging of requests by changing the 1127 * Zend_Http_Client_Adapter used for performing the requests. 1128 * NOTE: This will not work if you have customized the adapter 1129 * already to use a proxy server or other interface. 1130 * 1131 * @param string $logfile The logfile to use when logging the requests 1132 */ 1133 public function enableRequestDebugLogging($logfile) 1134 { 1135 $this->_httpClient->setConfig(array( 1136 'adapter' => 'Zend_Gdata_App_LoggingHttpClientAdapterSocket', 1137 'logfile' => $logfile 1138 )); 1139 } 1140 1141 /** 1142 * Retrieve next set of results based on a given feed. 1143 * 1144 * @param Zend_Gdata_App_Feed $feed The feed from which to 1145 * retreive the next set of results. 1146 * @param string $className (optional) The class of feed to be returned. 1147 * If null, the next feed (if found) will be the same class as 1148 * the feed that was given as the first argument. 1149 * @return Zend_Gdata_App_Feed|null Returns a 1150 * Zend_Gdata_App_Feed or null if no next set of results 1151 * exists. 1152 */ 1153 public function getNextFeed($feed, $className = null) 1154 { 1155 $nextLink = $feed->getNextLink(); 1156 if (!$nextLink) { 1157 return null; 1158 } 1159 $nextLinkHref = $nextLink->getHref(); 1160 1161 if ($className === null) { 1162 $className = get_class($feed); 1163 } 1164 1165 return $this->getFeed($nextLinkHref, $className); 1166 } 1167 1168 /** 1169 * Retrieve previous set of results based on a given feed. 1170 * 1171 * @param Zend_Gdata_App_Feed $feed The feed from which to 1172 * retreive the previous set of results. 1173 * @param string $className (optional) The class of feed to be returned. 1174 * If null, the previous feed (if found) will be the same class as 1175 * the feed that was given as the first argument. 1176 * @return Zend_Gdata_App_Feed|null Returns a 1177 * Zend_Gdata_App_Feed or null if no previous set of results 1178 * exists. 1179 */ 1180 public function getPreviousFeed($feed, $className = null) 1181 { 1182 $previousLink = $feed->getPreviousLink(); 1183 if (!$previousLink) { 1184 return null; 1185 } 1186 $previousLinkHref = $previousLink->getHref(); 1187 1188 if ($className === null) { 1189 $className = get_class($feed); 1190 } 1191 1192 return $this->getFeed($previousLinkHref, $className); 1193 } 1194 1195 /** 1196 * Returns the data for an If-Match header based on the current Etag 1197 * property. If Etags are not supported by the server or cannot be 1198 * extracted from the data, then null will be returned. 1199 * 1200 * @param boolean $allowWeak If false, then if a weak Etag is detected, 1201 * then return null rather than the Etag. 1202 * @return string|null $data 1203 */ 1204 public function generateIfMatchHeaderData($data, $allowWeek) 1205 { 1206 $result = ''; 1207 // Set an If-Match header if an ETag has been set (version >= 2 only) 1208 if ($this->_majorProtocolVersion >= 2 && 1209 $data instanceof Zend_Gdata_App_Entry) { 1210 $etag = $data->getEtag(); 1211 if (($etag !== null) && 1212 ($allowWeek || substr($etag, 0, 2) != 'W/')) { 1213 $result = $data->getEtag(); 1214 } 1215 } 1216 return $result; 1217 } 1218 1219 /** 1220 * Determine whether service object is using XML to object mapping. 1221 * 1222 * @return boolean True if service object is using XML to object mapping, 1223 * false otherwise. 1224 */ 1225 public function usingObjectMapping() 1226 { 1227 return $this->_useObjectMapping; 1228 } 1229 1230 /** 1231 * Enable/disable the use of XML to object mapping. 1232 * 1233 * @param boolean $value Pass in true to use the XML to object mapping. 1234 * Pass in false or null to disable it. 1235 * @return void 1236 */ 1237 public function useObjectMapping($value) 1238 { 1239 if ($value === True) { 1240 $this->_useObjectMapping = true; 1241 } else { 1242 $this->_useObjectMapping = false; 1243 } 1244 } 1245 1246 }