xref: /plugin/statistics/helper.php (revision ba6b3b10263c8439a170c848fb8e0ce174e4f468)
1<?php
2
3use dokuwiki\Extension\Plugin;
4use dokuwiki\HTTP\DokuHTTPClient;
5use dokuwiki\plugin\sqlite\SQLiteDB;
6use dokuwiki\plugin\statistics\IpResolverException;
7use dokuwiki\plugin\statistics\Logger;
8use dokuwiki\plugin\statistics\Query;
9use dokuwiki\plugin\statistics\StatisticsGraph;
10
11/**
12 * Statistics Plugin
13 *
14 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
15 * @author  Andreas Gohr <andi@splitbrain.org>
16 */
17class helper_plugin_statistics extends Plugin
18{
19    protected ?Query $oQuery = null;
20    protected ?StatisticsGraph $oGraph = null;
21    protected ?SQLiteDB $db = null;
22    public ?DokuHTTPClient $httpClient = null; // public for testing purposes
23
24    /**
25     * Get SQLiteDB instance
26     *
27     * @return SQLiteDB|null
28     * @throws Exception when SQLite initialization failed
29     */
30    public function getDB(): ?SQLiteDB
31    {
32        if (!$this->db instanceof SQLiteDB) {
33            if (!class_exists(SQLiteDB::class)) throw new Exception('SQLite Plugin missing');
34            $this->db = new SQLiteDB('statistics', DOKU_PLUGIN . 'statistics/db/');
35        }
36        return $this->db;
37    }
38
39
40    /**
41     * Return an instance of the query class
42     *
43     * @return Query
44     */
45    public function getQuery(): Query
46    {
47        if (is_null($this->oQuery)) {
48            $this->oQuery = new Query($this);
49        }
50        return $this->oQuery;
51    }
52
53    /**
54     * Return an instance of the logger class
55     *
56     * @return Logger
57     */
58    public function getLogger(): Logger
59    {
60        return new Logger($this);
61    }
62
63    /**
64     * Return an instance of the Graph class
65     *
66     * @return StatisticsGraph
67     */
68    public function getGraph($from, $to, $width, $height)
69    {
70        if (is_null($this->oGraph)) {
71            $this->oGraph = new StatisticsGraph($this, $from, $to, $width, $height);
72        }
73        return $this->oGraph;
74    }
75
76    /**
77     * Just send a 1x1 pixel blank gif to the browser
78     *
79     * @called from log.php
80     *
81     * @author Andreas Gohr <andi@splitbrain.org>
82     * @author Harry Fuecks <fuecks@gmail.com>
83     */
84    public function sendGIF($transparent = true)
85    {
86        if ($transparent) {
87            $img = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7');
88        } else {
89            $img = base64_decode('R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=');
90        }
91        header('Content-Type: image/gif');
92        header('Content-Length: ' . strlen($img));
93        header('Connection: Close');
94        echo $img;
95        flush();
96        // Browser should drop connection after this
97        // Thinks it got the whole image
98    }
99
100    /**
101     * Return the location information for an IP address
102     *
103     * @throws IpResolverException
104     * @noinspection HttpUrlsUsage
105     */
106    public function resolveIP($ip)
107    {
108        $http = $this->httpClient ?: new DokuHTTPClient();
109        $http->timeout = 7;
110        $json = $http->get('http://ip-api.com/json/' . $ip); // yes, it's HTTP only
111
112        if (!$json) {
113            throw new IpResolverException('Failed talk to ip-api.com.');
114        }
115        try {
116            $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
117        } catch (\JsonException $e) {
118            throw new IpResolverException('Failed to decode JSON from ip-api.com.', $e->getTrace(), 0, $e);
119        }
120        if (!isset($data['status'])) {
121            throw new IpResolverException('Invalid ip-api.com result for' . $ip, $data);
122        }
123        // we do not check for 'success' status here. when the API can't resolve the IP we still log it
124        // without location data, so we won't re-query it in the next 30 days.
125
126        return $data;
127    }
128}
129