├── Aliyun └── ACM │ ├── Autoload.php │ ├── Client.php │ ├── Model │ ├── Exception.php │ └── Server.php │ ├── RequestCore.php │ └── Util.php ├── README.md └── demo └── sample.php /Aliyun/ACM/Autoload.php: -------------------------------------------------------------------------------- 1 | 5) 13 | $classPath = array_slice($classPath, 0, 5); 14 | if(strpos($className, 'Request') !== false){ 15 | $lastPath = end($classPath); 16 | array_pop($classPath); 17 | array_push($classPath,'Request'); 18 | array_push($classPath, $lastPath); 19 | } 20 | if(strpos($className, 'Response') !== false){ 21 | $lastPath = end($classPath); 22 | array_pop($classPath); 23 | array_push($classPath,'Response'); 24 | array_push($classPath, $lastPath); 25 | } 26 | $filePath = realpath(dirname(__FILE__) . '/../../' . implode('/', $classPath) . '.php'); 27 | if (file_exists($filePath)) 28 | require_once($filePath); 29 | } 30 | } 31 | 32 | spl_autoload_register('Aliyun_ACM_Autoload'); 33 | -------------------------------------------------------------------------------- /Aliyun/ACM/Client.php: -------------------------------------------------------------------------------- 1 | endPoint = $endpoint; 33 | $this->port = $port; 34 | } 35 | 36 | /** 37 | * @param mixed $accessKey 38 | */ 39 | public function setAccessKey($accessKey) 40 | { 41 | $this->accessKey = $accessKey; 42 | } 43 | 44 | /** 45 | * @param mixed $secretKey 46 | */ 47 | public function setSecretKey($secretKey) 48 | { 49 | $this->secretKey = $secretKey; 50 | } 51 | 52 | /** 53 | * @param mixed $nameSpace 54 | */ 55 | public function setNameSpace($nameSpace) 56 | { 57 | $this->nameSpace = $nameSpace; 58 | } 59 | 60 | /** 61 | * @param mixed $appName 62 | */ 63 | public function setAppName($appName) 64 | { 65 | $this->appName = $appName; 66 | } 67 | 68 | private function getServerListStr(){ 69 | $server_host = str_replace(array('host','port'), array($this->endPoint, $this->port), 70 | 'http://host:port/diamond-server/diamond'); 71 | $request = new RequestCore(); 72 | $request->set_request_url($server_host); 73 | $request->send_request(true); 74 | if($request->get_response_code() != '200'){ 75 | print '[getServerList] got invalid http response: ('.$server_host.'.'; 76 | } 77 | $serverRawList = $request->get_response_body(); 78 | return $serverRawList; 79 | } 80 | 81 | public function refreshServerList(){ 82 | $this->serverList = array(); 83 | $serverRawList = $this->getServerListStr(); 84 | if(is_string($serverRawList)){ 85 | $serverArray = explode("\n", $serverRawList); 86 | $serverArray = array_filter($serverArray); 87 | foreach ($serverArray as $value){ 88 | $value = trim($value); 89 | $singleServerList = explode(':', $value); 90 | $singleServer = null; 91 | if(count($singleServerList) == 1){ 92 | $singleServer = new Aliyun_ACM_Model_Server($value, 93 | constant('DEFAULT_PORT'), 94 | Aliyun_ACM_Util::isIpv4($value)); 95 | }else{ 96 | $singleServer = new Aliyun_ACM_Model_Server($singleServerList[0], 97 | $singleServerList[1], 98 | Aliyun_ACM_Util::isIpv4($value)); 99 | } 100 | $this->serverList[$singleServer->url] = $singleServer; 101 | } 102 | } 103 | } 104 | 105 | public function getServerList(){ 106 | return $this->serverList; 107 | } 108 | 109 | public function getConfig($dataId, $group){ 110 | if(!is_string($this->secretKey) || 111 | !is_string($this->accessKey)){ 112 | throw new Aliyun_ACM_Exception ( 'Invalid auth string', "invalid auth info for dataId: $dataId" ); 113 | } 114 | 115 | Aliyun_ACM_Util::checkDataId($dataId); 116 | $group = Aliyun_ACM_Util::checkGroup($group); 117 | 118 | $servers = $this->serverList; 119 | $singleServer = $servers[array_rand($servers)]; 120 | 121 | $acm_host = str_replace(array('host','port'), array($singleServer->url, $singleServer->port), 122 | 'http://host:port/diamond-server/config.co'); 123 | 124 | $acm_host .= "?dataId=".urlencode($dataId)."&group=".urlencode($group) 125 | ."&tenant=".urlencode($this->nameSpace); 126 | 127 | $request = new RequestCore(); 128 | $request->set_request_url($acm_host); 129 | 130 | $headers = $this->getCommonHeaders($group); 131 | 132 | foreach ($headers as $header_key => $header_val) { 133 | $request->add_header($header_key, $header_val); 134 | } 135 | 136 | $request->send_request(true); 137 | if($request->get_response_code() != '200'){ 138 | print '[GETCONFIG] got invalid http response: ('.$acm_host.'.'; 139 | } 140 | $rawData = $request->get_response_body(); 141 | return $rawData; 142 | } 143 | 144 | public function publishConfig($dataId, $group, $content){ 145 | if(!is_string($this->secretKey) || 146 | !is_string($this->accessKey)){ 147 | throw new Aliyun_ACM_Exception ( 'Invalid auth string', "invalid auth info for dataId: $dataId" ); 148 | } 149 | 150 | Aliyun_ACM_Util::checkDataId($dataId); 151 | $group = Aliyun_ACM_Util::checkGroup($group); 152 | 153 | $servers = $this->serverList; 154 | $singleServer = $servers[array_rand($servers)]; 155 | 156 | $acm_host = str_replace(array('host','port'), array($singleServer->url, $singleServer->port), 157 | 'http://host:port/diamond-server/basestone.do?method=syncUpdateAll'); 158 | 159 | $acm_body = "dataId=".urlencode($dataId)."&group=".urlencode($group) 160 | ."&tenant=".urlencode($this->nameSpace) 161 | ."&content=".urlencode($content); 162 | if(is_string($this->appName)){ 163 | $acm_body .= "&appName=".$this->appName; 164 | } 165 | 166 | $request = new RequestCore(); 167 | $request->set_body($acm_body); 168 | $request->set_request_url($acm_host); 169 | 170 | $headers = $this->getCommonHeaders($group); 171 | 172 | foreach ($headers as $header_key => $header_val) { 173 | $request->add_header($header_key, $header_val); 174 | } 175 | $request->set_method("post"); 176 | $request->send_request(true); 177 | if($request->get_response_code() != '200'){ 178 | print '[PUBLISHCONFIG] got invalid http response: ('.$acm_host.'#'.$request->get_response_code(); 179 | } 180 | $rawData = $request->get_response_body(); 181 | return $rawData; 182 | } 183 | 184 | public function removeConfig($dataId, $group){ 185 | if(!is_string($this->secretKey) || 186 | !is_string($this->accessKey)){ 187 | throw new Aliyun_ACM_Exception ( 'Invalid auth string', "invalid auth info for dataId: $dataId" ); 188 | } 189 | 190 | Aliyun_ACM_Util::checkDataId($dataId); 191 | $group = Aliyun_ACM_Util::checkGroup($group); 192 | 193 | $servers = $this->serverList; 194 | $singleServer = $servers[array_rand($servers)]; 195 | 196 | $acm_host = str_replace(array('host','port'), array($singleServer->url, $singleServer->port), 197 | 'http://host:port/diamond-server//datum.do?method=deleteAllDatums'); 198 | 199 | $acm_body = "dataId=".urlencode($dataId)."&group=".urlencode($group) 200 | ."&tenant=".urlencode($this->nameSpace); 201 | 202 | $request = new RequestCore(); 203 | $request->set_body($acm_body); 204 | $request->set_request_url($acm_host); 205 | 206 | $headers = $this->getCommonHeaders($group); 207 | 208 | foreach ($headers as $header_key => $header_val) { 209 | $request->add_header($header_key, $header_val); 210 | } 211 | $request->set_method("post"); 212 | $request->send_request(true); 213 | if($request->get_response_code() != '200'){ 214 | print '[REMOVECONFIG] got invalid http response: ('.$acm_host.'#'.$request->get_response_code(); 215 | } 216 | $rawData = $request->get_response_body(); 217 | return $rawData; 218 | } 219 | 220 | private function getCommonHeaders($group){ 221 | $headers = array(); 222 | $headers['Diamond-Client-AppName'] = 'ACM-SDK-PHP'; 223 | $headers['Client-Version'] = '0.0.1'; 224 | $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; 225 | $headers['exConfigInfo'] = 'true'; 226 | $headers['Spas-AccessKey'] = $this->accessKey; 227 | 228 | $ts = round(microtime(true) * 1000); 229 | $headers['timeStamp'] = $ts; 230 | 231 | $signStr = $this->nameSpace.'+'; 232 | if(is_string($group)){ 233 | $signStr .= $group . "+"; 234 | } 235 | $signStr = $signStr.$ts; 236 | $headers['Spas-Signature'] = base64_encode(hash_hmac('sha1', $signStr, $this->secretKey,true)); 237 | return $headers; 238 | } 239 | 240 | } -------------------------------------------------------------------------------- /Aliyun/ACM/Model/Exception.php: -------------------------------------------------------------------------------- 1 | code = $code; 29 | $this->message = $message; 30 | $this->requestId = $requestId; 31 | } 32 | 33 | /** 34 | * The __toString() method allows a class to decide how it will react when 35 | * it is treated like a string. 36 | * 37 | * @return string 38 | */ 39 | public function __toString() { 40 | return "Aliyun_ACM_Exception: \n{\n ErrorCode: $this->code,\n ErrorMessage: $this->message\n RequestId: $this->requestId\n}\n"; 41 | } 42 | 43 | /** 44 | * Get Aliyun_ACM_Exception error code. 45 | * 46 | * @return string 47 | */ 48 | public function getErrorCode() { 49 | return $this->code; 50 | } 51 | 52 | /** 53 | * Get Aliyun_ACM_Exception error message. 54 | * 55 | * @return string 56 | */ 57 | public function getErrorMessage() { 58 | return $this->message; 59 | } 60 | 61 | /** 62 | * Get log service sever requestid, '' is set if client or Http error. 63 | * 64 | * @return string 65 | */ 66 | public function getRequestId() { 67 | return $this->requestId; 68 | } 69 | } -------------------------------------------------------------------------------- /Aliyun/ACM/Model/Server.php: -------------------------------------------------------------------------------- 1 | url = $url; 22 | $this->port = $port; 23 | $this->isIpv4 = $isIpv4; 24 | } 25 | } -------------------------------------------------------------------------------- /Aliyun/ACM/RequestCore.php: -------------------------------------------------------------------------------- 1 | ). 91 | */ 92 | public $request_class = 'RequestCore'; 93 | 94 | /** 95 | * The default class to use for HTTP Responses (defaults to ). 96 | */ 97 | public $response_class = 'ResponseCore'; 98 | 99 | /** 100 | * Default useragent string to use. 101 | */ 102 | public $useragent = 'RequestCore/1.4.3'; 103 | 104 | /** 105 | * File to read from while streaming up. 106 | */ 107 | public $read_file = null; 108 | 109 | /** 110 | * The resource to read from while streaming up. 111 | */ 112 | public $read_stream = null; 113 | 114 | /** 115 | * The size of the stream to read from. 116 | */ 117 | public $read_stream_size = null; 118 | 119 | /** 120 | * The length already read from the stream. 121 | */ 122 | public $read_stream_read = 0; 123 | 124 | /** 125 | * File to write to while streaming down. 126 | */ 127 | public $write_file = null; 128 | 129 | /** 130 | * The resource to write to while streaming down. 131 | */ 132 | public $write_stream = null; 133 | 134 | /** 135 | * Stores the intended starting seek position. 136 | */ 137 | public $seek_position = null; 138 | 139 | /** 140 | * The location of the cacert.pem file to use. 141 | */ 142 | public $cacert_location = false; 143 | 144 | /** 145 | * The state of SSL certificate verification. 146 | */ 147 | public $ssl_verification = true; 148 | 149 | /** 150 | * The user-defined callback function to call when a stream is read from. 151 | */ 152 | public $registered_streaming_read_callback = null; 153 | 154 | /** 155 | * The user-defined callback function to call when a stream is written to. 156 | */ 157 | public $registered_streaming_write_callback = null; 158 | 159 | 160 | /*%******************************************************************************************%*/ 161 | // CONSTANTS 162 | 163 | /** 164 | * GET HTTP Method 165 | */ 166 | const HTTP_GET = 'GET'; 167 | 168 | /** 169 | * POST HTTP Method 170 | */ 171 | const HTTP_POST = 'POST'; 172 | 173 | /** 174 | * PUT HTTP Method 175 | */ 176 | const HTTP_PUT = 'PUT'; 177 | 178 | /** 179 | * DELETE HTTP Method 180 | */ 181 | const HTTP_DELETE = 'DELETE'; 182 | 183 | /** 184 | * HEAD HTTP Method 185 | */ 186 | const HTTP_HEAD = 'HEAD'; 187 | 188 | 189 | /*%******************************************************************************************%*/ 190 | // CONSTRUCTOR/DESTRUCTOR 191 | 192 | /** 193 | * Constructs a new instance of this class. 194 | * 195 | * @param string $url (Optional) The URL to request or service endpoint to query. 196 | * @param string $proxy (Optional) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` 197 | * @param array $helpers (Optional) An associative array of classnames to use for request, and response functionality. Gets passed in automatically by the calling class. 198 | * @return $this A reference to the current instance. 199 | */ 200 | public function __construct($url = null, $proxy = null, $helpers = null) 201 | { 202 | // Set some default values. 203 | $this->request_url = $url; 204 | $this->method = self::HTTP_GET; 205 | $this->request_headers = array(); 206 | $this->request_body = ''; 207 | 208 | // Set a new Request class if one was set. 209 | if (isset($helpers['request']) && !empty($helpers['request'])) 210 | { 211 | $this->request_class = $helpers['request']; 212 | } 213 | 214 | // Set a new Request class if one was set. 215 | if (isset($helpers['response']) && !empty($helpers['response'])) 216 | { 217 | $this->response_class = $helpers['response']; 218 | } 219 | 220 | if ($proxy) 221 | { 222 | $this->set_proxy($proxy); 223 | } 224 | 225 | return $this; 226 | } 227 | 228 | /** 229 | * Destructs the instance. Closes opened file handles. 230 | * 231 | * @return $this A reference to the current instance. 232 | */ 233 | public function __destruct() 234 | { 235 | if (isset($this->read_file) && isset($this->read_stream)) 236 | { 237 | fclose($this->read_stream); 238 | } 239 | 240 | if (isset($this->write_file) && isset($this->write_stream)) 241 | { 242 | fclose($this->write_stream); 243 | } 244 | 245 | return $this; 246 | } 247 | 248 | 249 | /*%******************************************************************************************%*/ 250 | // REQUEST METHODS 251 | 252 | /** 253 | * Sets the credentials to use for authentication. 254 | * 255 | * @param string $user (Required) The username to authenticate with. 256 | * @param string $pass (Required) The password to authenticate with. 257 | * @return $this A reference to the current instance. 258 | */ 259 | public function set_credentials($user, $pass) 260 | { 261 | $this->username = $user; 262 | $this->password = $pass; 263 | return $this; 264 | } 265 | 266 | /** 267 | * Adds a custom HTTP header to the cURL request. 268 | * 269 | * @param string $key (Required) The custom HTTP header to set. 270 | * @param mixed $value (Required) The value to assign to the custom HTTP header. 271 | * @return $this A reference to the current instance. 272 | */ 273 | public function add_header($key, $value) 274 | { 275 | $this->request_headers[$key] = $value; 276 | return $this; 277 | } 278 | 279 | /** 280 | * Removes an HTTP header from the cURL request. 281 | * 282 | * @param string $key (Required) The custom HTTP header to set. 283 | * @return $this A reference to the current instance. 284 | */ 285 | public function remove_header($key) 286 | { 287 | if (isset($this->request_headers[$key])) 288 | { 289 | unset($this->request_headers[$key]); 290 | } 291 | return $this; 292 | } 293 | 294 | /** 295 | * Set the method type for the request. 296 | * 297 | * @param string $method (Required) One of the following constants: , , , , . 298 | * @return $this A reference to the current instance. 299 | */ 300 | public function set_method($method) 301 | { 302 | $this->method = strtoupper($method); 303 | return $this; 304 | } 305 | 306 | /** 307 | * Sets a custom useragent string for the class. 308 | * 309 | * @param string $ua (Required) The useragent string to use. 310 | * @return $this A reference to the current instance. 311 | */ 312 | public function set_useragent($ua) 313 | { 314 | $this->useragent = $ua; 315 | return $this; 316 | } 317 | 318 | /** 319 | * Set the body to send in the request. 320 | * 321 | * @param string $body (Required) The textual content to send along in the body of the request. 322 | * @return $this A reference to the current instance. 323 | */ 324 | public function set_body($body) 325 | { 326 | $this->request_body = $body; 327 | return $this; 328 | } 329 | 330 | /** 331 | * Set the URL to make the request to. 332 | * 333 | * @param string $url (Required) The URL to make the request to. 334 | * @return $this A reference to the current instance. 335 | */ 336 | public function set_request_url($url) 337 | { 338 | $this->request_url = $url; 339 | return $this; 340 | } 341 | 342 | /** 343 | * Set additional CURLOPT settings. These will merge with the default settings, and override if 344 | * there is a duplicate. 345 | * 346 | * @param array $curlopts (Optional) A set of key-value pairs that set `CURLOPT` options. These will merge with the existing CURLOPTs, and ones passed here will override the defaults. Keys should be the `CURLOPT_*` constants, not strings. 347 | * @return $this A reference to the current instance. 348 | */ 349 | public function set_curlopts($curlopts) 350 | { 351 | $this->curlopts = $curlopts; 352 | return $this; 353 | } 354 | 355 | /** 356 | * Sets the length in bytes to read from the stream while streaming up. 357 | * 358 | * @param integer $size (Required) The length in bytes to read from the stream. 359 | * @return $this A reference to the current instance. 360 | */ 361 | public function set_read_stream_size($size) 362 | { 363 | $this->read_stream_size = $size; 364 | 365 | return $this; 366 | } 367 | 368 | /** 369 | * Sets the resource to read from while streaming up. Reads the stream from its current position until 370 | * EOF or `$size` bytes have been read. If `$size` is not given it will be determined by and 371 | * . 372 | * 373 | * @param resource $resource (Required) The readable resource to read from. 374 | * @param integer $size (Optional) The size of the stream to read. 375 | * @return $this A reference to the current instance. 376 | */ 377 | public function set_read_stream($resource, $size = null) 378 | { 379 | if (!isset($size) || $size < 0) 380 | { 381 | $stats = fstat($resource); 382 | 383 | if ($stats && $stats['size'] >= 0) 384 | { 385 | $position = ftell($resource); 386 | 387 | if ($position !== false && $position >= 0) 388 | { 389 | $size = $stats['size'] - $position; 390 | } 391 | } 392 | } 393 | 394 | $this->read_stream = $resource; 395 | 396 | return $this->set_read_stream_size($size); 397 | } 398 | 399 | /** 400 | * Sets the file to read from while streaming up. 401 | * 402 | * @param string $location (Required) The readable location to read from. 403 | * @return $this A reference to the current instance. 404 | */ 405 | public function set_read_file($location) 406 | { 407 | $this->read_file = $location; 408 | $read_file_handle = fopen($location, 'r'); 409 | 410 | return $this->set_read_stream($read_file_handle); 411 | } 412 | 413 | /** 414 | * Sets the resource to write to while streaming down. 415 | * 416 | * @param resource $resource (Required) The writeable resource to write to. 417 | * @return $this A reference to the current instance. 418 | */ 419 | public function set_write_stream($resource) 420 | { 421 | $this->write_stream = $resource; 422 | 423 | return $this; 424 | } 425 | 426 | /** 427 | * Sets the file to write to while streaming down. 428 | * 429 | * @param string $location (Required) The writeable location to write to. 430 | * @return $this A reference to the current instance. 431 | */ 432 | public function set_write_file($location) 433 | { 434 | $this->write_file = $location; 435 | $write_file_handle = fopen($location, 'w'); 436 | 437 | return $this->set_write_stream($write_file_handle); 438 | } 439 | 440 | /** 441 | * Set the proxy to use for making requests. 442 | * 443 | * @param string $proxy (Required) The faux-url to use for proxy settings. Takes the following format: `proxy://user:pass@hostname:port` 444 | * @return $this A reference to the current instance. 445 | */ 446 | public function set_proxy($proxy) 447 | { 448 | $proxy = parse_url($proxy); 449 | $proxy['user'] = isset($proxy['user']) ? $proxy['user'] : null; 450 | $proxy['pass'] = isset($proxy['pass']) ? $proxy['pass'] : null; 451 | $proxy['port'] = isset($proxy['port']) ? $proxy['port'] : null; 452 | $this->proxy = $proxy; 453 | return $this; 454 | } 455 | 456 | /** 457 | * Set the intended starting seek position. 458 | * 459 | * @param integer $position (Required) The byte-position of the stream to begin reading from. 460 | * @return $this A reference to the current instance. 461 | */ 462 | public function set_seek_position($position) 463 | { 464 | $this->seek_position = isset($position) ? (integer) $position : null; 465 | 466 | return $this; 467 | } 468 | 469 | /** 470 | * Register a callback function to execute whenever a data stream is read from using 471 | * . 472 | * 473 | * The user-defined callback function should accept three arguments: 474 | * 475 | *
    476 | *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • 477 | *
  • $file_handle - resource - Required - The file handle resource that represents the file on the local file system.
  • 478 | *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • 479 | *
480 | * 481 | * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    482 | *
  • The name of a global function to execute, passed as a string.
  • 483 | *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • 484 | *
  • An anonymous function (PHP 5.3+).
485 | * @return $this A reference to the current instance. 486 | */ 487 | public function register_streaming_read_callback($callback) 488 | { 489 | $this->registered_streaming_read_callback = $callback; 490 | 491 | return $this; 492 | } 493 | 494 | /** 495 | * Register a callback function to execute whenever a data stream is written to using 496 | * . 497 | * 498 | * The user-defined callback function should accept two arguments: 499 | * 500 | *
    501 | *
  • $curl_handle - resource - Required - The cURL handle resource that represents the in-progress transfer.
  • 502 | *
  • $length - integer - Required - The length in kilobytes of the data chunk that was transferred.
  • 503 | *
504 | * 505 | * @param string|array|function $callback (Required) The callback function is called by , so you can pass the following values:
    506 | *
  • The name of a global function to execute, passed as a string.
  • 507 | *
  • A method to execute, passed as array('ClassName', 'MethodName').
  • 508 | *
  • An anonymous function (PHP 5.3+).
509 | * @return $this A reference to the current instance. 510 | */ 511 | public function register_streaming_write_callback($callback) 512 | { 513 | $this->registered_streaming_write_callback = $callback; 514 | 515 | return $this; 516 | } 517 | 518 | 519 | /*%******************************************************************************************%*/ 520 | // PREPARE, SEND, AND PROCESS REQUEST 521 | 522 | /** 523 | * A callback function that is invoked by cURL for streaming up. 524 | * 525 | * @param resource $curl_handle (Required) The cURL handle for the request. 526 | * @param resource $file_handle (Required) The open file handle resource. 527 | * @param integer $length (Required) The maximum number of bytes to read. 528 | * @return binary Binary data from a stream. 529 | */ 530 | public function streaming_read_callback($curl_handle, $file_handle, $length) 531 | { 532 | // Once we've sent as much as we're supposed to send... 533 | if ($this->read_stream_read >= $this->read_stream_size) 534 | { 535 | // Send EOF 536 | return ''; 537 | } 538 | 539 | // If we're at the beginning of an upload and need to seek... 540 | if ($this->read_stream_read == 0 && isset($this->seek_position) && $this->seek_position !== ftell($this->read_stream)) 541 | { 542 | if (fseek($this->read_stream, $this->seek_position) !== 0) 543 | { 544 | throw new RequestCore_Exception('The stream does not support seeking and is either not at the requested position or the position is unknown.'); 545 | } 546 | } 547 | 548 | $read = fread($this->read_stream, min($this->read_stream_size - $this->read_stream_read, $length)); // Remaining upload data or cURL's requested chunk size 549 | $this->read_stream_read += strlen($read); 550 | 551 | $out = $read === false ? '' : $read; 552 | 553 | // Execute callback function 554 | if ($this->registered_streaming_read_callback) 555 | { 556 | call_user_func($this->registered_streaming_read_callback, $curl_handle, $file_handle, $out); 557 | } 558 | 559 | return $out; 560 | } 561 | 562 | /** 563 | * A callback function that is invoked by cURL for streaming down. 564 | * 565 | * @param resource $curl_handle (Required) The cURL handle for the request. 566 | * @param binary $data (Required) The data to write. 567 | * @return integer The number of bytes written. 568 | */ 569 | public function streaming_write_callback($curl_handle, $data) 570 | { 571 | $length = strlen($data); 572 | $written_total = 0; 573 | $written_last = 0; 574 | 575 | while ($written_total < $length) 576 | { 577 | $written_last = fwrite($this->write_stream, substr($data, $written_total)); 578 | 579 | if ($written_last === false) 580 | { 581 | return $written_total; 582 | } 583 | 584 | $written_total += $written_last; 585 | } 586 | 587 | // Execute callback function 588 | if ($this->registered_streaming_write_callback) 589 | { 590 | call_user_func($this->registered_streaming_write_callback, $curl_handle, $written_total); 591 | } 592 | 593 | return $written_total; 594 | } 595 | 596 | /** 597 | * Prepares and adds the details of the cURL request. This can be passed along to a 598 | * function. 599 | * 600 | * @return resource The handle for the cURL object. 601 | */ 602 | public function prep_request() 603 | { 604 | $curl_handle = curl_init(); 605 | 606 | // Set default options. 607 | curl_setopt($curl_handle, CURLOPT_URL, $this->request_url); 608 | curl_setopt($curl_handle, CURLOPT_FILETIME, true); 609 | curl_setopt($curl_handle, CURLOPT_FRESH_CONNECT, false); 610 | curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 5); 611 | curl_setopt($curl_handle, CURLOPT_HEADER, true); 612 | curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true); 613 | curl_setopt($curl_handle, CURLOPT_TIMEOUT, 5184000); 614 | curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, 120); 615 | curl_setopt($curl_handle, CURLOPT_NOSIGNAL, true); 616 | curl_setopt($curl_handle, CURLOPT_REFERER, $this->request_url); 617 | curl_setopt($curl_handle, CURLOPT_USERAGENT, $this->useragent); 618 | curl_setopt($curl_handle, CURLOPT_READFUNCTION, array($this, 'streaming_read_callback')); 619 | 620 | // Verification of the SSL cert 621 | if ($this->ssl_verification) 622 | { 623 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, true); 624 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, 2); 625 | } 626 | else 627 | { 628 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYPEER, false); 629 | curl_setopt($curl_handle, CURLOPT_SSL_VERIFYHOST, false); 630 | } 631 | 632 | // chmod the file as 0755 633 | if ($this->cacert_location === true) 634 | { 635 | curl_setopt($curl_handle, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem'); 636 | } 637 | elseif (is_string($this->cacert_location)) 638 | { 639 | curl_setopt($curl_handle, CURLOPT_CAINFO, $this->cacert_location); 640 | } 641 | 642 | // Debug mode 643 | if ($this->debug_mode) 644 | { 645 | curl_setopt($curl_handle, CURLOPT_VERBOSE, true); 646 | } 647 | 648 | // Handle open_basedir & safe mode 649 | if (!ini_get('safe_mode') && !ini_get('open_basedir')) 650 | { 651 | curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true); 652 | } 653 | 654 | // Enable a proxy connection if requested. 655 | if ($this->proxy) 656 | { 657 | curl_setopt($curl_handle, CURLOPT_HTTPPROXYTUNNEL, true); 658 | 659 | $host = $this->proxy['host']; 660 | $host .= ($this->proxy['port']) ? ':' . $this->proxy['port'] : ''; 661 | curl_setopt($curl_handle, CURLOPT_PROXY, $host); 662 | 663 | if (isset($this->proxy['user']) && isset($this->proxy['pass'])) 664 | { 665 | curl_setopt($curl_handle, CURLOPT_PROXYUSERPWD, $this->proxy['user'] . ':' . $this->proxy['pass']); 666 | } 667 | } 668 | 669 | // Set credentials for HTTP Basic/Digest Authentication. 670 | if ($this->username && $this->password) 671 | { 672 | curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY); 673 | curl_setopt($curl_handle, CURLOPT_USERPWD, $this->username . ':' . $this->password); 674 | } 675 | 676 | // Handle the encoding if we can. 677 | if (extension_loaded('zlib')) 678 | { 679 | curl_setopt($curl_handle, CURLOPT_ENCODING, ''); 680 | } 681 | 682 | // Process custom headers 683 | if (isset($this->request_headers) && count($this->request_headers)) 684 | { 685 | $temp_headers = array(); 686 | 687 | foreach ($this->request_headers as $k => $v) 688 | { 689 | $temp_headers[] = $k . ': ' . $v; 690 | } 691 | 692 | curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $temp_headers); 693 | } 694 | 695 | switch ($this->method) 696 | { 697 | case self::HTTP_PUT: 698 | //unset($this->read_stream); 699 | curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, 'PUT'); 700 | if (isset($this->read_stream)) 701 | { 702 | if (!isset($this->read_stream_size) || $this->read_stream_size < 0) 703 | { 704 | throw new RequestCore_Exception('The stream size for the streaming upload cannot be determined.'); 705 | } 706 | 707 | curl_setopt($curl_handle, CURLOPT_INFILESIZE, $this->read_stream_size); 708 | curl_setopt($curl_handle, CURLOPT_UPLOAD, true); 709 | } 710 | else 711 | { 712 | curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); 713 | } 714 | break; 715 | 716 | case self::HTTP_POST: 717 | curl_setopt($curl_handle, CURLOPT_POST, true); 718 | curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); 719 | break; 720 | 721 | case self::HTTP_HEAD: 722 | curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, self::HTTP_HEAD); 723 | curl_setopt($curl_handle, CURLOPT_NOBODY, 1); 724 | break; 725 | 726 | default: // Assumed GET 727 | curl_setopt($curl_handle, CURLOPT_CUSTOMREQUEST, $this->method); 728 | if (isset($this->write_stream)) 729 | { 730 | curl_setopt($curl_handle, CURLOPT_WRITEFUNCTION, array($this, 'streaming_write_callback')); 731 | curl_setopt($curl_handle, CURLOPT_HEADER, false); 732 | } 733 | else 734 | { 735 | curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $this->request_body); 736 | } 737 | break; 738 | } 739 | 740 | // Merge in the CURLOPTs 741 | if (isset($this->curlopts) && sizeof($this->curlopts) > 0) 742 | { 743 | foreach ($this->curlopts as $k => $v) 744 | { 745 | curl_setopt($curl_handle, $k, $v); 746 | } 747 | } 748 | 749 | return $curl_handle; 750 | } 751 | 752 | /** 753 | * Take the post-processed cURL data and break it down into useful header/body/info chunks. Uses the 754 | * data stored in the `curl_handle` and `response` properties unless replacement data is passed in via 755 | * parameters. 756 | * 757 | * @param resource $curl_handle (Optional) The reference to the already executed cURL request. 758 | * @param string $response (Optional) The actual response content itself that needs to be parsed. 759 | * @return ResponseCore A object containing a parsed HTTP response. 760 | */ 761 | public function process_response($curl_handle = null, $response = null) 762 | { 763 | if ($response) 764 | { 765 | $this->response = $response; 766 | } 767 | // As long as this came back as a valid resource... 768 | if (is_resource($curl_handle)) 769 | { 770 | // Determine what's what. 771 | $header_size = curl_getinfo($curl_handle, CURLINFO_HEADER_SIZE); 772 | $this->response_headers = substr($this->response, 0, $header_size); 773 | $this->response_body = substr($this->response, $header_size); 774 | $this->response_code = curl_getinfo($curl_handle, CURLINFO_HTTP_CODE); 775 | $this->response_info = curl_getinfo($curl_handle); 776 | 777 | // Parse out the headers 778 | $this->response_headers = explode("\r\n\r\n", trim($this->response_headers)); 779 | $this->response_headers = array_pop($this->response_headers); 780 | $this->response_headers = explode("\r\n", $this->response_headers); 781 | array_shift($this->response_headers); 782 | 783 | // Loop through and split up the headers. 784 | $header_assoc = array(); 785 | foreach ($this->response_headers as $header) 786 | { 787 | $kv = explode(': ', $header); 788 | $header_assoc[strtolower($kv[0])] = isset($kv[1])?$kv[1]:''; 789 | } 790 | 791 | // Reset the headers to the appropriate property. 792 | $this->response_headers = $header_assoc; 793 | $this->response_headers['_info'] = $this->response_info; 794 | $this->response_headers['_info']['method'] = $this->method; 795 | 796 | if ($curl_handle && $this->response) 797 | { 798 | return new $this->response_class($this->response_headers, $this->response_body, $this->response_code, $curl_handle); 799 | } 800 | } 801 | 802 | // Return false 803 | return false; 804 | } 805 | 806 | /** 807 | * Sends the request, calling necessary utility functions to update built-in properties. 808 | * 809 | * @param boolean $parse (Optional) Whether to parse the response with ResponseCore or not. 810 | * @return string The resulting unparsed data from the request. 811 | */ 812 | public function send_request($parse = false) 813 | { 814 | set_time_limit(0); 815 | 816 | $curl_handle = $this->prep_request(); 817 | $this->response = curl_exec($curl_handle); 818 | 819 | if ($this->response === false) 820 | { 821 | throw new RequestCore_Exception('cURL resource: ' . (string) $curl_handle . '; cURL error: ' . curl_error($curl_handle) . ' (' . curl_errno($curl_handle) . ')'); 822 | } 823 | 824 | $parsed_response = $this->process_response($curl_handle, $this->response); 825 | 826 | curl_close($curl_handle); 827 | 828 | if ($parse) 829 | { 830 | return $parsed_response; 831 | } 832 | 833 | return $this->response; 834 | } 835 | 836 | /** 837 | * Sends the request using , enabling parallel requests. Uses the "rolling" method. 838 | * 839 | * @param array $handles (Required) An indexed array of cURL handles to process simultaneously. 840 | * @param array $opt (Optional) An associative array of parameters that can have the following keys:
    841 | *
  • callback - string|array - Optional - The string name of a function to pass the response data to. If this is a method, pass an array where the [0] index is the class and the [1] index is the method name.
  • 842 | *
  • limit - integer - Optional - The number of simultaneous requests to make. This can be useful for scaling around slow server responses. Defaults to trusting cURLs judgement as to how many to use.
843 | * @return array Post-processed cURL responses. 844 | */ 845 | public function send_multi_request($handles, $opt = null) 846 | { 847 | set_time_limit(0); 848 | 849 | // Skip everything if there are no handles to process. 850 | if (count($handles) === 0) return array(); 851 | 852 | if (!$opt) $opt = array(); 853 | 854 | // Initialize any missing options 855 | $limit = isset($opt['limit']) ? $opt['limit'] : -1; 856 | 857 | // Initialize 858 | $handle_list = $handles; 859 | $http = new $this->request_class(); 860 | $multi_handle = curl_multi_init(); 861 | $handles_post = array(); 862 | $added = count($handles); 863 | $last_handle = null; 864 | $count = 0; 865 | $i = 0; 866 | 867 | // Loop through the cURL handles and add as many as it set by the limit parameter. 868 | while ($i < $added) 869 | { 870 | if ($limit > 0 && $i >= $limit) break; 871 | curl_multi_add_handle($multi_handle, array_shift($handles)); 872 | $i++; 873 | } 874 | 875 | do 876 | { 877 | $active = false; 878 | 879 | // Start executing and wait for a response. 880 | while (($status = curl_multi_exec($multi_handle, $active)) === CURLM_CALL_MULTI_PERFORM) 881 | { 882 | // Start looking for possible responses immediately when we have to add more handles 883 | if (count($handles) > 0) break; 884 | } 885 | 886 | // Figure out which requests finished. 887 | $to_process = array(); 888 | 889 | while ($done = curl_multi_info_read($multi_handle)) 890 | { 891 | // Since curl_errno() isn't reliable for handles that were in multirequests, we check the 'result' of the info read, which contains the curl error number, (listed here http://curl.haxx.se/libcurl/c/libcurl-errors.html ) 892 | if ($done['result'] > 0) 893 | { 894 | throw new RequestCore_Exception('cURL resource: ' . (string) $done['handle'] . '; cURL error: ' . curl_error($done['handle']) . ' (' . $done['result'] . ')'); 895 | } 896 | 897 | // Because curl_multi_info_read() might return more than one message about a request, we check to see if this request is already in our array of completed requests 898 | elseif (!isset($to_process[(int) $done['handle']])) 899 | { 900 | $to_process[(int) $done['handle']] = $done; 901 | } 902 | } 903 | 904 | // Actually deal with the request 905 | foreach ($to_process as $pkey => $done) 906 | { 907 | $response = $http->process_response($done['handle'], curl_multi_getcontent($done['handle'])); 908 | $key = array_search($done['handle'], $handle_list, true); 909 | $handles_post[$key] = $response; 910 | 911 | if (count($handles) > 0) 912 | { 913 | curl_multi_add_handle($multi_handle, array_shift($handles)); 914 | } 915 | 916 | curl_multi_remove_handle($multi_handle, $done['handle']); 917 | curl_close($done['handle']); 918 | } 919 | } 920 | while ($active || count($handles_post) < $added); 921 | 922 | curl_multi_close($multi_handle); 923 | 924 | ksort($handles_post, SORT_NUMERIC); 925 | return $handles_post; 926 | } 927 | 928 | 929 | /*%******************************************************************************************%*/ 930 | // RESPONSE METHODS 931 | 932 | /** 933 | * Get the HTTP response headers from the request. 934 | * 935 | * @param string $header (Optional) A specific header value to return. Defaults to all headers. 936 | * @return string|array All or selected header values. 937 | */ 938 | public function get_response_header($header = null) 939 | { 940 | if ($header) 941 | { 942 | return $this->response_headers[strtolower($header)]; 943 | } 944 | return $this->response_headers; 945 | } 946 | 947 | /** 948 | * Get the HTTP response body from the request. 949 | * 950 | * @return string The response body. 951 | */ 952 | public function get_response_body() 953 | { 954 | return $this->response_body; 955 | } 956 | 957 | /** 958 | * Get the HTTP response code from the request. 959 | * 960 | * @return string The HTTP response code. 961 | */ 962 | public function get_response_code() 963 | { 964 | return $this->response_code; 965 | } 966 | } 967 | 968 | 969 | /** 970 | * Container for all response-related methods. 971 | */ 972 | class ResponseCore 973 | { 974 | /** 975 | * Stores the HTTP header information. 976 | */ 977 | public $header; 978 | 979 | /** 980 | * Stores the SimpleXML response. 981 | */ 982 | public $body; 983 | 984 | /** 985 | * Stores the HTTP response code. 986 | */ 987 | public $status; 988 | 989 | /** 990 | * Constructs a new instance of this class. 991 | * 992 | * @param array $header (Required) Associative array of HTTP headers (typically returned by ). 993 | * @param string $body (Required) XML-formatted response from AWS. 994 | * @param integer $status (Optional) HTTP response status code from the request. 995 | * @return object Contains an `header` property (HTTP headers as an associative array), a or `body` property, and an `status` code. 996 | */ 997 | public function __construct($header, $body, $status = null) 998 | { 999 | $this->header = $header; 1000 | $this->body = $body; 1001 | $this->status = $status; 1002 | 1003 | return $this; 1004 | } 1005 | 1006 | /** 1007 | * Did we receive the status code we expected? 1008 | * 1009 | * @param integer|array $codes (Optional) The status code(s) to expect. Pass an for a single acceptable value, or an of integers for multiple acceptable values. 1010 | * @return boolean Whether we received the expected status code or not. 1011 | */ 1012 | public function isOK($codes = array(200, 201, 204, 206)) 1013 | { 1014 | if (is_array($codes)) 1015 | { 1016 | return in_array($this->status, $codes); 1017 | } 1018 | 1019 | return $this->status === $codes; 1020 | } 1021 | } 1022 | 1023 | /** 1024 | * Default RequestCore Exception. 1025 | */ 1026 | class RequestCore_Exception extends Exception {} 1027 | -------------------------------------------------------------------------------- /Aliyun/ACM/Util.php: -------------------------------------------------------------------------------- 1 | refreshServerList) 24 | - get config data from ACM 25 | - no lisener, update manually(Client->getData) 26 | 27 | ## Installation 28 | - add the php libary to your project 29 | 30 | ## Getting Started 31 | - please check the sample.php 32 | 33 | ## Configuration 34 | TODO 35 | 36 | #### Extra Options 37 | TODO 38 | 39 | ## API Reference 40 | 41 | ### Get Config 42 | Get value of one config item following priority: 43 | 44 | * Step 1 - Get from local cache with timestamp, if the cache value was expired, get from following sources and update local cache. 45 | * Step 2 - Get from local failover dir(default: `${cwd}/acm/data`). 46 | * Failover dir can be manually copied from snapshot dir(default: `${cwd}/acm/snapshot`) in advance. 47 | * This helps to suppress the effect of known server failure. 48 | * Step 3 - Get from one server until value is got or all servers tried. 49 | * Content will be save to snapshot dir after got from server. 50 | * Step 4 - Get from snapshot dir. 51 | 52 | ### List All Config 53 | Get all config items of current namespace, with dataId and group information only. 54 | * Access local cache first, if local data was expired, request the data from server and update local cache correspondingly 55 | * Warning: If there are lots of config in namespace, this function may cost some time. 56 | 57 | ### Publish Config 58 | Publish one data item to ACM. 59 | * If the data key is not exist, create one first. 60 | * If the data key is exist, update to the content specified. 61 | * Content can not be set to None, if there is need to delete config item, use function __remove__ instead. 62 | 63 | ### Remove Config 64 | Remove one data item from ACM. 65 | 66 | ## Other Resources 67 | 68 | * Alibaba Cloud ACM homepage: [https://www.aliyun.com/product/acm](https://www.aliyun.com/product/acm) 69 | 70 | -------------------------------------------------------------------------------- /demo/sample.php: -------------------------------------------------------------------------------- 1 | getServerList(); 11 | $client->refreshServerList(); 12 | 13 | $client->setNameSpace('namespace'); 14 | $client->setAccessKey('accesskey'); 15 | $client->setSecretKey('secretkey'); 16 | $client->setAppName("appname"); 17 | 18 | echo $client->getConfig('test.test',null)."\n"; 19 | 20 | $client->publishConfig('test.test',null,"{\"test\":\"asdfasdfasdf\"}")."\n"; 21 | 22 | $ts = round(microtime(true) * 1000); 23 | $client->publishConfig('test'.$ts,null,"{\"test\":\"asdfasdfasdf\"}")."\n"; 24 | 25 | $client->removeConfig('test.test',null); 26 | 27 | var_dump(array_values($client->getServerList())[0]); 28 | 29 | echo "hello world"; 30 | 31 | echo strval(Aliyun_ACM_Util::isValid('data_id')); 32 | 33 | echo strval(Aliyun_ACM_Util::isValid('data*id')); 34 | 35 | --------------------------------------------------------------------------------