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 / RedirectDestinationSuggestionService.php

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

139 lines 4.3 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 * Looks up the best destination suggestion for a captured 404 URL.
9 */
10 class ABJ_404_Solution_RedirectDestinationSuggestionService {
11
12 /** @var ABJ_404_Solution_Logging */
13 private $logger;
14
15 /**
16 * @param ABJ_404_Solution_Logging $logger
17 */
18 public function __construct($logger) {
19 $this->logger = $logger;
20 }
21
22 /**
23 * Get the best suggested destination for a captured URL using the spell-checker.
24 *
25 * @param string $url The captured 404 URL.
26 * @param array<string, mixed> $options Plugin options.
27 * @return array{title: string, score: int, id_and_type: string, type_label: string}|null
28 */
29 public function getSuggestedDestination(string $url, array $options): ?array {
30 try {
31 $match = $this->findTopMatch($url);
32 if ($match === null) {
33 return null;
34 }
35
36 $permalink = $this->resolvePermalink($match['id_and_type'], $match['score'], $match['row_type'], $options);
37 if ($permalink === null) {
38 return null;
39 }
40
41 return array(
42 'title' => $permalink['title'],
43 'score' => $match['score'],
44 'id_and_type' => $match['id_and_type'],
45 'type_label' => $this->buildTypeLabelForIdAndType($match['id_and_type']),
46 );
47 } catch (\Throwable $e) {
48 $this->logger->debugMessage(
49 'Unable to build redirect suggestion for captured URL: ' . $e->getMessage()
50 );
51 return null;
52 }
53 }
54
55 /**
56 * @return array{id_and_type: string, score: int, row_type: string}|null
57 */
58 private function findTopMatch(string $url): ?array {
59 $spellChecker = abj_service('spell_checker');
60 $permalinksPacket = $spellChecker->findMatchingPosts($url, '1', '1');
61 $permalinks = is_array($permalinksPacket[0] ?? null) ? $permalinksPacket[0] : array();
62 if (empty($permalinks)) {
63 return null;
64 }
65
66 $topIdAndType = array_key_first($permalinks);
67 if (!is_string($topIdAndType)) {
68 return null;
69 }
70
71 $topScoreRaw = $permalinks[$topIdAndType] ?? 0;
72 $topScore = is_scalar($topScoreRaw) ? intval($topScoreRaw) : 0;
73 if ($topScore < 25) {
74 return null;
75 }
76
77 return array(
78 'id_and_type' => $topIdAndType,
79 'score' => $topScore,
80 'row_type' => is_string($permalinksPacket[1] ?? '') ? (string)($permalinksPacket[1] ?? '') : '',
81 );
82 }
83
84 /**
85 * @param string $idAndType
86 * @param int $score
87 * @param string $rowType
88 * @param array<string, mixed> $options
89 * @return array{title: string}|null
90 */
91 private function resolvePermalink(string $idAndType, int $score, string $rowType, array $options): ?array {
92 $permalink = ABJ_404_Solution_PermalinkResolver::permalinkInfoToArray(
93 $idAndType,
94 $score,
95 $rowType,
96 $options
97 );
98
99 $title = is_string($permalink['title'] ?? '') ? (string)($permalink['title'] ?? '') : '';
100 if ($title === '' || ($permalink['status'] ?? '') === 'trash') {
101 return null;
102 }
103
104 return array('title' => $title);
105 }
106
107 /**
108 * @return string
109 */
110 private function buildTypeLabelForIdAndType(string $idAndType): string {
111 $typeParts = explode('|', $idAndType);
112 $typeInt = isset($typeParts[1]) && is_numeric($typeParts[1]) ? (int)$typeParts[1] : -1;
113 return $this->buildTypeLabel($typeInt, $typeParts);
114 }
115
116 /**
117 * @param int $typeInt
118 * @param array<int, string> $typeParts
119 * @return string
120 */
121 private function buildTypeLabel(int $typeInt, array $typeParts): string {
122 if ($typeInt === ABJ404_TYPE_POST) {
123 $postType = get_post_type((int)$typeParts[0]);
124 return ($postType === 'page') ? __('Page', '404-solution') : __('Post', '404-solution');
125 }
126 if ($typeInt === ABJ404_TYPE_CAT) {
127 return __('Category', '404-solution');
128 }
129 if ($typeInt === ABJ404_TYPE_TAG) {
130 return __('Tag', '404-solution');
131 }
132 if ($typeInt === ABJ404_TYPE_HOME) {
133 return __('Home', '404-solution');
134 }
135
136 return '';
137 }
138 }
139