aboutsummaryrefslogtreecommitdiffhomepage
path: root/Digitigrade/LinkPreview.php
blob: 4716cc88218616b304760daa76f72a021411c5df (plain)
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
88
89
90
91
<?php
namespace Digitigrade;

class LinkPreview implements \JsonSerializable {
    public string $url;
    public string $siteName;
    public string $title;
    public string $description;
    public string $style;
    public ?string $imageUrl = null;

    /**
     * Generates a link preview for the given URL.
     * @param string $url
     * @return LinkPreview|null the link preview, or null if none is available
     */
    public static function generate(string $url): ?self {
        $p = new self();
        $p->url = $url;
        return $p->update() ? $p : null;
    }

    /**
     * Updates the information for this link preview.
     * @return bool true if successful
     */
    public function update(): bool {
        if (!(str_starts_with($this->url, "https://") || str_starts_with($this->url, "http://"))) {
            return false;
        }

        $d = new \DOMDocument();
        $c = @file_get_contents($this->url);
        if (!$c)
            return false;
        @$d->loadHTML($c);

        $url = $this->url;
        $siteName = hostname_from_uri($this->url);
        $style = 'text';
        $title = $d->getElementsByTagName('title')->item(0)?->textContent;
        $metas = $d->getElementsByTagName('meta');
        foreach ($metas as $tag) {
            if ($tag->attributes->getNamedItem('name')?->textContent == 'description') {
                // <meta name="description" content="...">
                $description = $tag->attributes->getNamedItem('content')?->textContent;
            } elseif ($prop = $tag->attributes->getNamedItem('property')) {
                // <meta property="og:..." content="...">
                $content = $tag->attributes->getNamedItem('content')?->textContent;
                match ($prop->textContent) {
                    'og:title' => $title = $content,
                    'og:description' => $description = $content,
                    'og:url' => $url = $content,
                    'og:site_name' => $siteName = $content,
                    'og:image' => $imageUrl = $content,
                    default => null
                };

            } elseif ($tag->attributes->getNamedItem('name')?->textContent == 'twitter:card'
                && $tag->attributes->getNamedItem("content")?->textContent == 'summary_large_image') {
                // <meta name="twitter:card" content="summary_large_image">
                $style = 'largeImage';
            }
        }
        // turn text into smallImage if an image is present
        $style = ($style == 'text' && isset($imageUrl)) ? 'smallImage' : $style;

        if (!isset($title, $description))
            return false;

        $this->url = $url;
        $this->siteName = $siteName;
        $this->title = $title;
        $this->description = $description;
        $this->style = $style;
        $this->imageUrl = $imageUrl ?? null;

        return true;
    }

    public function jsonSerialize(): array {
        return [
            'url' => $this->url,
            'siteName' => $this->siteName,
            'title' => $this->title,
            'description' => $this->description,
            'style' => $this->style,
            'imageUrl' => $this->imageUrl
        ];
    }
}