Result.php 15.5 KB
Newer Older
1 2
<?php

3
namespace App\Models;
4

Karl's avatar
Karl committed
5 6 7
/* Die Klasse Result sammelt alle Informationen über ein einzelnes Suchergebnis.
 *  Die Results werden von den Suchmaschinenspezifischen Parser-Skripten erstellt.
 */
8 9
class Result
{
Karl's avatar
Karl committed
10 11 12 13
    public $provider; # Die Engine von der das Suchergebnis kommt
    public $titel; # Der Groß Angezeigte Name für das Suchergebnis
    public $link; # Der Link auf die Ergebnisseite
    public $anzeigeLink; # Der tatsächlich angezeigte Link (rein optisch)
14 15
    public $descr; # Die eventuell gekürzte Beschreibung des Suchergebnisses
    public $longDescr; # Die ungekürzte Beschreibung des Suchergebnisses
16 17
    public $gefVon; # Die Suchmaschine von der dieses Ergebnis stammt
    public $gefVonLink;
Karl's avatar
Karl committed
18 19 20 21 22 23
    public $sourceRank; # Das Ranking für dieses Suchergebnis von der Seite, die es geliefert hat (implizit durch Ergebnisreihenfolge: 20 - Position in Ergebnisliste)
    public $partnershop; # Ist das Ergebnis von einem Partnershop? (bool)
    public $image; # Ein Vorschaubild für das Suchergebnis (als URL)

    public $proxyLink; # Der Link für die Seite über unseren Proxy-Service
    public $engineBoost = 1; # Der Boost für den Provider des Suchergebnisses
24
    public $valid = true; # Ob das Ergebnis noch gültig ist (bool)
Karl's avatar
Karl committed
25
    public $host; # Der aus dem Link gelesene Host des Suchergebnisses
26 27 28
    public $strippedHost; # Der Host in Form "foo.bar.de"
    public $strippedDomain; # Die Domain in Form "bar.de"
    public $strippedLink; # Der Link in Form "foo.bar.de/test"
Karl's avatar
Karl committed
29
    public $rank; # Das Ranking für das Ergebnis
30
    public $new = true;
Karl's avatar
Karl committed
31 32

    # Erstellt ein neues Ergebnis
33
    public function __construct($provider, $titel, $link, $anzeigeLink, $descr, $gefVon, $gefVonLink, $sourceRank, $additionalInformation = [])
34
    {
35 36
        $this->titel = strip_tags(trim($titel));
        $this->link = trim($link);
37
        $this->anzeigeLink = trim($anzeigeLink);
Dominik Hebeler's avatar
Dominik Hebeler committed
38
        $this->anzeigeLink = preg_replace("/(http[s]{0,1}:\/\/){0,1}(www\.){0,1}/si", "", $this->anzeigeLink);
39 40 41
        $this->descr = strip_tags(trim($descr), '<p>');
        $this->descr = preg_replace("/\n+/si", " ", $this->descr);
        $this->longDescr = $this->descr;
42 43
        if (strlen($this->descr) > 150) {
            $this->descr = wordwrap($this->descr, 150);
44
            $this->descr = substr($this->descr, 0, strpos($this->descr, "\n"));
45
            $this->descr .= "&#8230;"; // Ellipsis character
46
        }
47
        $this->gefVon = trim($gefVon);
48
        $this->gefVonLink = trim($gefVonLink);
49
        $this->proxyLink = $this->generateProxyLink($this->link);
50 51 52 53 54
        $this->sourceRank = $sourceRank;
        if ($this->sourceRank <= 0 || $this->sourceRank > 20) {
            $this->sourceRank = 20;
        }
        $this->sourceRank = 20 - $this->sourceRank;
55 56
        if (isset($provider->{"engine-boost"})) {
            $this->engineBoost = floatval($provider->{"engine-boost"});
57 58 59
        } else {
            $this->engineBoost = 1;
        }
60 61
        $this->valid = true;
        $this->host = @parse_url($link, PHP_URL_HOST);
Dominik Hebeler's avatar
Dominik Hebeler committed
62 63 64
        $this->strippedHost = $this->getStrippedHost($this->link);
        $this->strippedDomain = $this->getStrippedDomain($this->link);
        $this->strippedLink = $this->getStrippedLink($this->link);
65 66 67 68
        $this->rank = 0;
        $this->partnershop = isset($additionalInformation["partnershop"]) ? $additionalInformation["partnershop"] : false;
        $this->image = isset($additionalInformation["image"]) ? $additionalInformation["image"] : "";
        $this->price = isset($additionalInformation["price"]) ? $additionalInformation["price"] : 0;
69
        $this->price_text = $this->price_to_text($this->price);
Dominik Hebeler's avatar
Dominik Hebeler committed
70
        $this->additionalInformation = $additionalInformation;
71
        $this->hash = spl_object_hash($this);
72 73
    }

74 75
    private function price_to_text($price)
    {
76
        $euros = floor($price / 100);
77 78 79 80 81 82 83 84 85
        $cents = $price % 100;
        $price_text = $euros . ',';
        if ($cents < 10) {
            $price_text .= '0';
        }
        $price_text .= $cents . ' €';
        return $price_text;
    }

Karl's avatar
Karl committed
86 87 88 89 90
    /* Ranked das Ergebnis nach folgenden Aspekten:
     *  Startwert 0
     *  + 0.02 * Sourcerank (20 - Position in Ergebnisliste des Suchanbieters)
     *  * Engine-Boost
     */
91
    public function rank($eingabe)
92 93
    {
        $rank = 0;
Karl's avatar
Karl committed
94 95

        # Boost für Source Ranking
96 97
        $rank += ($this->sourceRank * 0.02);

Karl's avatar
Karl committed
98 99 100 101 102 103 104 105 106 107 108
        # Boost für passende ??? URL
        $rank += $this->calcURLBoost($eingabe);

        # Boost für Vorkommen der Suchwörter:
        $rank += $this->calcSuchwortBoost($eingabe);

        # Boost für Suchmaschine
        if ($this->engineBoost > 0) {
            $rank *= floatval($this->engineBoost);
        }

Dominik Hebeler's avatar
Dominik Hebeler committed
109 110 111 112 113
        # Runter Ranken von Yandex Ergebnissen mit zu viel kyrillischen Texten
        if (stripos($this->gefVon, "yandex") !== false) {
            $rank -= $this->calcYandexBoost($eingabe);
        }

Karl's avatar
Karl committed
114 115 116
        $this->rank = $rank;
    }

Dominik Hebeler's avatar
Dominik Hebeler committed
117 118 119 120 121 122 123 124 125 126 127 128 129
    # Berechnet, ob dieses Suchergebnis einen Malus erhalten soll, oder nicht
    # Übergeben werden alle Yandex Ergebnisse
    # Wenn die Suchworte kein kyrillisches Zeichen enthalten, wird das Ergebnis schlechter bewertet,
    # falls es selbst zu viele kyrillische Zeichen enthält
    private function calcYandexBoost($tmpEingabe)
    {
        $maxRatio = 0.1; # Gibt den Prozentsatz von Kyrillischen Zeichen in Titel und Beschreibung an, ab dem das Ergebnis runter gerankt werden soll
        if (preg_match('/[А-Яа-яЁё]/u', $tmpEingabe) === 1) {
            # Das Suchwort enthält kyrillische Zeichen, also dürfen es auch die Ergebnisse
            return 0;
        } else {
            # Wir überprüfen das Verhältnis von Kyrillischen Zeichen im Titel
            if (preg_match_all('/[А-Яа-яЁё]/u', $this->titel, $matches)) {
130
                $count = sizeof($matches[0]);
Dominik Hebeler's avatar
Dominik Hebeler committed
131
                $titleSize = strlen($this->titel);
132
                $percKyr = $count / $titleSize;
Dominik Hebeler's avatar
Dominik Hebeler committed
133 134 135 136 137 138
                if ($percKyr > $maxRatio) {
                    return 5;
                }
            }
            # Wir überprüfen das Verhältnis von Kyrillischen Zeichen in der Beschreibung
            if (preg_match_all('/[А-Яа-яЁё]/u', $this->descr, $matches)) {
139
                $count = sizeof($matches[0]);
Dominik Hebeler's avatar
Dominik Hebeler committed
140
                $descrSize = strlen($this->descr);
141
                $percKyr = $count / $descrSize;
Dominik Hebeler's avatar
Dominik Hebeler committed
142 143 144 145 146 147 148 149
                if ($percKyr > $maxRatio) {
                    return 5;
                }
            }
        }
        return 0;
    }

Karl's avatar
Karl committed
150 151 152
    # Berechnet den Ranking-Boost durch ??? URL
    public function calcURLBoost($tmpEingabe)
    {
153 154 155 156
        $link = $this->anzeigeLink;
        if (strpos($link, "http") !== 0) {
            $link = "http://" . $link;
        }
157 158 159
        $link = @parse_url($link, PHP_URL_HOST) . @parse_url($link, PHP_URL_PATH);
        $tmpLi = $link;
        $count = 0;
Karl's avatar
Karl committed
160 161
        $tmpLink = "";
        # Löscht verschiedene unerwünschte Teile aus $link und $tmpEingabe
162
        $regex = [
Karl's avatar
Karl committed
163 164 165 166 167 168 169
            "/\s+/si", # Leerzeichen
            "/http:/si", # "http:"
            "/https:/si", # "https:"
            "/www\./si", # "www."
            "/\//si", # "/"
            "/\./si", # "."
            "/-/si", # "-"
170 171
        ];
        foreach ($regex as $reg) {
172
            $link = preg_replace($regex, "", $link);
173 174 175
            $tmpEingabe = preg_replace($regex, "", $tmpEingabe);
        }
        foreach (str_split($tmpEingabe) as $char) {
Karl's avatar
Karl committed
176 177 178 179 180
            if (!$char
                || !$tmpEingabe
                || strlen($tmpEingabe) === 0
                || strlen($char) === 0
            ) {
181 182 183 184 185 186 187
                continue;
            }
            if (strpos(strtolower($tmpLink), strtolower($char)) >= 0) {
                $count++;
                $tmpLink = str_replace(urlencode($char), "", $tmpLink);
            }
        }
Dominik Hebeler's avatar
Dominik Hebeler committed
188
        if (strlen($this->descr) > 40 && strlen($link) > 0) {
Karl's avatar
Karl committed
189 190 191 192 193
            return $count / ((strlen($link)) * 60); # ???
        } else {
            return 0;
        }
    }
194

Karl's avatar
Karl committed
195 196 197
    # Berechnet den Ranking-Boost durch das Vorkommen von Suchwörtern
    private function calcSuchwortBoost($tmpEingabe)
    {
198 199
        $maxRank = 0.1;
        $tmpTitle = $this->titel;
200
        $tmpDescription = $this->descr;
201 202 203 204
        $isWithin = false;
        $tmpRank = 0;
        $tmpEingabe = preg_replace("/\b\w{1,3}\b/si", "", $tmpEingabe);
        $tmpEingabe = preg_replace("/\s+/si", " ", $tmpEingabe);
Karl's avatar
Karl committed
205

206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
        foreach (explode(" ", trim($tmpEingabe)) as $el) {
            if (strlen($tmpTitle) === 0 || strlen($el) === 0 || strlen($tmpDescription) === 0) {
                continue;
            }

            $el = preg_quote($el, "/");
            if (strlen($tmpTitle) > 0) {
                if (preg_match("/\b$el\b/si", $tmpTitle)) {
                    $tmpRank += .7 * .6 * $maxRank;
                } elseif (strpos($tmpTitle, $el) !== false) {
                    $tmpRank += .3 * .6 * $maxRank;
                }
            }
            if (strlen($tmpDescription) > 0) {
                if (preg_match("/\b$el\b/si", $tmpDescription)) {
                    $tmpRank += .7 * .4 * $maxRank;
                } elseif (strpos($tmpDescription, $el) !== false) {
                    $tmpRank += .3 * .4 * $maxRank;
                }
            }
        }

Karl's avatar
Karl committed
228 229
        $tmpRank /= sizeof(explode(" ", trim($tmpEingabe))) * 10;
        return $tmpRank;
230 231
    }

Karl's avatar
Karl committed
232
    # Überprüft ob das Ergebnis aus irgendwelchen Gründen unerwünscht ist.
233 234
    public function isValid(\App\MetaGer $metager)
    {
235 236 237
        # Perönliche Host und Domain Blacklist
        if (in_array(strtolower($this->strippedHost), $metager->getUserHostBlacklist())
            || in_array(strtolower($this->strippedDomain), $metager->getUserDomainBlacklist())) {
238 239 240
            return false;
        }

241 242
        # Persönliche URL Blacklist
        foreach ($metager->getUserUrlBlacklist() as $word) {
243 244 245 246
            if (strpos(strtolower($this->link), $word)) {
                return false;
            }

247 248
        }

Karl's avatar
Karl committed
249
        # Allgemeine URL und Domain Blacklist
250 251 252 253
        if ($this->strippedHost !== "" && (in_array($this->strippedHost, $metager->getDomainBlacklist()) || in_array($this->strippedLink, $metager->getUrlBlacklist()))) {
            return false;
        }

Karl's avatar
Karl committed
254
        # Eventueller Sprachfilter
Dominik Hebeler's avatar
Dominik Hebeler committed
255
        if ($metager->getLang() !== "all") {
256
            if (!isset($this->langCode) || $this->langCode === null || $metager->getLang() !== $this->langCode) {
257 258 259 260 261
                return false;
            }

        }

Karl's avatar
Karl committed
262
        # Stopworte
263 264 265 266 267 268 269
        foreach ($metager->getStopWords() as $stopWord) {
            $text = $this->titel . " " . $this->descr;
            if (stripos($text, $stopWord) !== false) {
                return false;
            }
        }

Dominik Hebeler's avatar
Dominik Hebeler committed
270
        /*
Karl's avatar
Karl committed
271
        # Phrasensuche:
272 273
        $text = strtolower($this->titel) . " " . strtolower($this->descr);
        foreach ($metager->getPhrases() as $phrase) {
274 275
        if (strpos($text, $phrase) === false) {
        return false;
276
        }
277 278
        }
         */
279

Phil Höfer's avatar
Phil Höfer committed
280
        /* Der Dublettenfilter, der sicher stellt,
Karl's avatar
Karl committed
281 282
         *  dass wir nach Möglichkeit keinen Link doppelt in der Ergebnisliste haben.
         */
283 284 285 286 287 288 289 290
        if ($metager->addLink($this->strippedLink)) {
            $metager->addHostCount($this->strippedHost);
            return true;
        } else {
            return false;
        }
    }

Karl's avatar
Karl committed
291 292 293 294 295
    /* Liest aus einem Link den Host.
     *  Dieser wird dabei in die Form:
     *  "http://www.foo.bar.de/test?ja=1" -> "foo.bar.de"
     *  gebracht.
     */
296
    public function getStrippedHost($link)
297
    {
298 299
        $match = $this->getUrlElements($link);
        return $match['host'];
300
    }
Karl's avatar
Karl committed
301 302 303 304 305 306

    /* Entfernt "http://", "www" und Parameter von einem Link
     *  Dieser wird dabei in die Form:
     *  "http://www.foo.bar.de/test?ja=1" -> "foo.bar.de/test"
     *  gebracht.
     */
307
    public function getStrippedLink($link)
308
    {
309
        $match = $this->getUrlElements($link);
310
        return $match['host'] . $match['path'];
311 312
    }

Karl's avatar
Karl committed
313 314 315 316 317
    /* Liest aus einem Link die Domain.
     *  Dieser wird dabei in die Form:
     *  "http://www.foo.bar.de/test?ja=1" -> "bar.de"
     *  gebracht.
     */
318
    public function getStrippedDomain($link)
319
    {
320 321
        $match = $this->getUrlElements($link);
        return $match['domain'];
322 323
    }

Karl's avatar
Karl committed
324
    # Erstellt aus einem Link einen Proxy-Link für unseren Proxy-Service
325
    public function generateProxyLink($link)
326 327 328 329
    {
        if (!$link) {
            return "";
        }
330

Dominik Hebeler's avatar
Dominik Hebeler committed
331 332 333 334 335
        # Link to our new Proxy software:
        $pw = md5(env('PROXY_PASSWORD') . $link);

        $proxyUrl = base64_encode(str_rot13($link));
        $proxyUrl = urlencode(str_replace("/", "<<SLASH>>", $proxyUrl));
336

Dominik Hebeler's avatar
Dominik Hebeler committed
337 338
        return "https://proxy.suma-ev.de/" . $pw . "/" . $proxyUrl;
/*
339 340 341 342 343 344 345 346 347 348 349
$tmp = $link;
$tmp = preg_replace("/\r?\n$/s", "", $tmp);
$tmp = str_replace("=", "=3d", $tmp);
$tmp = str_replace("?", "=3f", $tmp);
$tmp = str_replace("%", "=25", $tmp);
$tmp = str_replace("&", "=26", $tmp);
$tmp = str_replace(";", "=3b", $tmp);
$tmp = preg_replace("#^([\w+.-]+)://#s", "$1/", $tmp);
$tmp = str_replace("//", "/=2f", $tmp);
return "https://proxy.suma-ev.de/mger/nph-proxy.cgi/en/w0/" . $tmp;
 */
350
    }
Karl's avatar
Karl committed
351

352 353
    /* Liest aus einer URL alle Informationen aus
     * https://max:muster@www.example.site.page.com:8080/index/indexer/list.html?p1=A&p2=B#ressource
Karl's avatar
Karl committed
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
     * (?:((?:http)|(?:https))(?::\/\/))?
     * https://                  => [1] = http / https
     * (?:([a-z0-9.\-_~]+):([a-z0-9.\-_~]+)@)?
     * username:password@        => [2] = username, [3] = password
     * (?:(www)(?:\.))?
     * www.                      => [4] = www
     * ((?:(?:[a-z0-9.\-_~]+\.)+)?([a-z0-9.\-_~]+\.[a-z0-9.\-_~]+))
     * example.site.page.com     => [5] = example.site.page.com, [6] = page.com
     * (?:(?::)(\d+))?
     * :8080                     => [7] = 8080
     * ((?:(?:\/[a-z0-9.\-_~]+)+)(?:\.[a-z0-9.\-_~]+)?)?
     * /index/indexer/list.html  => [8] = /index/indexer/list.html
     * (\?[a-z0-9.\-_~]+=[a-z0-9.\-_~]+(?:&[a-z0-9.\-_~]+=[a-z0-9.\-_~]+)*)?
     * ?p1=A&p2=B                => [9] = ?p1=A&p2=B
     * (?:(?:#)([a-z0-9.\-_~]+))?
     * #ressource                => [10] = ressource
370 371 372
     */
    public function getUrlElements($url)
    {
Dominik Hebeler's avatar
Dominik Hebeler committed
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
        $parts = parse_url($url);
        $re = [];

        $re["schema"] = empty($parts["scheme"]) ? "" : $parts["scheme"];
        $re["username"] = empty($parts["user"]) ? "" : $parts["user"];
        $re["password"] = empty($parts["pass"]) ? "" : $parts["pass"];
        $re["web"] = "";
        $re["host"] = $parts["host"];
        if (stripos($re["host"], "www.") === 0) {
            $re["web"] = "www.";
            $re["host"] = substr($re["host"], strpos($re["host"], ".") + 1);
        }
        $hostParts = explode(".", $re["host"]);
        if (sizeof($hostParts) >= 3) {
            $re["domain"] = $hostParts[sizeof($hostParts) - 2] . "." . $hostParts[sizeof($hostParts) - 1];
388
        } else {
Dominik Hebeler's avatar
Dominik Hebeler committed
389
            $re["domain"] = $re["host"];
390
        }
Dominik Hebeler's avatar
Dominik Hebeler committed
391 392 393 394 395 396
        $re["port"] = empty($parts["port"]) ? ($re["schema"] === "http" ? 80 : ($re["schema"] === "https" ? 443 : 80)) : $parts["port"];
        $re["path"] = empty($parts["path"]) ? "" : $parts["path"];
        $re["query"] = empty($parts["query"]) ? "" : $parts["query"];
        $re["fragment"] = empty($parts["fragment"]) ? "" : $parts["fragment"];

        return $re;
397 398
    }

Karl's avatar
Karl committed
399 400 401 402 403 404
    # Getter

    public function getRank()
    {
        return $this->rank;
    }
405

406 407 408 409 410 411 412 413 414
    public function getDate()
    {
        if (isset($this->additionalInformation["date"])) {
            return $this->additionalInformation["date"];
        } else {
            return null;
        }
    }

415 416 417 418 419 420 421 422 423
    public function getLangString()
    {
        $string = "";

        $string .= $this->titel;
        $string .= $this->descr;

        return $string;
    }
Phil Höfer's avatar
Phil Höfer committed
424
}