Consumer   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Test Coverage

Coverage 93.48%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 44
dl 0
loc 148
ccs 43
cts 46
cp 0.9348
rs 10
c 1
b 0
f 0
wmc 11

9 Methods

Rating   Name   Duplication   Size   Complexity  
A newCrawler() 0 5 1
A fallbackForUrl() 0 8 1
A fallbackForTitle() 0 9 1
A newProperties() 0 5 1
A newProperty() 0 5 2
A newObject() 0 5 1
A fallback() 0 6 1
A loadHtml() 0 16 2
A fallbackForDescription() 0 8 1
1
<?php
2
3
namespace Mpyw\OpenGraph;
4
5
use DOMElement;
6
use Mpyw\OpenGraph\Objects\ObjectBase;
7
use Mpyw\OpenGraph\Objects\Website;
8
use Symfony\Component\DomCrawler\Crawler;
9
10
/**
11
 * Consumer that extracts Open Graph data from either a URL or a HTML string.
12
 */
13
class Consumer
14
{
15
    use GenericHelper;
16
17
    /**
18
     * When enabled, crawler will read content of title and meta description if no
19
     * Open Graph data is provided by target page.
20
     *
21
     * @var bool
22
     */
23
    public $useFallbackMode = false;
24
25
    /**
26
     * When enabled, crawler will throw exceptions for some crawling errors like unexpected
27
     * Open Graph elements.
28
     *
29
     * @var bool
30
     */
31
    public $debug = false;
32
33
    /**
34
     * @param  string      $html        HTML string, usually whole content of crawled web resource.
35
     * @param  null|string $fallbackUrl URL to use when fallback mode is enabled.
36
     * @return ObjectBase
37
     */
38 17
    public function loadHtml(string $html, ?string $fallbackUrl = null)
39
    {
40
        // Note: Avoid Crawler::evaluate() throwing LogicException against empty HTML
41 17
        $crawler = $this->newCrawler($this->emptyStringAsNull(trim($html)) ?? '<!DOCTYPE html><html></html>');
42
43 17
        $properties = $this->newProperties($crawler);
44
45
        $object = $this
46 17
            ->newObject($crawler)
47 17
            ->assignProperties($properties, $this->debug);
48
49 14
        if ($this->useFallbackMode) {
50 2
            $this->fallback($object, $crawler, $fallbackUrl);
51
        }
52
53 14
        return $object;
54
    }
55
56
    /**
57
     * @param  Crawler    $crawler
58
     * @return Property[]
59
     */
60 17
    protected function newProperties(Crawler $crawler): array
61
    {
62 17
        return array_map(
63 17
            [$this, 'newProperty'],
64 17
            iterator_to_array($crawler->filterXPath('//meta[starts-with(@property, "og:") or starts-with(@name, "og:")]'), false)
65
        );
66
    }
67
68
    /**
69
     * @param  string  $content
70
     * @return Crawler
71
     */
72 17
    protected function newCrawler(string $content)
73
    {
74 17
        $crawler = new Crawler();
75 17
        $crawler->addHTMLContent($content);
76 17
        return $crawler;
77
    }
78
79
    /**
80
     * @param  DOMElement $tag
81
     * @return Property
82
     */
83 13
    protected function newProperty(DOMElement $tag)
84
    {
85 13
        $name = trim($tag->getAttribute('name') ?: $tag->getAttribute('property'));
86 13
        $value = trim($tag->getAttribute('content'));
87 13
        return new Property($name, $value);
88
    }
89
90
    /**
91
     * @param  Crawler    $crawler
92
     * @return ObjectBase
93
     */
94 17
    protected function newObject(Crawler $crawler)
95
    {
96 17
        switch ($crawler->evaluate('normalize-space(//meta[@property="og:type" or @name="og:type"]/@content)')[0] ?? null) {
97
            default:
98 17
                return new Website();
99
        }
100
    }
101
102
    /**
103
     * @param  ObjectBase  $object
104
     * @param  Crawler     $crawler
105
     * @param  null|string $fallbackUrl
106
     * @return $this
107
     */
108 2
    protected function fallback(ObjectBase $object, Crawler $crawler, ?string $fallbackUrl = null)
109
    {
110
        return $this
111 2
            ->fallbackForUrl($object, $crawler, $fallbackUrl)
112 2
            ->fallbackForTitle($object, $crawler)
113 2
            ->fallbackForDescription($object, $crawler);
114
    }
115
116
    /**
117
     * @param  ObjectBase  $object
118
     * @param  Crawler     $crawler
119
     * @param  null|string $fallbackUrl
120
     * @return $this
121
     */
122 2
    protected function fallbackForUrl(ObjectBase $object, Crawler $crawler, ?string $fallbackUrl = null)
123
    {
124 2
        $object->url = $object->url
125 2
            ?? $this->emptyStringAsNull($crawler->evaluate('normalize-space(//link[@rel="canonical"]/@href)')[0])
126 1
            ?? $this->emptyStringAsNull($fallbackUrl)
127 2
            ?? $object->url;
128
129 2
        return $this;
130
    }
131
132
    /**
133
     * @param  ObjectBase $object
134
     * @param  Crawler    $crawler
135
     * @return $this
136
     */
137 2
    protected function fallbackForTitle(ObjectBase $object, Crawler $crawler)
138
    {
139 2
        $object->title = $object->title
140 2
            ?? $this->emptyStringAsNull($crawler->evaluate('normalize-space(//title)')[0])
141
            ?? $this->emptyStringAsNull($crawler->evaluate('normalize-space(//h1)')[0])
142
            ?? $this->emptyStringAsNull($crawler->evaluate('normalize-space(//h2)')[0])
143 2
            ?? $object->title;
144
145 2
        return $this;
146
    }
147
148
    /**
149
     * @param  ObjectBase $object
150
     * @param  Crawler    $crawler
151
     * @return $this
152
     */
153 2
    protected function fallbackForDescription(ObjectBase $object, Crawler $crawler)
154
    {
155 2
        $object->description = $object->description
156 2
            ?? $this->emptyStringAsNull($crawler->evaluate('normalize-space(//meta[@property="description" or @name="description"]/@content)')[0])
157
            ?? $this->emptyStringAsNull($crawler->evaluate('normalize-space(//p)')[0])
158 2
            ?? $object->description;
159
160 2
        return $this;
161
    }
162
}
163