access_key = $access_key;
$this->secret_key = $secret_key;
$this->api_host = $api_host;
}
/**
* Get a list of CloudFront distributions
*/
public function distributions_list() {
$response = $this->request( 'GET', '/2014-11-06/distribution' );
$data = $response['body_array'];
$data = $this->fix_array( $data, array(
'Items', 'DistributionSummary' ) );
if ( isset( $data['Items']['DistributionSummary'] ) ) {
foreach ( $data['Items']['DistributionSummary'] as $key => $value ) {
$data['Items']['DistributionSummary'][$key] =
$this->fix_distribution(
$data['Items']['DistributionSummary'][$key] );
}
}
return $data;
}
/**
* Get a list of CloudFront distributions
*/
public function distribution_get( $id ) {
$response = $this->request( 'GET', '/2014-11-06/distribution/' . $id );
$data = $response['body_array'];
if ( isset( $data['DistributionConfig'] ) )
$data['DistributionConfig'] = $this->fix_distribution(
$data['DistributionConfig'] );
return $data;
}
/**
* $distribution
* origin
*/
public function distribution_create( $distribution ) {
if ( !isset( $distribution['CallerReference'] ) )
$distribution['CallerReference'] = rand();
if ( !isset( $distribution['Enabled'] ) )
$distribution['Enabled'] = 'true';
$data =
'' .
'' .
$this->array_to_xml( $distribution ) .
'';
$response = $this->request( 'POST', '/2014-11-06/distribution', $data );
$response_data = $response['body_array'];
return $response_data;
}
/**
* $distribution
* origin
*/
public function distribution_update( $distribution_id, $distribution ) {
$get_response = $this->request( 'GET', '/2014-11-06/distribution/' .
$distribution_id );
$get_distribution = $get_response['body_array'];
$c = $get_distribution['DistributionConfig'];
// update values
foreach ( $distribution as $key => $value ) {
if ( $key == 'DefaultCacheBehavior' ) {
// inherit inner values of that setting too
foreach ( $distribution[$key] as $key2 => $value2 ) {
$c[$key][$key2] = $value2;
}
} else {
$c[$key] = $value;
}
}
$data =
'' .
'' .
$this->array_to_xml( $c ) .
'';
$response = $this->request( 'PUT',
'/2014-11-06/distribution/' . $distribution_id . '/config', $data,
array( 'If-Match' => $get_response['headers']['etag'] ) );
$data = $response['body_array'];
if ( isset( $data['DistributionConfig'] ) )
$data['DistributionConfig'] = $this->fix_distribution(
$data['DistributionConfig'] );
return $data;
}
/**
* Invalidate cache
*/
public function invalidation_create( $distribution_id, $uris ) {
$data =
'' .
'' .
'' .
'' . count( $uris ) . '' .
$this->array_to_xml( array(
'Items' => array(
'Path' => $uris
)
) ) .
'' .
'' . rand() . '' .
'';
$response = $this->request( 'POST',
'/2014-11-06/distribution/' . $distribution_id . '/invalidation', $data );
$response_data = $response['body_array'];
return $response_data;
}
/**
* Constructor
*
* @param string $verb Verb
* @param string $bucket Bucket name
* @param string $uri Object URI
* @return mixed
*/
private function request( $method, $uri = '', $data = '', $headers = array() ) {
$url = 'https://' . $this->api_host . $uri;
$headers['Host'] = $this->api_host;
$headers['x-amz-date'] = gmdate( 'Ymd\THis\Z', time() );
if ( $method == "POST" || $method == "PUT" ) {
$headers['Content-Type'] = 'application/xml; charset=utf-8';
$headers['Content-Length'] = strlen( $data );
} else {
$data = '';
}
$headers['Authorization'] = $this->calculateSignature( $method, $uri,
$headers, $data );
// do the request
$request = array(
'sslverify' => false,
'headers' => $headers,
'method' => $method,
'body' => $data
);
$response = wp_remote_request( $url, $request );
// handle response
if ( is_wp_error( $response ) ) {
throw new \Exception( 'Failed to reach CloudFront: ' .
implode( '; ', $response->get_error_messages() ) );
}
if ( substr( $response['body'], 0, 5 ) != 'canonicalize_uri( $uri ) . "\n" .
'' /* query string */ . "\n" .
implode( "\n", $canonical_headers ) . "\n\n" .
$signed_headers . "\n" .
$payload;
$string_to_sign =
"AWS4-HMAC-SHA256\n" .
$headers['x-amz-date'] . "\n" .
$credential_scope . "\n" .
hash( 'sha256', $canonical_request );
// Calculate the signing key using a series of derived keys
$signingKey = $this->get_signing_key( $short_date, $region, $service,
$this->secret_key );
$signature = hash_hmac( 'sha256', $string_to_sign, $signingKey );
return "AWS4-HMAC-SHA256 " .
"Credential={$this->access_key}/{$credential_scope}, " .
"SignedHeaders={$signed_headers}, Signature={$signature}";
}
private function canonicalize_uri( $uri ) {
$doubleEncoded = rawurlencode( ltrim( $uri, '/' ) );
return '/' . str_replace( '%2F', '/', $doubleEncoded );
}
private function get_signing_key( $short_date, $region, $service, $secretKey ) {
$dateKey = hash_hmac( 'sha256', $short_date, 'AWS4' . $secretKey, true );
$regionKey = hash_hmac( 'sha256', $region, $dateKey, true );
$serviceKey = hash_hmac( 'sha256', $service, $regionKey, true );
return hash_hmac( 'sha256', 'aws4_request', $serviceKey, true );
}
private function array_to_xml( $a ) {
if ( !is_array( $a ) )
return $a;
$s = '';
foreach ( $a as $key => $value ) {
if ( is_array( $value ) && isset( $value[0] ) ) {
// number-indexed array, serialized as list
foreach ( $value as $array_item ) {
$s .=
'<' . $key . '>' .
$this->array_to_xml( $array_item ) .
'' . $key . '>';
}
} else {
$s .=
'<' . $key . '>' .
$this->array_to_xml( $value ) .
'' . $key . '>';
}
}
return $s;
}
/**
* XML parsing suffers common problem of array recognition
* - a>> is accessible via $data['items']['item']
* but
*
- a>
- b>> is accessible
* via $data['items'][$n]['item']
*
* by knowing tag we can try to guess that it contains only 1 element
* and turn it to array so that it will be always accessible via 2nd way
*/
private function fix_array( $response, $keys ) {
$a = &$response;
for ( $n = 0; $n < count( $keys ) - 1; $n++ ) {
$key = $keys[$n];
if ( !isset( $a[$key] ) )
break;
$a = &$a[$key];
}
$last_key = $keys[count( $keys ) - 1];
if ( isset( $a[$last_key] ) ) {
if ( !isset( $a[$last_key][0] ) || is_string( $a[$last_key] ) ) {
$a[$last_key] = array(
$a[$last_key]
);
}
}
return $response;
}
private function fix_distribution( $data ) {
$data = $this->fix_array( $data, array( 'Aliases', 'Items', 'CNAME' ) );
$data = $this->fix_array( $data, array( 'Origins', 'Items', 'Origin' ) );
$data = $this->fix_array( $data, array( 'DefaultCacheBehavior',
'ForwardedValues', 'Headers', 'Items', 'Name' ) );
return $data;
}
}