private function updateGitHubInfo(RemoteFilesystem $rfs, Package $package, $owner, $repo, VcsRepository $repository) { $baseApiUrl = 'https://api.github.com/repos/' . $owner . '/' . $repo; $driver = $repository->getDriver(); if (!$driver instanceof GitHubDriver) { return; } $repoData = $driver->getRepoData(); try { $opts = ['http' => ['header' => ['Accept: application/vnd.github.v3.html']]]; $readme = $rfs->getContents('github.com', $baseApiUrl . '/readme', false, $opts); } catch (\Exception $e) { if (!$e instanceof \Composer\Downloader\TransportException || $e->getCode() !== 404) { return; } // 404s just mean no readme present so we proceed with the rest } if (!empty($readme)) { $elements = array('p', 'br', 'small', 'strong', 'b', 'em', 'i', 'strike', 'sub', 'sup', 'ins', 'del', 'ol', 'ul', 'li', 'h1', 'h2', 'h3', 'dl', 'dd', 'dt', 'pre', 'code', 'samp', 'kbd', 'q', 'blockquote', 'abbr', 'cite', 'table', 'thead', 'tbody', 'th', 'tr', 'td', 'a[href|target|rel|id]', 'img[src|title|alt|width|height|style]'); $config = \HTMLPurifier_Config::createDefault(); $config->set('HTML.Allowed', implode(',', $elements)); $config->set('Attr.EnableID', true); $config->set('Attr.AllowedFrameTargets', ['_blank']); $purifier = new \HTMLPurifier($config); $readme = $purifier->purify($readme); $dom = new \DOMDocument(); $dom->loadHTML('<?xml encoding="UTF-8">' . $readme); // Links can not be trusted, mark them nofollow and convert relative to absolute links $links = $dom->getElementsByTagName('a'); foreach ($links as $link) { $link->setAttribute('rel', 'nofollow noopener external'); if ('#' === substr($link->getAttribute('href'), 0, 1)) { $link->setAttribute('href', '#user-content-' . substr($link->getAttribute('href'), 1)); } elseif ('mailto:' === substr($link->getAttribute('href'), 0, 7)) { // do nothing } elseif (false === strpos($link->getAttribute('href'), '//')) { $link->setAttribute('href', 'https://github.com/' . $owner . '/' . $repo . '/blob/HEAD/' . $link->getAttribute('href')); } } // convert relative to absolute images $images = $dom->getElementsByTagName('img'); foreach ($images as $img) { if (false === strpos($img->getAttribute('src'), '//')) { $img->setAttribute('src', 'https://raw.github.com/' . $owner . '/' . $repo . '/HEAD/' . $img->getAttribute('src')); } } // remove first title as it's usually the project name which we don't need if ($dom->getElementsByTagName('h1')->length) { $first = $dom->getElementsByTagName('h1')->item(0); $first->parentNode->removeChild($first); } elseif ($dom->getElementsByTagName('h2')->length) { $first = $dom->getElementsByTagName('h2')->item(0); $first->parentNode->removeChild($first); } $readme = $dom->saveHTML(); $readme = substr($readme, strpos($readme, '<body>') + 6); $readme = substr($readme, 0, strrpos($readme, '</body>')); $package->setReadme($readme); } if (!empty($repoData['language'])) { $package->setLanguage($repoData['language']); } if (isset($repoData['stargazers_count'])) { $package->setGitHubStars($repoData['stargazers_count']); } if (isset($repoData['subscribers_count'])) { $package->setGitHubWatches($repoData['subscribers_count']); } if (isset($repoData['network_count'])) { $package->setGitHubForks($repoData['network_count']); } if (isset($repoData['open_issues_count'])) { $package->setGitHubOpenIssues($repoData['open_issues_count']); } }
private function updateGitHubInfo(RemoteFilesystem $rfs, Package $package, $owner, $repo) { $baseApiUrl = 'https://api.github.com/repos/' . $owner . '/' . $repo; try { $repoData = JsonFile::parseJson($rfs->getContents('github.com', $baseApiUrl, false), $baseApiUrl); } catch (\Exception $e) { return; } try { $opts = ['http' => ['header' => ['Accept: application/vnd.github.v3.html']]]; $readme = $rfs->getContents('github.com', $baseApiUrl . '/readme', false, $opts); } catch (\Exception $e) { if (!$e instanceof \Composer\Downloader\TransportException || $e->getCode() !== 404) { return; } // 404s just mean no readme present so we proceed with the rest } if (!empty($readme)) { $config = \HTMLPurifier_Config::createDefault(); $config->set('HTML.Allowed', 'a[href|target|rel|id],strong,b,em,i,strike,pre,code,p,ol,ul,li,br,h1,h2,h3,img[src|title|alt|width|height|style]'); $config->set('Attr.EnableID', true); $config->set('Attr.AllowedFrameTargets', ['_blank']); $purifier = new \HTMLPurifier($config); $readme = $purifier->purify($readme); $dom = new \DOMDocument(); $dom->loadHTML('<?xml encoding="UTF-8">' . $readme); // Links can not be trusted $links = $dom->getElementsByTagName('a'); foreach ($links as $link) { $link->setAttribute('rel', 'nofollow'); if ('#' === substr($link->getAttribute('href'), 0, 1)) { $link->setAttribute('href', '#user-content-' . substr($link->getAttribute('href'), 1)); } elseif (false === strpos($link->getAttribute('href'), '//')) { $link->setAttribute('href', 'https://github.com/' . $owner . '/' . $repo . '/blob/HEAD/' . $link->getAttribute('href')); } } // remove first title as it's usually the project name which we don't need if ($dom->getElementsByTagName('h1')->length) { $first = $dom->getElementsByTagName('h1')->item(0); $first->parentNode->removeChild($first); } elseif ($dom->getElementsByTagName('h2')->length) { $first = $dom->getElementsByTagName('h2')->item(0); $first->parentNode->removeChild($first); } $readme = $dom->saveHTML(); $readme = substr($readme, strpos($readme, '<body>') + 6); $readme = substr($readme, 0, strrpos($readme, '</body>')); $package->setReadme($readme); } if (!empty($repoData['language'])) { $package->setLanguage($repoData['language']); } if (!empty($repoData['stargazers_count'])) { $package->setGitHubStars($repoData['stargazers_count']); } if (!empty($repoData['subscribers_count'])) { $package->setGitHubWatches($repoData['subscribers_count']); } if (!empty($repoData['network_count'])) { $package->setGitHubForks($repoData['network_count']); } if (!empty($repoData['open_issues_count'])) { $package->setGitHubOpenIssues($repoData['open_issues_count']); } }