Skip to content

Commit 9752e1b

Browse files
Introduce UserNote DTO and refactor Sorter (#594)
1 parent cffc380 commit 9752e1b

File tree

9 files changed

+742
-213
lines changed

9 files changed

+742
-213
lines changed

include/shared-manual.inc

Lines changed: 43 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,16 @@ $PGI = []; $SIDEBAR_DATA = '';
2323
// =============================================================================
2424

2525
require_once __DIR__ . '/../autoload.php';
26-
use phpweb\UserNotes\Sorter;
2726

28-
// Print out all user notes for this manual page
29-
function manual_notes($notes) {
27+
use phpweb\UserNotes\Sorter;
28+
use phpweb\UserNotes\UserNote;
29+
30+
/**
31+
* Print out all user notes for this manual page
32+
*
33+
* @param array<string, UserNote> $notes
34+
*/
35+
function manual_notes($notes):void {
3036
// Get needed values
3137
list($filename) = $GLOBALS['PGI']['this'];
3238

@@ -68,68 +74,63 @@ END_USERNOTE_HEADER;
6874
// If we have notes, print them out
6975
echo '<div id="allnotes">';
7076
foreach ($notes as $note) {
71-
manual_note_display(
72-
$note['xwhen'], $note['user'], $note['note'], $note['id'], $note['votes']
73-
);
77+
manual_note_display($note);
7478
}
7579
echo "</div>\n";
7680
echo "<div class=\"foot\">$addnotesnippet</div>\n";
7781
}
7882
echo "</section>";
7983
}
80-
// Get user notes from the appropriate text dump
81-
function manual_notes_load($id)
84+
85+
/**
86+
* Get user notes from the appropriate text dump
87+
*
88+
* @return array<string, UserNote>
89+
*/
90+
function manual_notes_load(string $id): array
8291
{
83-
// Initialize values
84-
$notes = [];
8592
$hash = substr(md5($id), 0, 16);
8693
$notes_file = $_SERVER['DOCUMENT_ROOT'] . "/backend/notes/" .
8794
substr($hash, 0, 2) . "/$hash";
8895

8996
// Open the note file for reading and get the data (12KB)
9097
// ..if it exists
9198
if (!file_exists($notes_file)) {
92-
return $notes;
99+
return [];
93100
}
101+
$notes = [];
94102
if ($fp = @fopen($notes_file, "r")) {
95103
while (!feof($fp)) {
96104
$line = chop(fgets($fp, 12288));
97105
if ($line == "") { continue; }
98106
@list($id, $sect, $rate, $ts, $user, $note, $up, $down) = explode("|", $line);
99-
$notes[$id] = [
100-
"id" => $id,
101-
"sect" => $sect,
102-
"rate" => $rate,
103-
"xwhen" => $ts,
104-
"user" => $user,
105-
"note" => base64_decode($note, true),
106-
"votes" => ["up" => (int)$up, "down" => (int)$down]
107-
];
107+
$notes[$id] = new UserNote($id, $sect, $rate, $ts, $user, base64_decode($note, true), (int) $up, (int) $down);
108108
}
109109
fclose($fp);
110110
}
111111
return $notes;
112112
}
113113

114114
// Print out one user note entry
115-
function manual_note_display($date, $name, $text, $id, $votes = ['up' => 0, 'down' => 0], $voteOption = true)
115+
function manual_note_display(UserNote $note, $voteOption = true)
116116
{
117-
if ($name) {
118-
$name = "\n <strong class=\"user\"><em>" . htmlspecialchars($name) . "</em></strong>";
117+
if ($note->user) {
118+
$name = "\n <strong class=\"user\"><em>" . htmlspecialchars($note->user) . "</em></strong>";
119119
} else {
120120
$name = "<strong class=\"user\"><em>Anonymous</em></strong>";
121121
}
122-
$name = ($id ? "\n <a href=\"#$id\" class=\"name\">$name</a><a class=\"genanchor\" href=\"#$id\"> &para;</a>" : "\n $name");
122+
$name = ($note->id ? "\n <a href=\"#{$note->id}\" class=\"name\">$name</a><a class=\"genanchor\" href=\"#{$note->id}\"> &para;</a>" : "\n $name");
123123

124124
// New date style will be relative time
125-
$datestr = relTime(new DateTime("@{$date}"));
126-
$fdatestr = date("Y-m-d h:i", $date);
127-
$text = clean_note($text);
125+
$date = new DateTime("@{$note->ts}");
126+
$datestr = relTime($date);
127+
$fdatestr = $date->format("Y-m-d h:i");
128+
$text = clean_note($note->text);
128129

129130
// Calculate note rating by up/down votes
130-
$vote = $votes['up'] - $votes['down'];
131-
$p = floor(($votes['up'] / (($votes['up'] + $votes['down']) ?: 1)) * 100);
132-
$rate = !$p && !($votes['up'] + $votes['down']) ? "no votes..." : "$p% like this...";
131+
$vote = $note->upvotes - $note->downvotes;
132+
$p = floor(($note->upvotes / (($note->upvotes + $note->downvotes) ?: 1)) * 100);
133+
$rate = !$p && !($note->upvotes + $note->downvotes) ? "no votes..." : "$p% like this...";
133134

134135
// Vote User Notes Div
135136
if ($voteOption) {
@@ -140,13 +141,13 @@ function manual_note_display($date, $name, $text, $id, $votes = ['up' => 0, 'dow
140141
$rredir_filename = urlencode($redir_filename);
141142
$votediv = <<<VOTEDIV
142143
<div class="votes">
143-
<div id="Vu{$id}">
144-
<a href="/manual/vote-note.php?id={$id}&amp;page={$rredir_filename}&amp;vote=up" title="Vote up!" class="usernotes-voteu">up</a>
144+
<div id="Vu{$note->id}">
145+
<a href="/manual/vote-note.php?id={$note->id}&amp;page={$rredir_filename}&amp;vote=up" title="Vote up!" class="usernotes-voteu">up</a>
145146
</div>
146-
<div id="Vd{$id}">
147-
<a href="/manual/vote-note.php?id={$id}&amp;page={$rredir_filename}&amp;vote=down" title="Vote down!" class="usernotes-voted">down</a>
147+
<div id="Vd{$note->id}">
148+
<a href="/manual/vote-note.php?id={$note->id}&amp;page={$rredir_filename}&amp;vote=down" title="Vote down!" class="usernotes-voted">down</a>
148149
</div>
149-
<div class="tally" id="V{$id}" title="{$rate}">
150+
<div class="tally" id="V{$note->id}" title="{$rate}">
150151
{$vote}
151152
</div>
152153
</div>
@@ -156,26 +157,26 @@ VOTEDIV;
156157
}
157158

158159
// If the viewer is logged in, show admin options
159-
if (isset($_COOKIE['IS_DEV']) && $id) {
160+
if (isset($_COOKIE['IS_DEV']) && $note->id) {
160161

161162
$admin = "\n <span class=\"admin\">\n " .
162163

163164
make_popup_link(
164-
'https://main.php.net/manage/user-notes.php?action=edit+' . $id,
165+
'https://main.php.net/manage/user-notes.php?action=edit+' . $note->id,
165166
'<img src="/images/[email protected]" height="12" width="12" alt="edit note">',
166167
'admin',
167168
'scrollbars=yes,width=650,height=400'
168169
) . "\n " .
169170

170171
make_popup_link(
171-
'https://main.php.net/manage/user-notes.php?action=reject+' . $id,
172+
'https://main.php.net/manage/user-notes.php?action=reject+' . $note->id,
172173
'<img src="/images/[email protected]" height="12" width="12" alt="reject note">',
173174
'admin',
174175
'scrollbars=no,width=300,height=200'
175176
) . "\n " .
176177

177178
make_popup_link(
178-
'https://main.php.net/manage/user-notes.php?action=delete+' . $id,
179+
'https://main.php.net/manage/user-notes.php?action=delete+' . $note->id,
179180
'<img src="/images/[email protected]" height="12" width="12" alt="delete note">',
180181
'admin',
181182
'scrollbars=no,width=300,height=200'
@@ -187,8 +188,8 @@ VOTEDIV;
187188

188189
echo <<<USER_NOTE_TEXT
189190

190-
<div class="note" id="$id">{$votediv}{$name}{$admin}<div class="date" title="$fdatestr"><strong>{$datestr}</strong></div>
191-
<div class="text" id="Hcom{$id}">
191+
<div class="note" id="{$note->id}">{$votediv}{$name}{$admin}<div class="date" title="$fdatestr"><strong>{$datestr}</strong></div>
192+
<div class="text" id="Hcom{$note->id}">
192193
{$text}
193194
</div>
194195
</div>
@@ -295,7 +296,7 @@ function manual_setup($setup) {
295296
$USERNOTES = manual_notes_load($filename);
296297
if ($USERNOTES) {
297298
$note = current($USERNOTES);
298-
$timestamps[] = $note["xwhen"];
299+
$timestamps[] = $note->ts;
299300
}
300301

301302
$lastmod = max($timestamps);

manual/add-note.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
include_once __DIR__ . '/../include/shared-manual.inc';
88
include __DIR__ . '/spam_challenge.php';
99

10+
use phpweb\UserNotes\UserNote;
11+
1012
site_header("Add Manual Note", ['css' => 'add-note.css']);
1113

1214
// Copy over "sect" and "redirect" from GET to POST
@@ -140,7 +142,7 @@
140142
// Print out preview of note
141143
echo '<p>This is what your entry will look like, roughly:</p>';
142144
echo '<div id="usernotes">';
143-
manual_note_display(time(), $user, $note, false);
145+
manual_note_display(new UserNote('', '', '', time(), $user, $note));
144146
echo '</div><br><br>';
145147
}
146148

manual/vote-note.php

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@
1717
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
1818
if (isset($_SERVER['HTTP_X_JSON']) && $_SERVER['HTTP_X_JSON'] == 'On' && !empty($_REQUEST['id']) && !empty($_REQUEST['page']) && ($N = manual_notes_load($_REQUEST['page'])) && array_key_exists($_REQUEST['id'], $N) && !empty($_REQUEST['vote']) && ($_REQUEST['vote'] === 'up' || $_REQUEST['vote'] === 'down')) {
1919
$response = [];
20-
$update = $N[$_REQUEST['id']]['votes']['up'] - $N[$_REQUEST['id']]['votes']['down'];
2120
$hash = substr(md5($_REQUEST['page']), 0, 16);
22-
$notes_file = $_SERVER['DOCUMENT_ROOT'] . "/backend/notes/" .
23-
substr($hash, 0, 2) . "/$hash";
21+
$notes_file = $_SERVER['DOCUMENT_ROOT'] . "/backend/notes/" . substr($hash, 0, 2) . "/$hash";
2422
if (!file_exists($notes_file)) {
2523
$response["success"] = false;
2624
$response["msg"] = "Invalid request.";
@@ -60,15 +58,13 @@
6058
}
6159
else {
6260
if ($_REQUEST['vote'] === 'up') {
63-
$N[$_REQUEST['id']]['votes']['up']++;
61+
$N[$_REQUEST['id']]->upvotes++;
6462
}
6563
elseif ($_REQUEST['vote'] === 'down') {
66-
$N[$_REQUEST['id']]['votes']['down']++;
64+
$N[$_REQUEST['id']]->downvotes++;
6765
}
68-
$update = $N[$_REQUEST['id']]['votes']['up'] - $N[$_REQUEST['id']]['votes']['down'];
6966
$hash = substr(md5($_REQUEST['page']), 0, 16);
70-
$notes_file = $_SERVER['DOCUMENT_ROOT'] . "/backend/notes/" .
71-
substr($hash, 0, 2) . "/$hash";
67+
$notes_file = $_SERVER['DOCUMENT_ROOT'] . "/backend/notes/" . substr($hash, 0, 2) . "/$hash";
7268
if (file_exists($notes_file)) {
7369
$data = [
7470
"noteid" => $_REQUEST['id'],
@@ -128,10 +124,7 @@
128124
<?php
129125
$backID = htmlspecialchars($_REQUEST['id']);
130126
$backPAGE = htmlspecialchars($_REQUEST['page']);
131-
manual_note_display(
132-
$N[$_REQUEST['id']]['xwhen'], $N[$_REQUEST['id']]['user'], $N[$_REQUEST['id']]['note'], $N[$_REQUEST['id']]['id'],
133-
$N[$_REQUEST['id']]['votes'], false
134-
);
127+
manual_note_display($N[$_REQUEST['id']], false);
135128
?>
136129
</div>
137130
<div style="width: 90%; margin: auto;"><p><a href="<?php echo "/{$backPAGE}#{$backID}"; ?>">&lt;&lt; Back to user notes page</a></p></div>
@@ -184,10 +177,7 @@
184177
<?php
185178
$backID = htmlspecialchars($_REQUEST['id']);
186179
$backPAGE = htmlspecialchars($_REQUEST['page']);
187-
manual_note_display(
188-
$N[$_REQUEST['id']]['xwhen'], $N[$_REQUEST['id']]['user'], $N[$_REQUEST['id']]['note'], $N[$_REQUEST['id']]['id'],
189-
$N[$_REQUEST['id']]['votes'], false
190-
);
180+
manual_note_display($N[$_REQUEST['id']], false);
191181
?>
192182
</div>
193183
<div style="width: 90%; margin: auto;"><p><a href="<?php echo "/{$backPAGE}#{$backID}"; ?>">&lt;&lt; Back to user notes page</a></p></div>

src/UserNotes/Sorter.php

Lines changed: 39 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ class Sorter {
2121

2222
private $ageWeight = 2;
2323

24-
public function sort(array &$notes) {
24+
/**
25+
* @param array<string, UserNote> $notes
26+
*/
27+
public function sort(array &$notes):void {
2528
// First we make a pass over the data to get the min and max values
2629
// for data normalization.
2730
$this->findMinMaxValues($notes);
@@ -36,80 +39,58 @@ public function sort(array &$notes) {
3639
$this->ageFactor *= $this->ageWeight;
3740

3841
// Second we loop through to calculate sort priority using the above numbers
39-
$this->calcSortPriority($notes);
42+
$prio = $this->calcSortPriority($notes);
4043

4144
// Third we sort the data.
42-
uasort($notes, [$this, 'factorSort']);
45+
uasort($notes, function ($a, $b) use ($prio) {
46+
return $prio[$b->id] <=> $prio[$a->id];
47+
});
4348
}
4449

45-
private function calcVotePriority(array $note) {
46-
return ($note['score'] - $this->minVote) * $this->voteFactor + .3;
50+
private function calcVotePriority(UserNote $note):float {
51+
return ($note->upvotes - $note->downvotes - $this->minVote) * $this->voteFactor + .3;
4752
}
4853

49-
private function calcRatingPriority(array $note) {
50-
if ($note['total'] <= 2) {
51-
return 0.5;
52-
}
53-
54-
return $note['rating'];
54+
private function calcRatingPriority(UserNote $note):float {
55+
return $note->upvotes + $note->downvotes <= 2 ? 0.5 : $this->calcRating($note);
5556
}
5657

57-
private function calcSortPriority(array &$notes) {
58-
foreach ($notes as &$note) {
59-
$prio = [
60-
'vote' => $this->calcVotePriority($note) * $this->voteWeight,
61-
'rating' => $this->calcRatingPriority($note) * $this->ratingWeight,
62-
'age' => ($note['xwhen'] - $this->minAge) * $this->ageFactor
63-
];
64-
$note['sort'] = $prio['value'] = array_sum($prio);
65-
}
58+
private function calcRating(UserNote $note):float {
59+
$totalVotes = $note->upvotes + $note->downvotes;
60+
return $totalVotes > 0 ? $note->upvotes / $totalVotes : .5;
6661
}
6762

68-
/*
69-
* Not sure why, but using `$b['sort'] - $a['sort']` does not seem to
70-
* work properly.
63+
/**
64+
* @param array<string, UserNote> $notes
7165
*/
72-
private function factorSort($a, $b) {
73-
if ($a['sort'] < $b['sort']) {
74-
return 1;
75-
}
76-
77-
if ($a['sort'] == $b['sort']) {
78-
return 0;
66+
private function calcSortPriority(array $notes): array {
67+
$prio = [];
68+
foreach ($notes as $note) {
69+
$prio[$note->id] = ($this->calcVotePriority($note) * $this->voteWeight)
70+
+ ($this->calcRatingPriority($note) * $this->ratingWeight)
71+
+ (($note->ts - $this->minAge) * $this->ageFactor);
7972
}
80-
81-
return -1;
73+
return $prio;
8274
}
8375

84-
private function findMinMaxValues(array &$notes) {
85-
$count = count($notes);
86-
if ($count <= 0) {
76+
/**
77+
* @param array<string, UserNote> $notes
78+
*/
79+
private function findMinMaxValues(array $notes):void {
80+
if ($notes === []) {
8781
return;
8882
}
89-
$note = array_shift($notes);
90-
$note['score'] = $net = ($note['votes']['up'] - $note['votes']['down']);
91-
$note['total'] = $totalVotes = ($note['votes']['up'] + $note['votes']['down']);
92-
$note['rating'] = $totalVotes > 0
93-
? $note['votes']['up'] / $totalVotes
94-
: .5;
9583

96-
$this->minVote = $this->maxVote = $net;
97-
$this->minAge = $this->maxAge = $age = $note['xwhen'];
98-
99-
$first = $note;
100-
101-
foreach ($notes as &$note) {
102-
$note['score'] = $net = ($note['votes']['up'] - $note['votes']['down']);
103-
$note['total'] = $totalVotes = ($note['votes']['up'] + $note['votes']['down']);
104-
$note['rating'] = $totalVotes > 0
105-
? $note['votes']['up'] / $totalVotes
106-
: .5;
107-
$age = $note['xwhen'];
108-
$this->maxVote = max($this->maxVote, $net);
109-
$this->minVote = min($this->minVote, $net);
110-
$this->maxAge = max($this->maxAge, $age);
111-
$this->minAge = min($this->minAge, $age);
84+
$first = array_shift($notes);
85+
86+
$this->minVote = $this->maxVote = ($first->upvotes - $first->downvotes);
87+
$this->minAge = $this->maxAge = $first->ts;
88+
89+
foreach ($notes as $note) {
90+
$this->maxVote = max($this->maxVote, ($note->upvotes - $note->downvotes));
91+
$this->minVote = min($this->minVote, ($note->upvotes - $note->downvotes));
92+
$this->maxAge = max($this->maxAge, $note->ts);
93+
$this->minAge = min($this->minAge, $note->ts);
11294
}
113-
array_unshift($notes, $first);
11495
}
11596
}

0 commit comments

Comments
 (0)