· 6 years ago · Nov 01, 2019, 08:56 PM
1<?php
2/**
3 * Makes a request to ATS for the top 10 sites in a country
4 */
5class TopSites {
6
7 protected static $ActionName = 'Topsites';
8 protected static $ResponseGroupName = 'Country';
9 protected static $ServiceEndpoint = 'ats.us-west-1.amazonaws.com';
10 protected static $ServiceHost = 'ats.amazonaws.com';
11 protected static $NumReturn = 10;
12 protected static $StartNum = 1;
13 protected static $SigVersion = '2';
14 protected static $HashAlgorithm = 'HmacSHA256';
15 protected static $ServiceURI = "/api";
16 protected static $ServiceRegion = "us-west-1";
17 protected static $ServiceName = "AlexaTopSites";
18
19
20 public function TopSites($accessKeyId, $secretAccessKey, $countryCode) {
21 $this->accessKeyId = $accessKeyId;
22 $this->secretAccessKey = $secretAccessKey;
23 $this->countryCode = $countryCode;
24 $now = time();
25 $this->amzDate = gmdate("Ymd\THis\Z", $now);
26 $this->dateStamp = gmdate("Ymd", $now);
27
28 }
29
30 /**
31 * Get site info from AWIS.
32 */
33 public function getTopSites() {
34 $canonicalQuery = $this->buildQueryParams();
35 $canonicalHeaders = $this->buildHeaders(true);
36 $signedHeaders = $this->buildHeaders(false);
37 $payloadHash = hash('sha256', "");
38 $canonicalRequest = "GET" . "\n" . self::$ServiceURI . "\n" . $canonicalQuery . "\n" . $canonicalHeaders . "\n" . $signedHeaders . "\n" . $payloadHash;
39 $algorithm = "AWS4-HMAC-SHA256";
40 $credentialScope = $this->dateStamp . "/" . self::$ServiceRegion . "/" . self::$ServiceName . "/" . "aws4_request";
41 $stringToSign = $algorithm . "\n" . $this->amzDate . "\n" . $credentialScope . "\n" . hash('sha256', $canonicalRequest);
42 $signingKey = $this->getSignatureKey();
43 $signature = hash_hmac('sha256', $stringToSign, $signingKey);
44 $authorizationHeader = $algorithm . ' ' . 'Credential=' . $this->accessKeyId . '/' . $credentialScope . ', ' . 'SignedHeaders=' . $signedHeaders . ', ' . 'Signature=' . $signature;
45
46 $url = 'https://' . self::$ServiceHost . self::$ServiceURI . '?' . $canonicalQuery;
47 $ret = self::makeRequest($url, $authorizationHeader);
48 self::parseResponse($ret);
49 }
50
51 protected function sign($key, $msg) {
52 return hash_hmac('sha256', $msg, $key, true);
53 }
54
55 protected function getSignatureKey() {
56 $kSecret = 'AWS4' . $this->secretAccessKey;
57 $kDate = $this->sign($kSecret, $this->dateStamp);
58 $kRegion = $this->sign($kDate, self::$ServiceRegion);
59 $kService = $this->sign($kRegion, self::$ServiceName);
60 $kSigning = $this->sign($kService, 'aws4_request');
61 return $kSigning;
62 }
63
64 /**
65 * Builds headers for the request to AWIS.
66 * @return String headers for the request
67 */
68 protected function buildHeaders($list) {
69 $params = array(
70 'host' => self::$ServiceEndpoint,
71 'x-amz-date' => $this->amzDate
72 );
73 ksort($params);
74 $keyvalue = array();
75 foreach($params as $k => $v) {
76 if ($list)
77 $keyvalue[] = $k . ':' . $v;
78 else {
79 $keyvalue[] = $k;
80 }
81 }
82 return ($list) ? implode("\n",$keyvalue) . "\n" : implode(';',$keyvalue) ;
83 }
84
85 /**
86 * Builds query parameters for the request to AWIS.
87 * Parameter names will be in alphabetical order and
88 * parameter values will be urlencoded per RFC 3986.
89 * @return String query parameters for the request
90 */
91 protected function buildQueryParams() {
92 $params = array(
93 'Action' => self::$ActionName,
94 'ResponseGroup' => self::$ResponseGroupName,
95 'CountryCode' => $this->countryCode,
96 'Count' => self::$NumReturn,
97 'Start' => self::$StartNum
98 );
99 ksort($params);
100 $keyvalue = array();
101 foreach($params as $k => $v) {
102 $keyvalue[] = $k . '=' . rawurlencode($v);
103 }
104 return implode('&',$keyvalue);
105 }
106
107 /**
108 * Makes request to TopSites
109 * @param String $url URL to make request to
110 * @param String authorizationHeader Authorization string
111 * @return String Result of request
112 */
113 protected function makeRequest($url, $authorizationHeader) {
114 echo "\nMaking request to:\n$url\n";
115 $ch = curl_init($url);
116 curl_setopt($ch, CURLOPT_TIMEOUT, 4);
117 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
118 curl_setopt($ch, CURLOPT_HTTPHEADER, array(
119 'Accept: application/xml',
120 'Content-Type: application/xml',
121 'X-Amz-Date: ' . $this->amzDate,
122 'Authorization: ' . $authorizationHeader
123 ));
124 $result = curl_exec($ch);
125 curl_close($ch);
126 return $result;
127 }
128
129 /**
130 * Parses the XML response from ATS and echoes the DataUrl element
131 * for each returned site
132 *
133 * @param String $response xml response from ATS
134 */
135 protected static function parseResponse($response) {
136 echo "\nSites: \n";
137 $xml = new SimpleXMLElement($response,null, false, 'aws', true);
138 foreach($xml->Response->TopSitesResult->Alexa->TopSites->Country->Sites->children('aws', true) as $site) {
139 echo $site->DataUrl . "\n";
140 }
141 }
142
143}
144
145if (count($argv) < 3) {
146 echo "Usage: $argv[0] ACCESS_KEY_ID SECRET_ACCESS_KEY [COUNTRY_CODE]\n";
147 exit(-1);
148}
149else {
150 $accessKeyId = $argv[1];
151 $secretAccessKey = $argv[2];
152 $countryCode = count($argv) > 3 ? $argv[3] : "";
153}
154
155$topSites = new TopSites($accessKeyId, $secretAccessKey, $countryCode);
156$topSites->getTopSites();
157
158?>