Update jQuery from 1.12.4 to 3.7.1
[phorkie.git] / src / phorkie / HtmlParser.php
1 <?php
2 namespace phorkie;
3
4 class HtmlParser
5 {
6     /**
7      * Contains error message when parse() failed
8      */
9     public $error;
10
11     /**
12      * Array with keys (URL title) and values (arrays of urls)
13      * Only supported URLs are included.
14      *
15      * @var array
16      */
17     protected $arGitUrls;
18
19
20
21     /**
22      * Extract git URLs from the given URL, eventually fetching
23      * HTML and extracting URLs from there.
24      *
25      * Sets $error and $arGitUrls class variables
26      *
27      * @param string $url  Git or HTTP URL
28      * @param string $html HTML content of $url
29      *
30      * @return boolean True when all went well, false in case of an error
31      * @uses   $error
32      * @uses   $arGitUrls
33      */
34     public function extractGitUrls($url, $html = null)
35     {
36         if ($url == '') {
37             $this->error = 'Empty fork URL';
38             return false;
39         }
40
41         $arUrl  = parse_url($url);
42         $scheme = isset($arUrl['scheme']) ? $arUrl['scheme'] : '';
43
44         if ($scheme == 'https' && isset($arUrl['host'])
45             && $arUrl['host'] == 'gist.github.com'
46         ) {
47             //https://gist.github.com/cweiske/2400389
48             // clone URL: https://gist.github.com/2400389.git
49             $parts = explode('/', ltrim($arUrl['path'], '/'));
50             if (count($parts == 2)) {
51                 //we only want the number, not the user name
52                 $path = $parts[1];
53             } else {
54                 $path = ltrim($arUrl['path'], '/');
55             }
56             $title = $this->getHtmlTitle($url);
57             if ($title === null) {
58                 $this->arGitUrls[][] = 'https://gist.github.com/'
59                     . $path . '.git';
60             } else {
61                 $this->arGitUrls[$title][] = 'https://gist.github.com/'
62                     . $path . '.git';
63             }
64             return true;
65         }
66
67         switch ($scheme) {
68         case 'git':
69             //clearly a git url
70             $this->arGitUrls = array(array($url));
71             return true;
72
73         case 'ssh':
74             //FIXME: maybe loosen this when we know how to skip the
75             //"do you trust this server" question of ssh
76             $this->error = 'ssh:// URLs are not supported';
77             return false;
78
79         case 'http':
80         case 'https':
81             return $this->extractUrlsFromHtml($url, $html);
82         }
83
84         $this->error = 'Unknown URLs scheme: ' . $scheme;
85         return false;
86     }
87
88     protected function extractUrlsFromHtml($url, $html = null)
89     {
90         //HTML is not necessarily well-formed, and Gitorious has many problems
91         // in this regard
92         //$sx = simplexml_load_file($url);
93
94         libxml_use_internal_errors(true);
95         $domDoc = new \DOMDocument();
96         if ($html === null) {
97             $domDoc->loadHTMLFile($url);
98         } else {
99             $domDoc->loadHTML($html);
100         }
101         $sx = simplexml_import_dom($domDoc);
102         //FIXME: handle network error
103
104         $elems = $sx->xpath('//*[@rel="vcs-git"]');
105         $titles = $sx->xpath('/html/head/title');
106         $pageTitle = $this->cleanPageTitle((string) reset($titles));
107
108         $count = $anonymous = 0;
109         foreach ($elems as $elem) {
110             if (!isset($elem['href'])) {
111                 continue;
112             }
113             $str = (string)$elem;
114             if (isset($elem['title'])) {
115                 //<link href=".." rel="vcs-git" title="title" />
116                 $title = (string)$elem['title'];
117             } else if ($str != '') {
118                 //<a href=".." rel="vcs-git">title</a>
119                 $title = $str;
120             } else if ($pageTitle != '') {
121                 $title = $pageTitle;
122             } else {
123                 $title = 'Unnamed repository #' . ++$anonymous;
124             }
125             $url = (string)$elem['href'];
126             if ($this->isSupported($url)) {
127                 ++$count;
128                 $this->arGitUrls[$title][] = $url;
129             }
130         }
131
132         if ($count > 0) {
133             return true;
134         }
135
136         $this->error = 'No git:// clone URL found';
137         return false;
138     }
139
140     public function getGitUrls()
141     {
142         return $this->arGitUrls;
143     }
144
145     /**
146      * Remove application names from HTML page titles
147      *
148      * @param string $title HTML page title
149      *
150      * @return string Cleaned HTML page title
151      */
152     protected function cleanPageTitle($title)
153     {
154         $title = trim($title);
155         if (substr($title, -9) == '- phorkie') {
156             $title = trim(substr($title, 0, -9));
157         }
158
159         return $title;
160     }
161
162     public function isSupported($url)
163     {
164         $scheme = parse_url($url, PHP_URL_SCHEME);
165         return $scheme == 'git'
166             || $scheme == 'http' || $scheme == 'https';
167     }
168
169     /**
170      * Extract the title from a HTML URL
171      *
172      * @param string $url URL to a HTML page
173      *
174      * @return string|null NULL on error, title otherwise
175      */
176     public function getHtmlTitle($url)
177     {
178         libxml_use_internal_errors(true);
179         //allow loading URLs in DOMDocument
180         libxml_disable_entity_loader(false);
181         $doc = \DOMDocument::loadHTMLFile($url);
182         if ($doc === false) {
183             return null;
184         }
185         $sx = simplexml_import_dom($doc);
186         $title = (string) $sx->head->title;
187         if ($title == '') {
188             return null;
189         }
190         return $title;
191     }
192 }
193 ?>