PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / services / RequestIgnoreNormalizer.php

RequestIgnoreNormalizer.php in 404 Solution trunk, at includes/services/RequestIgnoreNormalizer.php

203 lines 9.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Normalizes frontend ignore state and the WordPress normal post query fallback.
9 */
10 class ABJ_404_Solution_RequestIgnoreNormalizer {
11
12 /** @var mixed Object exposing getOptions(). */
13 private $optionsProvider;
14
15 /** @var ABJ_404_Solution_Functions */
16 private $f;
17
18 /** @var ABJ_404_Solution_Logging */
19 private $logger;
20
21 /** @var ABJ_404_Solution_RedirectsRepositoryInterface */
22 private $redirectsRepo;
23
24 /** @var ABJ_404_Solution_LogsRepositoryInterface */
25 private $logsRepo;
26
27 /** @var ABJ_404_Solution_NotFoundResponseService */
28 private $notFoundResponse;
29
30 /**
31 * @param ABJ_404_Solution_RequestIgnoreNormalizerDependencies|null $deps
32 */
33 function __construct(?ABJ_404_Solution_RequestIgnoreNormalizerDependencies $deps = null) {
34 $deps = $deps ?? new ABJ_404_Solution_RequestIgnoreNormalizerDependencies();
35 $this->optionsProvider = $deps->optionsProvider !== null ? $deps->optionsProvider : abj_service('options_repository');
36 $this->f = $deps->functions !== null ? $deps->functions : abj_service('functions');
37 $this->logger = $deps->logging !== null ? $deps->logging : abj_service('logging');
38 $this->redirectsRepo = $deps->redirectsRepo !== null ? $deps->redirectsRepo : abj_service('redirects_repository');
39 $this->logsRepo = $deps->logsRepo !== null ? $deps->logsRepo : abj_service('logs_repository');
40 $this->notFoundResponse = $deps->notFoundResponse !== null ? $deps->notFoundResponse : abj_service('not_found_response');
41 }
42
43 /**
44 * @param string $urlRequest the requested URL
45 * @param string $urlSlugOnly only the slug
46 * @return void
47 */
48 function initializeIgnoreValues(string $urlRequest, string $urlSlugOnly): void {
49 $options = $this->getOptions();
50 $httpUserAgent = array_key_exists('HTTP_USER_AGENT', $_SERVER) && is_scalar($_SERVER['HTTP_USER_AGENT']) ?
51 $this->f->strtolower((string)$_SERVER['HTTP_USER_AGENT']) : '';
52
53 $ignoreReasonDoNotProcess = $this->getAdminIgnoreReason($urlRequest);
54 $ignoreReasonDoNotProcess = $this->getUserAgentDoNotProcessReason(
55 $options,
56 $httpUserAgent,
57 $urlRequest,
58 $ignoreReasonDoNotProcess
59 );
60 $ignoreReasonDoNotProcess = $this->getFolderIgnoreReason($options, $urlSlugOnly, $ignoreReasonDoNotProcess);
61 abj_service('request_context')->ignore_donotprocess = is_string($ignoreReasonDoNotProcess) ? $ignoreReasonDoNotProcess : false;
62
63 $ignoreReasonDoProcess = $this->getUserAgentDoProcessReason($options, $httpUserAgent, $urlRequest);
64 abj_service('request_context')->ignore_doprocess = is_string($ignoreReasonDoProcess) ? $ignoreReasonDoProcess : false;
65 }
66
67 /**
68 * Forward to a real page for queries like ?p=10.
69 *
70 * @param array<string, mixed> $options
71 * @return void
72 */
73 function tryNormalPostQuery(array $options): void {
74 global $wp_query;
75
76 $query = is_object($wp_query) && isset($wp_query->query) && is_array($wp_query->query) ? $wp_query->query : array();
77 if (!isset($query['p'])) {
78 return;
79 }
80 $pageidRaw = $query['p'];
81 if (!is_scalar($pageidRaw)) {
82 return;
83 }
84 $pageid = (int)$pageidRaw;
85 if (!empty($pageid)) {
86 $rawPermalink = get_permalink($pageid);
87 $permalink = abj_service('sanitizer')->normalizeUrlString($rawPermalink !== false ? $rawPermalink : null);
88 $status = get_post_status($pageid);
89 if (($permalink != false) &&
90 (in_array($status, array('publish', 'published')))) {
91 $homeURL = get_home_url();
92 if ($homeURL == null) {
93 $homeURL = '';
94 }
95 $urlHomeDirectory = parse_url($homeURL, PHP_URL_PATH);
96 if ($urlHomeDirectory == null) {
97 $urlHomeDirectory = '';
98 }
99 $urlHomeDirectory = rtrim($urlHomeDirectory, '/');
100 $fromURL = $urlHomeDirectory . '/?p=' . $pageid;
101 $redirect = $this->redirectsRepo->getExistingRedirectForURL($fromURL);
102 $defaultRedirect = is_scalar($options['default_redirect'] ?? null) ? (string)$options['default_redirect'] : '301';
103 if (!isset($redirect['id']) || $redirect['id'] == 0) {
104 $this->redirectsRepo->setupRedirect(ABJ_404_Solution_RedirectSpec::create(
105 $fromURL, (string)ABJ404_STATUS_AUTO, (string)ABJ404_TYPE_POST,
106 (string)$pageid, $defaultRedirect, 0, 'page ID'
107 ));
108 }
109 $this->logsRepo->logRedirectHit(ABJ_404_Solution_RedirectHitLogEntry::create($fromURL, $permalink, 'page ID'));
110 $this->notFoundResponse->forceRedirect($permalink, (int)$defaultRedirect);
111 exit;
112 }
113 }
114 }
115
116 /** @return array<string, mixed> */
117 private function getOptions(): array {
118 if (is_object($this->optionsProvider) && method_exists($this->optionsProvider, 'getOptions')) {
119 $options = $this->optionsProvider->getOptions();
120 return is_array($options) ? $options : array();
121 }
122 return array();
123 }
124
125 /** @return string|null */
126 private function getAdminIgnoreReason(string $urlRequest) {
127 $adminURLRaw = parse_url(admin_url(), PHP_URL_PATH);
128 $adminURL = is_string($adminURLRaw) ? $adminURLRaw : '/wp-admin/';
129 if (is_admin() || $this->f->substr($urlRequest, 0, $this->f->strlen($adminURL)) == $adminURL) {
130 $this->logger->debugMessage("Ignoring admin URL: " . $urlRequest);
131 return 'Admin URL';
132 }
133 return null;
134 }
135
136 /**
137 * @param array<string, mixed> $options
138 * @param string|null $currentReason
139 * @return string|null
140 */
141 private function getUserAgentDoNotProcessReason(array $options, string $httpUserAgent, string $urlRequest, $currentReason) {
142 $ignoreDontProcess = is_string($options['ignore_dontprocess'] ?? null) ? $options['ignore_dontprocess'] : '';
143 foreach ($this->f->explodeNewline($ignoreDontProcess) as $agentToIgnore) {
144 if (stripos($httpUserAgent, trim($agentToIgnore)) !== false) {
145 $ua = $this->currentUserAgent();
146 $this->logger->debugMessage("Ignoring user agent (do not redirect): " .
147 esc_html($ua) . " for URL: " . esc_html($urlRequest));
148 return 'User agent (do not redirect): ' . esc_html($ua);
149 }
150 }
151 return $currentReason;
152 }
153
154 /**
155 * @param array<string, mixed> $options
156 * @param string|null $currentReason
157 * @return string|null
158 */
159 private function getFolderIgnoreReason(array $options, string $urlSlugOnly, $currentReason) {
160 $patternsToIgnore = is_array($options['folders_files_ignore_usable'] ?? null)
161 ? $options['folders_files_ignore_usable'] : array();
162 foreach ($patternsToIgnore as $patternToIgnore) {
163 if (!is_scalar($patternToIgnore)) {
164 continue;
165 }
166 $patternToIgnoreNoSlashes = stripslashes((string)$patternToIgnore);
167 abj_service('request_context')->debug_info = 'Applying regex pattern to ignore\"' .
168 $patternToIgnoreNoSlashes . '" to URL slug: ' . $urlSlugOnly;
169 $matches = array();
170 if ($this->f->regexMatch($patternToIgnoreNoSlashes, $urlSlugOnly, $matches)) {
171 $this->logger->debugMessage("Ignoring file/folder (do not redirect) for URL: " .
172 esc_html($urlSlugOnly) . ", pattern used: " . $patternToIgnoreNoSlashes);
173 $currentReason = 'Files and folders (do not redirect) pattern: ' .
174 esc_html($patternToIgnoreNoSlashes);
175 }
176 abj_service('request_context')->debug_info = 'Cleared after regex pattern to ignore.';
177 }
178 return $currentReason;
179 }
180
181 /**
182 * @param array<string, mixed> $options
183 * @return string|null
184 */
185 private function getUserAgentDoProcessReason(array $options, string $httpUserAgent, string $urlRequest) {
186 $ignoreDoProcess = is_string($options['ignore_doprocess'] ?? null) ? $options['ignore_doprocess'] : '';
187 foreach ($this->f->explodeNewline($ignoreDoProcess) as $agentToIgnore) {
188 if (stripos($httpUserAgent, trim($agentToIgnore)) !== false) {
189 $ua = $this->currentUserAgent();
190 $this->logger->debugMessage("Ignoring user agent (process ok): " .
191 esc_html($ua) . " for URL: " . esc_html($urlRequest));
192 return 'User agent (process ok): ' . $agentToIgnore;
193 }
194 }
195 return null;
196 }
197
198 private function currentUserAgent(): string {
199 return array_key_exists('HTTP_USER_AGENT', $_SERVER) && is_scalar($_SERVER['HTTP_USER_AGENT'])
200 ? (string)$_SERVER['HTTP_USER_AGENT'] : '';
201 }
202 }
203