1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* webtrees: online genealogy |
4
|
|
|
* Copyright (C) 2018 webtrees development team |
5
|
|
|
* This program is free software: you can redistribute it and/or modify |
6
|
|
|
* it under the terms of the GNU General Public License as published by |
7
|
|
|
* the Free Software Foundation, either version 3 of the License, or |
8
|
|
|
* (at your option) any later version. |
9
|
|
|
* This program is distributed in the hope that it will be useful, |
10
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
11
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12
|
|
|
* GNU General Public License for more details. |
13
|
|
|
* You should have received a copy of the GNU General Public License |
14
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
15
|
|
|
*/ |
16
|
|
|
declare(strict_types=1); |
17
|
|
|
|
18
|
|
|
namespace Fisharebest\Webtrees\Http\Middleware; |
19
|
|
|
|
20
|
|
|
use Closure; |
21
|
|
|
use Fisharebest\Webtrees\Database; |
22
|
|
|
use Fisharebest\Webtrees\Resolver; |
23
|
|
|
use Fisharebest\Webtrees\Session; |
24
|
|
|
use Fisharebest\Webtrees\Tree; |
25
|
|
|
use Fisharebest\Webtrees\User; |
26
|
|
|
use Fisharebest\Webtrees\View; |
27
|
|
|
use Symfony\Component\HttpFoundation\Request; |
28
|
|
|
use Symfony\Component\HttpFoundation\Response; |
29
|
|
|
use Throwable; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Middleware to count requests for particular pages. |
33
|
|
|
* |
34
|
|
|
* For historical reasons, we record the names of the original webtrees script and parameter. |
35
|
|
|
*/ |
36
|
|
|
class PageHitCounter implements MiddlewareInterface |
37
|
|
|
{ |
38
|
|
|
// Which pages/routes do we count? |
39
|
|
|
const PAGE_NAMES = [ |
40
|
|
|
'family' => 'family.php', |
41
|
|
|
'individual' => 'individual.php', |
42
|
|
|
'media' => 'mediaviewer.php', |
43
|
|
|
'note' => 'note.php', |
44
|
|
|
'repository' => 'repo.php', |
45
|
|
|
'source' => 'source.php', |
46
|
|
|
'tree-page' => 'index.php', |
47
|
|
|
'user-page' => 'index.php', |
48
|
|
|
]; |
49
|
|
|
|
50
|
|
|
/** @var Tree|null */ |
51
|
|
|
private $tree; |
52
|
|
|
|
53
|
|
|
/** @var User */ |
54
|
|
|
private $user; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* PageHitCounter constructor. |
58
|
|
|
* |
59
|
|
|
* @param Resolver $resolver |
60
|
|
|
* @param Tree|null $tree |
61
|
|
|
*/ |
62
|
|
|
public function __construct(User $user, Tree $tree = null) |
63
|
|
|
{ |
64
|
|
|
$this->tree = $tree; |
65
|
|
|
$this->user = $user; |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @param Request $request |
70
|
|
|
* @param Closure $next |
71
|
|
|
* |
72
|
|
|
* @return Response |
73
|
|
|
* @throws Throwable |
74
|
|
|
*/ |
75
|
|
|
public function handle(Request $request, Closure $next): Response |
76
|
|
|
{ |
77
|
|
|
$page_hits = 0; |
78
|
|
|
|
79
|
|
|
if ($this->tree !== null && $this->tree->getPreference('SHOW_COUNTER')) { |
80
|
|
|
$route = $request->get('route'); |
81
|
|
|
|
82
|
|
|
$page_name = self::PAGE_NAMES[$route] ?? ''; |
83
|
|
|
|
84
|
|
|
switch ($route) { |
85
|
|
|
case 'family': |
|
|
|
|
86
|
|
|
case 'individual': |
|
|
|
|
87
|
|
|
case 'media': |
88
|
|
|
case 'note': |
89
|
|
|
case 'repository': |
90
|
|
|
case 'source': |
91
|
|
|
$page_hits = $this->countHit($this->tree, $page_name, $request->get('xref', '')); |
92
|
|
|
break; |
93
|
|
|
|
94
|
|
|
case 'tree-page': |
95
|
|
|
$page_hits = $this->countHit($this->tree, $page_name, 'gedcom:' . $this->tree->getTreeId()); |
96
|
|
|
break; |
97
|
|
|
|
98
|
|
|
case 'user-page': |
99
|
|
|
$page_hits = $this->countHit($this->tree, $page_name, 'user:' . $this->user->getUserId()); |
100
|
|
|
break; |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
// Make the count available to the layout. |
105
|
|
|
View::share('page_hits', $page_hits); |
106
|
|
|
|
107
|
|
|
return $next($request); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Increment the page count. |
112
|
|
|
* |
113
|
|
|
* @param Tree $tree |
114
|
|
|
* @param string $page |
115
|
|
|
* @param string $parameter |
116
|
|
|
* |
117
|
|
|
* @return int |
118
|
|
|
*/ |
119
|
|
|
private function countHit(Tree $tree, $page, $parameter): int |
120
|
|
|
{ |
121
|
|
|
$gedcom_id = $tree->getTreeId(); |
122
|
|
|
|
123
|
|
|
// Don't increment the counter while we stay on the same page. |
124
|
|
|
if ( |
125
|
|
|
Session::get('last_gedcom_id') === $gedcom_id && |
126
|
|
|
Session::get('last_page_name') === $page && |
127
|
|
|
Session::get('last_page_parameter') === $parameter |
128
|
|
|
) { |
129
|
|
|
return Session::get('last_count'); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
$count = $this->getCount($tree, $page, $parameter); |
133
|
|
|
|
134
|
|
|
if ($count === 0) { |
135
|
|
|
Database::prepare( |
136
|
|
|
"INSERT INTO `##hit_counter` (gedcom_id, page_name, page_parameter, page_count) VALUES (:tree_id, :page, :parameter, 1)" |
137
|
|
|
)->execute([ |
138
|
|
|
'tree_id' => $gedcom_id, |
139
|
|
|
'page' => $page, |
140
|
|
|
'parameter' => $parameter, |
141
|
|
|
]); |
142
|
|
|
} else { |
143
|
|
|
Database::prepare( |
144
|
|
|
"UPDATE `##hit_counter` SET page_count = page_count + 1 WHERE gedcom_id = :tree_id AND page_name = :page AND page_parameter = :parameter" |
145
|
|
|
)->execute([ |
146
|
|
|
'tree_id' => $gedcom_id, |
147
|
|
|
'page' => $page, |
148
|
|
|
'parameter' => $parameter, |
149
|
|
|
]); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
$count++; |
153
|
|
|
|
154
|
|
|
Session::put('last_gedcom_id', $gedcom_id); |
155
|
|
|
Session::put('last_page_name', $page); |
156
|
|
|
Session::put('last_page_parameter', $parameter); |
157
|
|
|
Session::put('last_count', $count); |
158
|
|
|
|
159
|
|
|
return $count; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* How many times has a page been viewed |
164
|
|
|
* |
165
|
|
|
* @param Tree $tree |
166
|
|
|
* @param string $page |
167
|
|
|
* @param string $parameter |
168
|
|
|
* |
169
|
|
|
* @return int |
170
|
|
|
*/ |
171
|
|
|
public function getCount(Tree $tree, $page, $parameter): int |
172
|
|
|
{ |
173
|
|
|
return (int)Database::prepare( |
174
|
|
|
"SELECT page_count FROM `##hit_counter` WHERE gedcom_id = :tree_id AND page_name = :page AND page_parameter = :parameter" |
175
|
|
|
)->execute([ |
176
|
|
|
'tree_id' => $tree->getTreeId(), |
177
|
|
|
'page' => $page, |
178
|
|
|
'parameter' => $parameter, |
179
|
|
|
])->fetchOne(); |
180
|
|
|
} |
181
|
|
|
} |
182
|
|
|
|
As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next
break
.There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.