43d9459328ce9cf8fa4bc998f7de4b96555a1a7f
[phinde.git] / src / phinde / Crawler.php
1 <?php
2 namespace phinde;
3
4 class Crawler
5 {
6     protected $es;
7     protected $queue;
8
9     /**
10      * If the links only should be shown, not queued
11      */
12     protected $showLinksOnly = false;
13
14     static $supportedIndexTypes = array(
15         'application/atom+xml'  => '\\phinde\\LinkExtractor\\Atom',
16         'application/xhtml+xml' => '\\phinde\\LinkExtractor\\Html',
17         'text/html'             => '\\phinde\\LinkExtractor\\Html',
18     );
19
20     public function __construct()
21     {
22         $this->es = new Elasticsearch($GLOBALS['phinde']['elasticsearch']);
23         $this->queue = new Queue();
24     }
25
26     public function crawl($url)
27     {
28         $res = $this->fetch($url);
29         if ($res === false) {
30             return;
31         }
32
33         $linkInfos = $this->extractLinks($res);
34         if ($this->showLinksOnly) {
35             $this->showLinks($linkInfos);
36         } else {
37             $this->enqueue($linkInfos);
38         }
39     }
40
41     protected function fetch($url)
42     {
43         $existingDoc = $this->es->get($url);
44
45         $req = new HttpRequest($url);
46         $req->setHeader(
47             'accept',
48             implode(',', array_keys(static::$supportedIndexTypes))
49         );
50         if ($existingDoc) {
51             $nMoDate = strtotime($existingDoc->modate);
52             $req->setHeader('If-Modified-Since: ' . date('r', $nMoDate));
53         }
54
55         $res = $req->send();
56         if ($res->getStatus() === 304) {
57             //not modified since last time, so don't crawl again
58             return false;
59         } else if ($res->getStatus() !== 200) {
60             throw new \Exception(
61                 "Response code is not 200 but "
62                 . $res->getStatus() . ", stopping"
63             );
64         }
65         return $res;
66     }
67
68     protected function extractLinks(\HTTP_Request2_Response $res)
69     {
70         $mimetype = explode(';', $res->getHeader('content-type'))[0];
71         if (!isset(static::$supportedIndexTypes[$mimetype])) {
72             echo "MIME type not supported for indexing: $mimetype\n";
73             return array();
74         }
75
76         $class = static::$supportedIndexTypes[$mimetype];
77         $extractor = new $class();
78         return $extractor->extract($res);
79     }
80
81     protected function enqueue($linkInfos)
82     {
83         foreach ($linkInfos as $linkInfo) {
84             if ($this->es->isKnown($linkInfo->url)) {
85                 continue;
86             }
87             $this->es->markQueued($linkInfo->url);
88             $this->queue->addToIndex(
89                 $linkInfo->url, $linkInfo->title, $linkInfo->source
90             );
91             if (Helper::isUrlAllowed($linkInfo->url)) {
92                 $this->queue->addToCrawl($linkInfo->url);
93             }
94         }
95     }
96
97     protected function showLinks($linkInfos)
98     {
99         foreach ($linkInfos as $linkInfo) {
100             echo $linkInfo->url . "\n";
101             if ($linkInfo->title) {
102                 echo '  title: ' . $linkInfo->title . "\n";
103                 echo '  source: ' . $linkInfo->source . "\n";
104             }
105         }
106     }
107
108     public function setShowLinksOnly($showLinksOnly)
109     {
110         $this->showLinksOnly = $showLinksOnly;
111     }
112 }
113 ?>