1<?php 2/** 3 * Copyright 2010 Google Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18/** 19 * Implements the actual methods/resources of the discovered Google API using magic function 20 * calling overloading (__call()), which on call will see if the method name (plus.activities.list) 21 * is available in this service, and if so construct an apiHttpRequest representing it. 22 * 23 * @author Chris Chabot <chabotc@google.com> 24 * @author Chirag Shah <chirags@google.com> 25 * 26 */ 27class Google_ServiceResource { 28 // Valid query parameters that work, but don't appear in discovery. 29 private $stackParameters = array( 30 'alt' => array('type' => 'string', 'location' => 'query'), 31 'boundary' => array('type' => 'string', 'location' => 'query'), 32 'fields' => array('type' => 'string', 'location' => 'query'), 33 'trace' => array('type' => 'string', 'location' => 'query'), 34 'userIp' => array('type' => 'string', 'location' => 'query'), 35 'userip' => array('type' => 'string', 'location' => 'query'), 36 'quotaUser' => array('type' => 'string', 'location' => 'query'), 37 'file' => array('type' => 'complex', 'location' => 'body'), 38 'data' => array('type' => 'string', 'location' => 'body'), 39 'mimeType' => array('type' => 'string', 'location' => 'header'), 40 'uploadType' => array('type' => 'string', 'location' => 'query'), 41 'mediaUpload' => array('type' => 'complex', 'location' => 'query'), 42 ); 43 44 /** @var Google_Service $service */ 45 private $service; 46 47 /** @var string $serviceName */ 48 private $serviceName; 49 50 /** @var string $resourceName */ 51 private $resourceName; 52 53 /** @var array $methods */ 54 private $methods; 55 56 public function __construct($service, $serviceName, $resourceName, $resource) { 57 $this->service = $service; 58 $this->serviceName = $serviceName; 59 $this->resourceName = $resourceName; 60 $this->methods = isset($resource['methods']) ? $resource['methods'] : array($resourceName => $resource); 61 } 62 63 /** 64 * @param $name 65 * @param $arguments 66 * @return Google_HttpRequest|array 67 * @throws Google_Exception 68 */ 69 public function __call($name, $arguments) { 70 if (! isset($this->methods[$name])) { 71 throw new Google_Exception("Unknown function: {$this->serviceName}->{$this->resourceName}->{$name}()"); 72 } 73 $method = $this->methods[$name]; 74 $parameters = $arguments[0]; 75 76 // postBody is a special case since it's not defined in the discovery document as parameter, but we abuse the param entry for storing it 77 $postBody = null; 78 if (isset($parameters['postBody'])) { 79 if (is_object($parameters['postBody'])) { 80 $this->stripNull($parameters['postBody']); 81 } 82 83 // Some APIs require the postBody to be set under the data key. 84 if (is_array($parameters['postBody']) && 'latitude' == $this->serviceName) { 85 if (!isset($parameters['postBody']['data'])) { 86 $rawBody = $parameters['postBody']; 87 unset($parameters['postBody']); 88 $parameters['postBody']['data'] = $rawBody; 89 } 90 } 91 92 $postBody = is_array($parameters['postBody']) || is_object($parameters['postBody']) 93 ? json_encode($parameters['postBody']) 94 : $parameters['postBody']; 95 unset($parameters['postBody']); 96 97 if (isset($parameters['optParams'])) { 98 $optParams = $parameters['optParams']; 99 unset($parameters['optParams']); 100 $parameters = array_merge($parameters, $optParams); 101 } 102 } 103 104 if (!isset($method['parameters'])) { 105 $method['parameters'] = array(); 106 } 107 108 $method['parameters'] = array_merge($method['parameters'], $this->stackParameters); 109 foreach ($parameters as $key => $val) { 110 if ($key != 'postBody' && ! isset($method['parameters'][$key])) { 111 throw new Google_Exception("($name) unknown parameter: '$key'"); 112 } 113 } 114 if (isset($method['parameters'])) { 115 foreach ($method['parameters'] as $paramName => $paramSpec) { 116 if (isset($paramSpec['required']) && $paramSpec['required'] && ! isset($parameters[$paramName])) { 117 throw new Google_Exception("($name) missing required param: '$paramName'"); 118 } 119 if (isset($parameters[$paramName])) { 120 $value = $parameters[$paramName]; 121 $parameters[$paramName] = $paramSpec; 122 $parameters[$paramName]['value'] = $value; 123 unset($parameters[$paramName]['required']); 124 } else { 125 unset($parameters[$paramName]); 126 } 127 } 128 } 129 130 // Discovery v1.0 puts the canonical method id under the 'id' field. 131 if (! isset($method['id'])) { 132 $method['id'] = $method['rpcMethod']; 133 } 134 135 // Discovery v1.0 puts the canonical path under the 'path' field. 136 if (! isset($method['path'])) { 137 $method['path'] = $method['restPath']; 138 } 139 140 $servicePath = $this->service->servicePath; 141 142 // Process Media Request 143 $contentType = false; 144 if (isset($method['mediaUpload'])) { 145 $media = Google_MediaFileUpload::process($postBody, $parameters); 146 if ($media) { 147 $contentType = isset($media['content-type']) ? $media['content-type']: null; 148 $postBody = isset($media['postBody']) ? $media['postBody'] : null; 149 $servicePath = $method['mediaUpload']['protocols']['simple']['path']; 150 $method['path'] = ''; 151 } 152 } 153 154 $url = Google_REST::createRequestUri($servicePath, $method['path'], $parameters); 155 $httpRequest = new Google_HttpRequest($url, $method['httpMethod'], null, $postBody); 156 if ($postBody) { 157 $contentTypeHeader = array(); 158 if (isset($contentType) && $contentType) { 159 $contentTypeHeader['content-type'] = $contentType; 160 } else { 161 $contentTypeHeader['content-type'] = 'application/json; charset=UTF-8'; 162 $contentTypeHeader['content-length'] = Google_Utils::getStrLen($postBody); 163 } 164 $httpRequest->setRequestHeaders($contentTypeHeader); 165 } 166 167 $httpRequest = Google_Client::$auth->sign($httpRequest); 168 if (Google_Client::$useBatch) { 169 return $httpRequest; 170 } 171 172 // Terminate immediately if this is a resumable request. 173 if (isset($parameters['uploadType']['value']) 174 && Google_MediaFileUpload::UPLOAD_RESUMABLE_TYPE == $parameters['uploadType']['value']) { 175 $contentTypeHeader = array(); 176 if (isset($contentType) && $contentType) { 177 $contentTypeHeader['content-type'] = $contentType; 178 } 179 $httpRequest->setRequestHeaders($contentTypeHeader); 180 if ($postBody) { 181 $httpRequest->setPostBody($postBody); 182 } 183 return $httpRequest; 184 } 185 186 return Google_REST::execute($httpRequest); 187 } 188 189 public function useObjects() { 190 global $apiConfig; 191 return (isset($apiConfig['use_objects']) && $apiConfig['use_objects']); 192 } 193 194 protected function stripNull(&$o) { 195 $o = (array) $o; 196 foreach ($o as $k => $v) { 197 if ($v === null || strstr($k, "\0*\0__")) { 198 unset($o[$k]); 199 } 200 elseif (is_object($v) || is_array($v)) { 201 $this->stripNull($o[$k]); 202 } 203 } 204 } 205} 206