Searchengine.php 10.3 KB
Newer Older
1 2
<?php

3
namespace App\Models;
4 5

use App\Jobs\Search;
6
use App\MetaGer;
7 8
use Cache;
use Illuminate\Foundation\Bus\DispatchesJobs;
9 10
use Log;
use Redis;
11

12
abstract class Searchengine
13
{
14
    use DispatchesJobs;
15

16
    protected $ch; # Curl Handle zum erhalten der Ergebnisse
Karl's avatar
Karl committed
17 18
    protected $getString = ""; # Der String für die Get-Anfrage
    protected $engine; # Die ursprüngliche Engine XML
Dominik Hebeler's avatar
Dominik Hebeler committed
19 20 21 22 23 24
    public $enabled  = true; # true, wenn die Suchmaschine nicht explizit disabled ist
    public $results  = []; # Die geladenen Ergebnisse
    public $ads      = []; # Die geladenen Werbungen
    public $products = []; # Die geladenen Produkte
    public $loaded   = false; # wahr, sobald die Ergebnisse geladen wurden
    public $cached   = false;
Karl's avatar
Karl committed
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

    public $ip; # Die IP aus der metager
    public $gefVon; # Der HTML-Code für die Verlinkung des Suchanbieters
    public $uses; # Die Anzahl der Nutzungen dieser Suchmaschine
    public $homepage; # Die Homepage dieser Suchmaschine
    public $name; # Der Name dieser Suchmaschine
    public $disabled; # Ob diese Suchmaschine ausgeschaltet ist
    public $useragent; # Der HTTP Useragent
    public $startTime; # Die Zeit der Erstellung dieser Suchmaschine
    public $hash; # Der Hash-Wert dieser Suchmaschine

    public $fp; # Wird für Artefakte benötigt
    protected $socketNumber = null; # Wird für Artefakte benötigt
    protected $counter      = 0; # Wird eventuell für Artefakte benötigt
    public $write_time      = 0; # Wird eventuell für Artefakte benötigt
    public $connection_time = 0; # Wird eventuell für Artefakte benötigt
41 42 43

    public function __construct(\SimpleXMLElement $engine, MetaGer $metager)
    {
Karl's avatar
Karl committed
44
        # Versucht möglichst viele attribute aus dem engine XML zu laden
45 46 47
        foreach ($engine->attributes() as $key => $value) {
            $this->$key = $value->__toString();
        }
Karl's avatar
Karl committed
48 49

        # Standardhomepage metager.de
50 51 52 53
        if (!isset($this->homepage)) {
            $this->homepage = "https://metager.de";
        }

Karl's avatar
Karl committed
54
        # Speichert die XML der Engine
55
        $this->engine = $engine->asXML();
56

Karl's avatar
Karl committed
57
        # Cache Standarddauer 60
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
        if (!isset($this->cacheDuration)) {
            $this->cacheDuration = 60;
        }

        # Eine Suchmaschine kann automatisch temporär deaktiviert werden, wenn es Verbindungsprobleme gab:
        if (isset($this->disabled) && strtotime($this->disabled) <= time()) {
            # In diesem Fall ist der Timeout der Suchmaschine abgelaufen.
            $this->enable($metager->getSumaFile(), "Die Suchmaschine " . $this->name . " wurde wieder eingeschaltet.");
        } elseif (isset($this->disabled) && strtotime($this->disabled) > time()) {
            $this->enabled = false;
            return;
        }

        $this->useragent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1";
        $this->ip        = $metager->getIp();
        $this->gefVon    = "<a href=\"" . $this->homepage . "\" target=\"_blank\">" . $this->displayName . "</a>";
        $this->startTime = microtime();

Karl's avatar
Karl committed
76
        # Suchstring generieren
77 78 79 80 81 82 83 84 85 86 87
        $q = "";
        if (isset($this->hasSiteSearch) && $this->hasSiteSearch === "1") {
            if (strlen($metager->getSite()) === 0) {
                $q = $metager->getQ();
            } else {
                $q = $metager->getQ() . " site:" . $metager->getSite();
            }

        } else {
            $q = $metager->getQ();
        }
Dominik Hebeler's avatar
Dominik Hebeler committed
88
        $this->getString  = $this->generateGetString($q, $metager->getUrl(), $metager->getLanguage(), $metager->getCategory());
89 90
        $this->hash       = md5($this->host . $this->getString . $this->port . $this->name);
        $this->resultHash = $metager->getHashCode();
91
        $this->canCache   = $metager->canCache();
Phil Höfer's avatar
Phil Höfer committed
92
        if (!isset($this->additionalHeaders)) {$this->additionalHeaders = "";}
93 94 95 96
    }

    abstract public function loadResults($result);

Karl's avatar
Karl committed
97
    # ???
98 99
    public function getNext(MetaGer $metager, $result)
    {
100 101 102

    }

Karl's avatar
Karl committed
103
    # Prüft, ob die Suche bereits gecached ist, ansonsted wird sie als Job dispatched
104 105
    public function startSearch(\App\MetaGer $metager)
    {
106
        if ($this->canCache && Cache::has($this->hash)) {
107
            $this->cached = true;
108
            $this->retrieveResults($metager);
109
        } else {
Karl's avatar
Karl committed
110 111 112 113 114 115
            /* Die Anfragen an die Suchmaschinen werden nun von der Laravel-Queue bearbeitet:
             *  Hinweis: solange in der .env der QUEUE_DRIVER auf "sync" gestellt ist, werden die Abfragen
             *  nacheinander abgeschickt.
             *  Sollen diese Parallel verarbeitet werden, muss ein anderer QUEUE_DRIVER verwendet werden.
             *  siehe auch: https://laravel.com/docs/5.2/queues
             */
Phil Höfer's avatar
Phil Höfer committed
116
            $this->dispatch(new Search($this->resultHash, $this->host, $this->port, $this->name, $this->getString, $this->useragent, $this->additionalHeaders));
117 118 119
        }
    }

Karl's avatar
Karl committed
120
    # Ruft die Ranking-Funktion aller Ergebnisse auf.
121
    public function rank($eingabe)
122 123
    {
        foreach ($this->results as $result) {
124
            $result->rank($eingabe);
125 126 127
        }
    }

Karl's avatar
Karl committed
128
    # Magic ???
129 130 131 132 133 134 135 136 137
    private function setStatistic($key, $val)
    {

        $oldVal = floatval(Redis::hget($this->name, $key)) * $this->uses;
        $newVal = ($oldVal + max($val, 0)) / $this->uses;
        Redis::hset($this->name, $key, $newVal);
        $this->$key = $newVal;
    }

Karl's avatar
Karl committed
138
    # Entfernt wenn gesetzt das disabled="1" für diese Suchmaschine aus der sumas.xml
139 140 141 142 143 144 145 146 147 148 149 150 151
    public function enable($sumaFile, $message)
    {
        Log::info($message);
        $xml = simplexml_load_file($sumaFile);
        unset($xml->xpath("//sumas/suma[@name='" . $this->name . "']")['0']['disabled']);
        $xml->saveXML($sumaFile);
    }

    public function closeFp()
    {
        fclose($this->fp);
    }

Karl's avatar
Karl committed
152
    # Öffnet einen neuen Socket für diese Engine
153 154 155 156 157 158 159 160 161 162 163
    public function getSocket()
    {
        $number = Redis::hget('search.' . $this->hash, $this->name);
        if ($number === null) {
            die("test");
            return null;
        } else {
            return pfsockopen($this->getHost() . ":" . $this->port . "/$number", $this->port, $errstr, $errno, 1);
        }
    }

Karl's avatar
Karl committed
164
    # Fragt die Ergebnisse von Redis ab und lädt Sie
165
    public function retrieveResults(MetaGer $metager)
166 167 168
    {
        if ($this->loaded) {
            return true;
169 170
        }

171
        $body = "";
172
        if ($this->canCache && $this->cacheDuration > 0 && Cache::has($this->hash)) {
173 174 175
            $body = Cache::get($this->hash);
        } elseif (Redis::hexists('search.' . $this->resultHash, $this->name)) {
            $body = Redis::hget('search.' . $this->resultHash, $this->name);
176
            if ($this->canCache && $this->cacheDuration > 0) {
177 178 179 180 181 182
                Cache::put($this->hash, $body, $this->cacheDuration);
            }

        }
        if ($body !== "") {
            $this->loadResults($body);
183
            $this->getNext($metager, $body);
184 185 186 187 188 189 190 191 192 193 194 195 196
            $this->loaded = true;
            Redis::hdel('search.' . $this->hash, $this->name);
            return true;
        } else {
            return false;
        }
    }

    public function shutdown()
    {
        Redis::del($this->host . "." . $this->socketNumber);
    }

Karl's avatar
Karl committed
197
    # Erstellt den für die Get-Anfrage genutzten Host-Link
198 199 200 201 202 203 204 205 206 207 208 209
    protected function getHost()
    {
        $return = "";
        if ($this->port === "443") {
            $return .= "tls://";
        } else {
            $return .= "tcp://";
        }
        $return .= $this->host;
        return $return;
    }

Karl's avatar
Karl committed
210
    # Erstellt den für die Get-Anfrage genutzten String
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
    private function generateGetString($query, $url, $language, $category)
    {
        $getString = "";

        # Skript:
        if (strlen($this->skript) > 0) {
            $getString .= $this->skript;
        } else {
            $getString .= "/";
        }

        # FormData:
        if (strlen($this->formData) > 0) {
            $getString .= "?" . $this->formData;
        }

        # Wir müssen noch einige Platzhalter in dem GET-String ersetzen:
Karl's avatar
Karl committed
228
        # Useragent
229 230 231 232
        if (strpos($getString, "<<USERAGENT>>")) {
            $getString = str_replace("<<USERAGENT>>", $this->urlEncode($this->useragent), $getString);
        }

Karl's avatar
Karl committed
233
        # Query
234 235 236 237
        if (strpos($getString, "<<QUERY>>")) {
            $getString = str_replace("<<QUERY>>", $this->urlEncode($query), $getString);
        }

Karl's avatar
Karl committed
238
        # IP
239 240 241 242
        if (strpos($getString, "<<IP>>")) {
            $getString = str_replace("<<IP>>", $this->urlEncode($this->ip), $getString);
        }

Karl's avatar
Karl committed
243
        # Language
244 245 246 247
        if (strpos($getString, "<<LANGUAGE>>")) {
            $getString = str_replace("<<LANGUAGE>>", $this->urlEncode($language), $getString);
        }

Karl's avatar
Karl committed
248
        # Category
249 250 251 252
        if (strpos($getString, "<<CATEGORY>>")) {
            $getString = str_replace("<<CATEGORY>>", $this->urlEncode($category), $getString);
        }

Karl's avatar
Karl committed
253
        # Affildata
254 255 256 257 258 259
        if (strpos($getString, "<<AFFILDATA>>")) {
            $getString = str_replace("<<AFFILDATA>>", $this->getOvertureAffilData($url), $getString);
        }
        return $getString;
    }

Karl's avatar
Karl committed
260
    # Wandelt einen String nach aktuell gesetztem inputEncoding dieser Searchengine in URL-Format um
261 262 263 264 265 266 267 268 269
    protected function urlEncode($string)
    {
        if (isset($this->inputEncoding)) {
            return urlencode(mb_convert_encoding($string, $this->inputEncoding));
        } else {
            return urlencode($string);
        }
    }

Karl's avatar
Karl committed
270
    # Liefert Sonderdaten für Yahoo
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
    private function getOvertureAffilData($url)
    {
        $affil_data = 'ip=' . $this->ip;
        $affil_data .= '&ua=' . $this->useragent;
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            $affil_data .= '&xfip=' . $_SERVER['HTTP_X_FORWARDED_FOR'];
        }
        $affilDataValue = $this->urlEncode($affil_data);
        # Wir benötigen die ServeUrl:
        $serveUrl = $this->urlEncode($url);

        return "&affilData=" . $affilDataValue . "&serveUrl=" . $serveUrl;
    }

    public function isEnabled()
    {
        return $this->enabled;
    }
Karl's avatar
Karl committed
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310

    # Artefaktmethoden

    public function getCurlInfo()
    {
        return curl_getinfo($this->ch);
    }

    public function getCurlErrors()
    {
        return curl_errno($this->ch);
    }

    public function addCurlHandle($mh)
    {
        curl_multi_add_handle($mh, $this->ch);
    }

    public function removeCurlHandle($mh)
    {
        curl_multi_remove_handle($mh, $this->ch);
    }
311
}