Das mit dem file() und dann das Array kann ich nicht empfehlen. Die Datei wird explosionsartig anwachsen und bei jedem Ref größer und entsprechend wird der RAM voller und voller.
Der Ansatz von Melegrian macht da schon mehr Sinn, wobei er das Array nicht auf 20 Zeilen reduziert, sondern einfach nur 20 Zeilen ausliest und dann abbricht. Wenn das Array aber 100.000 Zeilen enthalten hatte, lagen die bereits im RAM.
Wie man es normalerweise macht:
- alle Daten in eine Datenbanktabelle schreiben
- zu einem bestimmten Zeitpunkt z.B. 1x am Tag die Daten durcharbeiten lassen und das gewünschte Ergebnis in einen Cache legen
- bei der Ausgabe nur auf den Cache zugreifen
Mit Dateien lässt sich das nur schwer halbwegs performant nachbauen. In dem Fall würde ich pro Artikel einen Ordner machen und für jede Phrase eine Datei deren Inhalt nur der Counter ist. Zusätzlich wird eine Art Indexdatei geschrieben wo die Dateien und Counter der Phrasen mit den häufigsten Zugriffen gespeichert wird.
Wie man sieht ist das auf Dateienbasis eher unpraktisch.
Mit 20 Phrasen pro Artikel in eine Datei wird übrigens nicht gehen, weil sobald 20 Phrasen drin sind, kommt keine neue hinzu, egal wie häufig sie noch gesucht werden (sind also 20 unwichtige drin, werden keine wichtigen mehr ergänzt).
Eine Notlösung gäbe es aber trotzdem, wenn es denn unbedingt eine Datei sein soll. In dem Fall speichert man einfach 1.000 Phrasen pro Artikel und liest immer nur 20 aus. Durch die Begrenzung wäre dann gewährleistet, dass der RAM nicht vollläuft und 1.000 Phrasen sollten reichen, damit wirklich wichtige nicht verloren gehen.
D.h. "$phrasen[$phrase] = $counter;" wäre die Grundstruktur der Daten. Das ganze wird dann per serialize() oder json_encode() in einer Datei gespeichert, so dass Du nur per file_get_contents() und unserialize/json_decode() wieder die Daten in nutzbarer Form zurück erhälst.
Bevor Du die neuen Phrasen speicherst machst Du dann nur das:
Code: Alles auswählen
asort($phrasen); // sortieren nach meisten zugriffe zuerst
$phrasen = array_slice($phrasen, 0, 1000); // auf 1000 phrasen reduzieren
Neue Wörter wären dann so:
Code: Alles auswählen
// $phrase hast du mit dem q aus der url gefüllt
// und $filename ist mit dem Namen gefüllt, der sich pro Artikel ändert. Dann weiter:
//
// datei vorhanden? wenn ja auslesen, wenn nein leeres array setzen
$phrasen = file_exists($filename) ? json_decode(file_get_contents($filename)) : array();
if ($phrasen !== null) {
if (!isset($phrasen[$phrase])) {
$phrasen[$phrase] = 1; // neue phrase hinzufügen
}
else {
$phrasen[$phrase]++; // counter der bestehenden phrase um eins erhöhen
}
// daten neu in datei schreiben, hier eine funktion von mir:
function mkfile($filename, $data='', $chmod=0644) {
$h = @fopen($filename, 'w');
@fwrite($h, $data);
@fclose($h);
@umask(0000);
@chmod($filename, $chmod);
}
// sortieren und reduzieren
asort($phrasen);
$phrasen = array_slice($phrasen, 0, 1000);
// und so dann neu schreiben:
mkfile($filename, json_encode($phrasen));
}
Die Abfrage !== null ist übrigens sehr wichtig. Wenn man nämlich Daten einliest kann es sein, dass parallel ein anderer Besucher einen Schreibvorgang ausgelöst hat. In dem Fall ist die Datei nicht vollständig geschrieben und json_decode() wird null resultieren, weil der eingelesene String ungültig ist. json_decode() verifziert also gleichzeitig die Gültikeit der Daten. Das erspart uns die Prüfung ob die Datei gelocked ist oder nicht (versucht es erst gar nicht mit LOCK, selbst das ist nicht 100% verbindlich, json_decode() bzw. unserialize() kann das viel besser).
Bei der Ausgabe kannst Du dann so vorgehen und nur die ersten 20 Elemente ausgeben. Also:
Code: Alles auswählen
if ($phrasen) {
$i = 0;
foreach ($phrasen as $phrase => $counter) {
$i++;
echo $i . '. ' . $phrase . '<br />';
if ($i > 19) {
break;
}
}
}
Zusätzlich wird es übrigens nötig sein die $phrase auf utf8 zu prüfen. Entsprechende Funktionen findest Du im Netz. Ist es nicht utf8 solltest Du es in utf8 wandeln.
Auch solltest Du Dir überlegen ob Du die Zugriffe zumindest pro IP begrenzt, damit jemand mit F5 nicht Deine Statistik zerschießt.
Zuletzt solltest Du sicherstellen, dass kein XSS resultiert. Du solltest also auf $phrase, bevor Du es in das Array packst auch noch mal per htmlspecialchars() behandeln. Ansonsten rufe ich Deine Seite nämlich einfach auf mit einem Ref der q=<script>alert('xss')</script> enthält (oder einen lustigen Backlink ^^) und schon habe ich Dich
Zusätzlich könntest Du darüber nachdenken $phrase in seiner Länge zu begrenzen z.B. mit substr($phrase, 0, 255) oder besser alles über strlen($phrase) > 255 ablehnen. Sonst könnte jemand q= mit unendlich vielen Daten füllen, die Du wiederrum in Deiner Datei ablegst und ständig in den RAM lädst. Bei einer Datenbank gibt es dieses Problem normalerweise nicht, weil das entsprechende Feld meist VARCHAR(255) ist.
Und was mir auch noch einfällt: Jemand könnte sich einen Scherz erlauben und böse Wörter in den q= setzen. Das ginge sogar über Google, wenn man weiß wie man Deine Seite verbindlich findet. Ein Schimpfwortfilter wäre also auch zu überlegen.
Wie man sieht heißt PHP auch über die Sicherheit nachdenken.
Die Sache mit den undefinierten Variablen ist übrigens eine fehlerhafte Einstellung Deines Servers. Es ist natürlich richtig, dass diese Meldung erscheint, aber diese sollte nur für Programmierer angezeigt werden, weil es sich um keine kritische Fehlermeldung handelt.