generated from dunglas/symfony-docker
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathStackDetector.php
87 lines (70 loc) · 2.21 KB
/
StackDetector.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?php
namespace App\Util;
use App\Enum\Framework;
use Psr\Log\LoggerInterface;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
class StackDetector
{
public function __construct(
private readonly HttpClientInterface $httpClient,
private readonly LoggerInterface $logger,
) {
}
public function detectFramework(string $url): ?string
{
try {
$crawler = $this->getCrawler($url);
$documentNode = $crawler->filter("html");
$headNode = $crawler->filter("head");
$generatorNode = $headNode->filter("meta[name='generator']");
$generator = $generatorNode->count() ? strtolower($generatorNode->attr("content")) : null;
// Webflow
if ($documentNode->attr("data-wf-site")) {
return Framework::WEBFLOW;
}
// Wordpress
if (str_contains($generator, "wordpress")
// A single wp-* resource could be from an external source; 2 or more is most likely a Wordpress site though.
|| $headNode->children("[href*='wp-content'], [src*='wp-content'], [href*='wp-includes'], [src*='wp-includes']")->count() > 1) {
return Framework::WORDPRESS;
}
// Shopify
if ($headNode->children("[href*='cdn.shopify.com'], [src*='cdn.shopify.com']")->count() > 1) {
return Framework::SHOPIFY;
}
} catch (\Exception $exception) {
$this->logger->error($exception);
}
return null;
}
private function getCrawler(string $url): ?Crawler
{
static $cachedCrawlers = [];
$response = $this->fetch($url);
$contentType = implode("\n", $response?->getHeaders()["content-type"] ?? []);
if (!str_contains($contentType, "html")) {
return null;
}
$html = $response?->getContent();
$cachedCrawlers[$url] = $html ? new Crawler($html) : null;
return $cachedCrawlers[$url];
}
private function fetch(string $url): ?ResponseInterface
{
static $cachedResponses = [];
if (isset($cachedResponses[$url])) {
return $cachedResponses[$url];
}
try {
$response = $this->httpClient->request("GET", $url);
$response->getHeaders();
$response->getContent();
$cachedResponses[$url] = $response;
} catch (\Exception) {
$cachedResponses[$url] = null;
}
return $cachedResponses[$url];
}
}