/**
  * @param array                                     $config an array of configuration parameters
  * @param \MicrosoftTranslator\HttpInterface|null   $http   if null, a new Http manager will be used
  * @param \MicrosoftTranslator\AuthInterface|null   $auth   if null, a new Auth manager will be used
  * @param \MicrosoftTranslator\LoggerInterface|null $logger if null, a new Logger manager will be used
  *
  * @throws Exception
  */
 public function __construct($config = array(), $http = null, $auth = null, $logger = null)
 {
     // Init logger at first
     if (is_null($logger)) {
         $logger = new Logger($config);
     }
     if (!$logger instanceof LoggerInterface) {
         throw new Exception('Logger Manager is not an instance of MicrosoftTranslator\\LoggerInterface');
     }
     $this->logger = $logger;
     // Load configuration for Client
     foreach ($this->config_keys as $key) {
         if (isset($config[$key])) {
             $this->{$key} = $config[$key];
             $this->logger->debug(__CLASS__, 'config', sprintf('%s = %s', $key, $this->{$key}));
         }
     }
     // Init HTTP Manager
     if (is_null($http)) {
         $http = new Http($config, $logger);
     }
     if (!$http instanceof HttpInterface) {
         throw new Exception('HTTP Manager is not an instance of MicrosoftTranslator\\HttpInterface');
     }
     $this->http = $http;
     // Init Auth Manager
     if (is_null($auth)) {
         $auth = new Auth($config, $logger, $http);
     }
     if (!$auth instanceof AuthInterface) {
         throw new Exception('Auth Manager is not an instance of MicrosoftTranslator\\AuthInterface');
     }
     $this->auth = $auth;
 }
 /**
  * Save AT and timestamp in file
  *
  * @param string $access_token
  * @param int    $timestamp
  *
  * @return bool
  */
 private function save($access_token, $timestamp)
 {
     unset($this->runtime_file_content);
     $bytes = @file_put_contents($this->getFilePath(), json_encode(array('a' => $access_token, 'e' => $timestamp)));
     if ($bytes === false) {
         // fatal log will throw an exception
         $this->logger->fatal(__CLASS__, 'store', sprintf('Unable to store access token in %s', $this->getFilePath()));
     }
     $this->logger->debug(__CLASS__, 'store', sprintf('Access token stored in %s', $this->getFilePath()));
     return true;
 }
 /**
  * @return array|string
  * @throws \MicrosoftTranslator\Exception
  */
 private function generateAndStoreNewAccessToken()
 {
     $url = trim($this->auth_base_url, "/ \t\n\r\v");
     $access_token = null;
     $auth = array('grant_type' => 'client_credentials', 'client_id' => $this->api_client_id, 'client_secret' => $this->api_client_secret, 'scope' => $this->api_client_scope);
     $result = $this->http->post($url, null, $auth, null);
     if (Http::isRequestOk($result)) {
         $result['http_body'] = json_decode($result['http_body'], true);
         if (!isset($result['http_body']['access_token'])) {
             throw new Exception('Access token not found in response');
         }
         if (!is_string($result['http_body']['access_token'])) {
             throw new Exception('Access token found in response but it is not a string');
         }
         $access_token = strval(@$result['http_body']['access_token']);
         $expires_in = @(int) $result['http_body']['expires_in'];
         $this->logger->debug(__CLASS__, 'oauth', sprintf('New access_token generated %s...', substr($access_token, 0, 10)));
         $this->guard->storeAccessTokenForSeconds($access_token, $expires_in);
     } else {
         $this->logger->fatal(__CLASS__, 'oauth', 'Unable to generate a new access token : ' . json_encode($result));
     }
     return $access_token;
 }
 /**
  * @param string       $url
  * @param string       $method
  * @param string|null  $access_token
  * @param string|array $parameters
  * @param string       $contentType
  *
  * @return array
  */
 private function doApiCall($url, $method, $access_token = null, $parameters = array(), $contentType = 'text/xml')
 {
     $request = array();
     $headers = array();
     $request[CURLOPT_TIMEOUT] = (int) $this->http_timeout;
     $request[CURLOPT_USERAGENT] = str_replace('%VERSION%', Client::VERSION, $this->http_user_agent);
     $request[CURLOPT_CUSTOMREQUEST] = $method;
     if (!empty($parameters)) {
         if ($method === 'GET') {
             $url = Tools::httpBuildUrl($url, array("query" => http_build_query($parameters)), Tools::HTTP_URL_JOIN_QUERY);
         } else {
             if (is_array($parameters)) {
                 $request[CURLOPT_POSTFIELDS] = http_build_query($parameters);
             } else {
                 if (is_string($parameters)) {
                     $request[CURLOPT_POSTFIELDS] = $parameters;
                 }
             }
         }
     }
     $request[CURLOPT_URL] = $url;
     if (!is_null($contentType)) {
         $headers[] = "Content-Type: {$contentType}";
     }
     if (!is_null($access_token)) {
         $headers[] = 'Authorization: Bearer ' . $access_token;
     }
     if (!empty($this->http_proxy_host)) {
         $request[CURLOPT_PROXY] = $this->http_proxy_host;
         if (!empty($this->http_proxy_port)) {
             $request[CURLOPT_PROXYPORT] = $this->http_proxy_port;
         }
         if (!empty($this->http_proxy_type)) {
             $request[CURLOPT_PROXYTYPE] = $this->http_proxy_type;
         }
         if (!empty($this->http_proxy_auth)) {
             $request[CURLOPT_PROXYAUTH] = $this->http_proxy_auth;
         }
         if (!empty($this->http_proxy_user)) {
             $request[CURLOPT_PROXYUSERPWD] = $this->http_proxy_user . ':' . $this->http_proxy_pass;
         }
     }
     $request[CURLOPT_HTTPHEADER] = $headers;
     $this->logger->info(__CLASS__, 'api', sprintf('%s %s', $method, $url));
     $start = microtime(true);
     @(list($result, $status_code, $error, $errno) = $this->execCurl($request));
     $end = microtime(true);
     $duration = (int) round(($end - $start) * 1000);
     if ($errno === 0) {
         $return = array('http_code' => $status_code, 'http_body' => $result, 'duration' => $duration);
         if ($status_code >= 400) {
             $this->logger->error(__CLASS__, 'api', sprintf('Response HTTP code %s, body length %s bytes, duration %sms on endpoint %s %s', $status_code, strlen($result), $duration, $method, $url));
         } else {
             if ($status_code >= 300) {
                 $this->logger->warning(__CLASS__, 'api', sprintf('Response HTTP code %s, body length %s bytes, duration %sms on endpoint %s %s', $status_code, strlen($result), $duration, $method, $url));
             } else {
                 $this->logger->info(__CLASS__, 'api', sprintf('Response HTTP code %s, body length %s bytes, duration %sms', $status_code, strlen($result), $duration));
             }
         }
     } else {
         $return = array('error_msg' => $error, 'error_num' => $errno, 'duration' => $duration);
         $this->logger->error(__CLASS__, 'api', sprintf('cURL error #%s : %s', $errno, $error));
     }
     return $return;
 }