xref: /plugin/statistics/helper.php (revision 523da37276acfd8d5f4f6c331a0062602b449fec)
16b6f8822SAndreas Gohr<?php
2a8acb244SAndreas Gohr
3*523da372SAndreas Gohruse dokuwiki\ErrorHandler;
4d5ef99ddSAndreas Gohruse dokuwiki\Extension\Plugin;
5ba6b3b10SAndreas Gohruse dokuwiki\HTTP\DokuHTTPClient;
6d5ef99ddSAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB;
7*523da372SAndreas Gohruse dokuwiki\plugin\statistics\DummyLogger;
8*523da372SAndreas Gohruse dokuwiki\plugin\statistics\IgnoreException;
9ba6b3b10SAndreas Gohruse dokuwiki\plugin\statistics\IpResolverException;
10aab59130SAndreas Gohruse dokuwiki\plugin\statistics\Logger;
11b6632b6eSAndreas Gohruse dokuwiki\plugin\statistics\Query;
1264ba7f66SAnna Dabrowskause dokuwiki\plugin\statistics\StatisticsGraph;
13d5ef99ddSAndreas Gohr
146b6f8822SAndreas Gohr/**
156b6f8822SAndreas Gohr * Statistics Plugin
166b6f8822SAndreas Gohr *
176b6f8822SAndreas Gohr * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
186b6f8822SAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
196b6f8822SAndreas Gohr */
20d5ef99ddSAndreas Gohrclass helper_plugin_statistics extends Plugin
21a8acb244SAndreas Gohr{
2241d1fffcSAndreas Gohr    protected ?Query $oQuery = null;
23c4c84f98SAndreas Gohr    protected ?StatisticsGraph $oGraph = null;
24762f4807SAndreas Gohr    protected ?SQLiteDB $db = null;
25ba6b3b10SAndreas Gohr    public ?DokuHTTPClient $httpClient = null; // public for testing purposes
266b6f8822SAndreas Gohr
276b6f8822SAndreas Gohr    /**
28d5ef99ddSAndreas Gohr     * Get SQLiteDB instance
29d5ef99ddSAndreas Gohr     *
30d5ef99ddSAndreas Gohr     * @return SQLiteDB|null
31483101d3SAndreas Gohr     * @throws Exception when SQLite initialization failed
32d5ef99ddSAndreas Gohr     */
33483101d3SAndreas Gohr    public function getDB(): ?SQLiteDB
34d5ef99ddSAndreas Gohr    {
352adee4c6SAndreas Gohr        if (!$this->db instanceof SQLiteDB) {
36211caa5dSAndreas Gohr            if (!class_exists(SQLiteDB::class)) throw new Exception('SQLite Plugin missing');
37762f4807SAndreas Gohr            $this->db = new SQLiteDB('statistics', DOKU_PLUGIN . 'statistics/db/');
38d5ef99ddSAndreas Gohr        }
39d5ef99ddSAndreas Gohr        return $this->db;
40d5ef99ddSAndreas Gohr    }
41d5ef99ddSAndreas Gohr
42d5ef99ddSAndreas Gohr
43d5ef99ddSAndreas Gohr    /**
446b6f8822SAndreas Gohr     * Return an instance of the query class
456b6f8822SAndreas Gohr     *
46b6632b6eSAndreas Gohr     * @return Query
476b6f8822SAndreas Gohr     */
48211caa5dSAndreas Gohr    public function getQuery(): Query
49a8acb244SAndreas Gohr    {
506b6f8822SAndreas Gohr        if (is_null($this->oQuery)) {
51b6632b6eSAndreas Gohr            $this->oQuery = new Query($this);
526b6f8822SAndreas Gohr        }
536b6f8822SAndreas Gohr        return $this->oQuery;
546b6f8822SAndreas Gohr    }
556b6f8822SAndreas Gohr
566b6f8822SAndreas Gohr    /**
576b6f8822SAndreas Gohr     * Return an instance of the logger class
586b6f8822SAndreas Gohr     *
59*523da372SAndreas Gohr     * When the logger cannot be created for any reason a DummyLogger is returned
60*523da372SAndreas Gohr     *
61*523da372SAndreas Gohr     * @return Logger|DummyLogger
626b6f8822SAndreas Gohr     */
63*523da372SAndreas Gohr    public function getLogger()
64a8acb244SAndreas Gohr    {
65*523da372SAndreas Gohr        try {
6641d1fffcSAndreas Gohr            return new Logger($this);
67*523da372SAndreas Gohr        } catch (Exception $e) {
68*523da372SAndreas Gohr            if (!$e instanceof IgnoreException) {
69*523da372SAndreas Gohr                ErrorHandler::logException($e);
70*523da372SAndreas Gohr            }
71*523da372SAndreas Gohr
72*523da372SAndreas Gohr            return new DummyLogger();
73*523da372SAndreas Gohr        }
746b6f8822SAndreas Gohr    }
756b6f8822SAndreas Gohr
766b6f8822SAndreas Gohr    /**
776b6f8822SAndreas Gohr     * Return an instance of the Graph class
786b6f8822SAndreas Gohr     *
791664ba1dSAndreas Gohr     * @return StatisticsGraph
806b6f8822SAndreas Gohr     */
81211caa5dSAndreas Gohr    public function getGraph($from, $to, $width, $height)
82a8acb244SAndreas Gohr    {
836b6f8822SAndreas Gohr        if (is_null($this->oGraph)) {
84f0a4cceeSAnna Dabrowska            $this->oGraph = new StatisticsGraph($this, $from, $to, $width, $height);
856b6f8822SAndreas Gohr        }
866b6f8822SAndreas Gohr        return $this->oGraph;
876b6f8822SAndreas Gohr    }
886b6f8822SAndreas Gohr
896b6f8822SAndreas Gohr    /**
904f41a2ccSAndreas Gohr     * Just send a 1x1 pixel blank gif to the browser
914f41a2ccSAndreas Gohr     *
9287e0f0b1SAndreas Gohr     * @called from dispatch.php
934f41a2ccSAndreas Gohr     *
944f41a2ccSAndreas Gohr     * @author Andreas Gohr <andi@splitbrain.org>
954f41a2ccSAndreas Gohr     * @author Harry Fuecks <fuecks@gmail.com>
964f41a2ccSAndreas Gohr     */
97a8acb244SAndreas Gohr    public function sendGIF($transparent = true)
98a8acb244SAndreas Gohr    {
99259897e1SAndreas Gohr        if ($transparent) {
1004f41a2ccSAndreas Gohr            $img = base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7');
101259897e1SAndreas Gohr        } else {
102259897e1SAndreas Gohr            $img = base64_decode('R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=');
103259897e1SAndreas Gohr        }
1044f41a2ccSAndreas Gohr        header('Content-Type: image/gif');
1054f41a2ccSAndreas Gohr        header('Content-Length: ' . strlen($img));
1064f41a2ccSAndreas Gohr        header('Connection: Close');
107a8acb244SAndreas Gohr        echo $img;
1084f41a2ccSAndreas Gohr        flush();
1094f41a2ccSAndreas Gohr        // Browser should drop connection after this
110211caa5dSAndreas Gohr        // Thinks it got the whole image
1114f41a2ccSAndreas Gohr    }
112ba6b3b10SAndreas Gohr
113ba6b3b10SAndreas Gohr    /**
114ba6b3b10SAndreas Gohr     * Return the location information for an IP address
115ba6b3b10SAndreas Gohr     *
116ba6b3b10SAndreas Gohr     * @throws IpResolverException
117ba6b3b10SAndreas Gohr     * @noinspection HttpUrlsUsage
118ba6b3b10SAndreas Gohr     */
119ba6b3b10SAndreas Gohr    public function resolveIP($ip)
120ba6b3b10SAndreas Gohr    {
121ba6b3b10SAndreas Gohr        $http = $this->httpClient ?: new DokuHTTPClient();
122ba6b3b10SAndreas Gohr        $http->timeout = 7;
123ba6b3b10SAndreas Gohr        $json = $http->get('http://ip-api.com/json/' . $ip); // yes, it's HTTP only
124ba6b3b10SAndreas Gohr
125ba6b3b10SAndreas Gohr        if (!$json) {
126ba6b3b10SAndreas Gohr            throw new IpResolverException('Failed talk to ip-api.com.');
127ba6b3b10SAndreas Gohr        }
128ba6b3b10SAndreas Gohr        try {
129ba6b3b10SAndreas Gohr            $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
130ba6b3b10SAndreas Gohr        } catch (\JsonException $e) {
131ba6b3b10SAndreas Gohr            throw new IpResolverException('Failed to decode JSON from ip-api.com.', $e->getTrace(), 0, $e);
132ba6b3b10SAndreas Gohr        }
133ba6b3b10SAndreas Gohr        if (!isset($data['status'])) {
134ba6b3b10SAndreas Gohr            throw new IpResolverException('Invalid ip-api.com result for' . $ip, $data);
135ba6b3b10SAndreas Gohr        }
136ba6b3b10SAndreas Gohr        // we do not check for 'success' status here. when the API can't resolve the IP we still log it
137ba6b3b10SAndreas Gohr        // without location data, so we won't re-query it in the next 30 days.
138ba6b3b10SAndreas Gohr
139ba6b3b10SAndreas Gohr        return $data;
140ba6b3b10SAndreas Gohr    }
1416b6f8822SAndreas Gohr}
142