namespace Cubist\Matomo;
use GuzzleHttp\Client;
+use GuzzleHttp\Exception\RequestException;
+// Matomo Reporting API Wrapper
+// API Reference: https://developer.matomo.org/api-reference/reporting-api
class Reporting
{
+ const PERIOD_DAY = 'day';
+ const PERIOD_WEEK = 'week';
+ const PERIOD_MONTH = 'month';
+ const PERIOD_YEAR = 'year';
+ const PERIOD_RANGE = 'range';
+
+ const DATE_TODAY = 'today';
+ const DATE_YESTERDAY = 'yesterday';
+
+ const FORMAT_XML = 'xml';
+ const FORMAT_JSON = 'json';
+ const FORMAT_CSV = 'csv';
+ const FORMAT_TSV = 'tsv';
+ const FORMAT_HTML = 'html';
+ const FORMAT_RSS = 'rss';
+ const FORMAT_PHP = 'php';
+ const FORMAT_ORIGINAL = 'original';
+
+ /**
+ * @var string The base URL of the Matomo server
+ */
+ protected $serverUrl;
/**
- * @var string
+ * @var string The API access token
*/
protected $token;
/**
- * @var Client
+ * @var integer The Matomo ID of the website
+ */
+ protected $siteId;
+
+ /**
+ * @var string The date / date range for the report
+ */
+ protected $date = '';
+
+ /**
+ * @var string The period used to group the statistics
+ */
+ protected $period;
+
+ /**
+ * @var string What format the API response should return
+ */
+ protected $format;
+
+ /**
+ * @var string Language for translatable strings
+ */
+ protected $language = 'en';
+
+ /**
+ * @var Client Guzzle HTTP Client for making requests
*/
protected $client;
/**
- * @var null|array
+ * @var string Stores the URL of the last API call for debugging purposes
+ */
+ protected $lastApiUrl = '';
+
+ /**
+ * @var null|array Cached list of sites defined in Matomo, keyed by ID
+ */
+ protected $allSites = null;
+
+ public function __construct(
+ $baseUrl,
+ $token,
+ $siteId = null,
+ $date = self::DATE_YESTERDAY,
+ $period = self::PERIOD_DAY,
+ $format = self::FORMAT_JSON
+ ) {
+ $this->serverUrl = $baseUrl;
+ $this->token = $token;
+ $this->siteId = $siteId;
+ $this->date = $date;
+ $this->period = $period;
+ $this->format = $format;
+ $this->client = new Client(['base_uri' => $this->serverUrl]);
+ }
+
+ /**
+ * Get API token
+ *
+ * @return string
*/
- protected $_allSites = null;
+ public function getToken(): string
+ {
+ return $this->token;
+ }
- public function __construct($baseURL, $token)
+ /**
+ * Set API token
+ *
+ * @param string $token
+ * @return $this
+ */
+ public function setToken(string $token): Reporting
{
$this->token = $token;
- $this->client = new Client(['base_uri' => $baseURL]);
+
+ return $this;
+ }
+
+ /**
+ * Get current site ID
+ *
+ * @return mixed
+ */
+ public function getSiteId()
+ {
+ return $this->siteId;
+ }
+
+ /**
+ * Set current site ID
+ *
+ * @param mixed $id
+ * @return $this
+ */
+ public function setSiteId($id = null): Reporting
+ {
+ $this->siteId = $id;
+ return $this;
+ }
+
+ /**
+ * Get response format
+ *
+ * @return string
+ */
+ public function getFormat(): string
+ {
+ return $this->format;
+ }
+
+ /**
+ * Set response format
+ *
+ * @param string $format
+ * FORMAT_XML
+ * FORMAT_JSON
+ * FORMAT_CSV
+ * FORMAT_TSV
+ * FORMAT_HTML
+ * FORMAT_RSS
+ * FORMAT_PHP
+ * FORMAT_ORIGINAL
+ * @return $this
+ */
+ public function setFormat(string $format): Reporting
+ {
+ $this->format = $format;
+ return $this;
+ }
+
+ /**
+ * Get language
+ *
+ * @return string
+ */
+ public function getLanguage(): string
+ {
+ return $this->language;
+ }
+
+ /**
+ * Set language
+ *
+ * @param string $language
+ * @return $this
+ */
+ public function setLanguage(string $language): Reporting
+ {
+ $this->language = $language;
+ return $this;
+ }
+
+ /**
+ * Get date
+ *
+ * @return string
+ */
+ public function getDate(): string
+ {
+ return $this->date;
+ }
+
+ /**
+ * Set date
+ *
+ * @param string|null $date Format Y-m-d or class constant:
+ * DATE_TODAY
+ * DATE_YESTERDAY
+ * @return $this
+ */
+ public function setDate(string $date = null): Reporting
+ {
+ $this->date = $date;
+ return $this;
+ }
+
+ /**
+ * Get period
+ *
+ * @return string
+ */
+ public function getPeriod(): string
+ {
+ return $this->period;
+ }
+
+ /**
+ * Set time period
+ *
+ * @param string $period
+ * PERIOD_DAY
+ * PERIOD_MONTH
+ * PERIOD_WEEK
+ * PERIOD_YEAR
+ * PERIOD_RANGE
+ * @return $this
+ */
+ public function setPeriod(string $period): Reporting
+ {
+ $this->period = $period;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getServerUrl(): string {
+ return $this->serverUrl;
+ }
+
+ /**
+ * @return string
+ */
+ public function getLastApiUrl(): string {
+ return $this->lastApiUrl;
}
/**
*/
public function getAllSites()
{
- if (null === $this->_allSites) {
- $a = $this->_apicall('SitesManager.getAllSites');
- $this->_allSites = [];
+ if (null === $this->allSites) {
+ $a = $this->apiCall('SitesManager.getAllSites');
+ $this->allSites = [];
foreach ($a as $item) {
- $this->_allSites[$item['idsite']] = $item;
+ $this->allSites[$item['idsite']] = $item;
}
}
- return $this->_allSites;
+ return $this->allSites;
}
/**
*/
public function createSite($name, $url, $search = true, $ecommerce = false)
{
- $res = $this->_apicall('SitesManager.addSite', ['siteName' => $name, 'urls' => $url, 'ecommerce' => $ecommerce, 'siteSearch' => $search]);
+ $res = $this->apiCall('SitesManager.addSite', ['siteName' => $name, 'urls' => $url, 'ecommerce' => $ecommerce, 'siteSearch' => $search]);
return $res['value'] ?? null;
}
public function deleteSite($siteId)
{
- if (isset($this->_allSites[$siteId])) {
- unset($this->_allSites[$siteId]);
+ if (isset($this->allSites[$siteId])) {
+ unset($this->allSites[$siteId]);
}
- return $this->_apicall('SitesManager.deleteSite', ['idSite'=>$siteId]);
+ return $this->apiCall('SitesManager.deleteSite', ['idSite' => $siteId]);
}
/**
*/
public function getSiteIdByURL($url)
{
- $res = $this->_apicall('SitesManager.getSitesIdFromSiteUrl', ['url' => $url]);
+ $res = $this->apiCall('SitesManager.getSitesIdFromSiteUrl', ['url' => $url]);
return $res[0]['idsite'] ?? null;
}
- protected function _apicall($method, $params = [], $options = [])
+ public function getVisits($params = []) {
+ return $this->apiCall('VisitsSummary.get', $params);
+ }
+
+ public function getPageViews($params = []) {
+ return $this->apiCall('Actions.get', $params);
+ }
+
+ public function getPageUrls($params = []) {
+ return $this->apiCall('Actions.getPageUrls', $params);
+ }
+
+ public function getSearchKeywords($params = []) {
+ return $this->apiCall('Actions.getSiteSearchKeywords', $params);
+ }
+
+ public function getEventsByCategory($params = []) {
+ // Note: Passing the parameter 'expanded' => 1 will include the sub-table data
+ return $this->apiCall('Events.getCategory', $params + ['secondaryDimension' => 'eventName', 'expanded' => 0]);
+ }
+
+ public function getOutlinks($params = []) {
+ return $this->apiCall('Actions.getOutlinks', $params);
+ }
+
+ public function getCountries($params = []) {
+ return $this->apiCall('UserCountry.getCountry', $params);
+ }
+
+ /**
+ * @throws \Exception|RequestException
+ */
+ public function apiCall($method, $params = [], $options = [])
{
$default = [
- 'method' => $method,
+ 'token_auth' => $this->getToken(),
+ 'idSite' => $this->getSiteId(),
'module' => 'API',
- 'token_auth' => $this->token,
- 'format' => 'JSON'
+ 'method' => $method,
+ 'date' => $this->getDate(),
+ 'period' => $this->getPeriod(),
+ 'format' => $this->getFormat(),
+ 'language' => $this->getLanguage(),
+ 'filter_limit' => -1,
];
$defaultOptions = ['query' => array_merge($default, $params)];
- $response = $this->client->get('/', array_merge($defaultOptions, $options));
- return json_decode($response->getBody()->getContents(), true);
+ try {
+ $response = $this->client->get('/', array_merge($defaultOptions, $options)
+ + ['on_stats' => function ($stats) {
+ $this->lastApiUrl = $stats->getEffectiveUri(); // Store requested API URL for debugging purposes
+ }]
+ );
+
+ } catch (RequestException $e) {
+ // Just return the error instead of the full message because it contains the URL, which reveals the auth token.
+ throw new \Exception("Error: API call for method '{$method}' failed. {$e->getHandlerContext()['error']}");
+ }
+
+ $API_response = json_decode($response->getBody()->getContents(), true);
+
+ if (isset($API_response['result']) && $API_response['result'] === 'error') {
+ throw new \Exception("Error: {$API_response['message']}");
+ }
+
+ return $API_response;
}
-}
\ No newline at end of file
+}