zend-blog-3-backend
170 строк · 4.2 Кб
1<?php
2/**
3* Created by PhpStorm.
4* User: morontt
5* Date: 20.04.18
6* Time: 1:41
7*/
8
9namespace App\Utils;
10
11class ExternalLinkProcessor
12{
13/**
14* @var array
15*/
16protected $hrefs = [];
17
18/**
19* @var array
20*/
21protected $replaces = [];
22
23/**
24* @var array
25*/
26protected $internalHosts;
27
28/**
29* @param array $hosts
30*/
31public function __construct(array $hosts = [])
32{
33$this->internalHosts = $hosts;
34}
35
36/**
37* @param string|null $content
38*
39* @return string|null
40*/
41public function upgradeLinks(string $content = null): ?string
42{
43if ($content === null) {
44return null;
45}
46
47$this->hrefs = [];
48$this->replaces = [];
49
50$oldHash = sha1($content);
51
52$content0 = $content;
53$fuse = 0;
54do {
55$r = $this->externalLinksProcessing($content0);
56$fuse++;
57} while ($r && $fuse < 200);
58
59if (count($this->hrefs)) {
60$content1 = $content;
61$fuse = 0;
62do {
63$r = $this->linksAndAttributeProcessing($content1);
64$fuse++;
65} while ($r && $fuse < 200);
66
67foreach ($this->replaces as $replacePair) {
68$content = str_replace($replacePair['old'], $replacePair['new'], $content);
69}
70
71return ($oldHash === sha1($content)) ? null : $content;
72}
73
74return null;
75}
76
77/**
78* @param string $text
79*
80* @return bool
81*/
82protected function externalLinksProcessing(&$text): bool
83{
84$result = false;
85$matches = [];
86
87$pattern = '/href="(?P<url>https?:\/\/(?P<host>[^\/]+)(\/?[^"]*))"/';
88
89if (preg_match($pattern, $text, $matches)) {
90$result = true;
91
92if (!in_array($matches['host'], $this->internalHosts, true)) {
93$this->hrefs[] = ['url' => $matches['url'], 'host' => $matches['host']];
94}
95
96$text = str_replace('href="' . $matches['url'] . '"', '', $text);
97}
98
99return $result;
100}
101
102/**
103* @param string $text
104*
105* @return bool
106*/
107protected function linksAndAttributeProcessing(&$text): bool
108{
109$result = false;
110$matches = [];
111
112$pattern = '/<a(?:\s+(?:[^>]+))>/';
113if (preg_match($pattern, $text, $matches)) {
114$result = true;
115
116$this->checkAttributes($matches[0]);
117
118$text = str_replace($matches[0], '', $text);
119}
120
121return $result;
122}
123
124/**
125* @param string $link
126*/
127protected function checkAttributes(string $link): void
128{
129foreach ($this->hrefs as $externalLink) {
130if (strpos($link, $externalLink['url']) !== false) {
131$attributes = [];
132
133$linkItem = str_replace('>', '/>', $link);
134try {
135$xml = simplexml_load_string($linkItem);
136foreach ($xml->attributes() as $k => $v) {
137$attributes[] = sprintf('%s="%s"', $k, (string)$v);
138}
139} catch (\ErrorException $e) {
140break;
141}
142
143$findRel = false;
144$newAttributes = [];
145foreach ($attributes as $attribute) {
146$matches = [];
147if (preg_match('/rel="([^"]+)"/', $attribute, $matches)) {
148$findRel = true;
149if (strpos($matches[1], 'nofollow') === false) {
150$newAttributes[] = sprintf('rel="%s nofollow"', $matches[1]);
151} else {
152$newAttributes[] = $attribute;
153}
154} else {
155$newAttributes[] = $attribute;
156}
157}
158if (!$findRel) {
159$newAttributes[] = 'rel="nofollow"';
160}
161
162$this->replaces[] = [
163'old' => $link,
164'new' => sprintf('<a %s>', implode(' ', $newAttributes)),
165];
166break;
167}
168}
169}
170}
171