PluginProbe
404 Solution / 4.2.0
404 Solution v4.2.0
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 / View.php

View.php in 404 Solution 4.2.0, at includes/View.php

154 lines 5.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3
4 if (!defined('ABSPATH')) {
5 exit;
6 }
7
8 /* Turns data into an html display and vice versa.
9 * Houses all displayed pages. Logs, options page, captured 404s, stats, etc. */
10
11 require_once __DIR__ . '/ViewTrait_Shared.php';
12 require_once __DIR__ . '/ViewTrait_UI.php';
13 require_once __DIR__ . '/ViewTrait_Stats.php';
14 require_once __DIR__ . '/ViewTrait_Settings.php';
15 require_once __DIR__ . '/ViewTrait_Redirects.php';
16 require_once __DIR__ . '/ViewTrait_RedirectsTable.php';
17 require_once __DIR__ . '/ViewTrait_RedirectTypeUI.php';
18 require_once __DIR__ . '/ViewTrait_RedirectConditions.php';
19 require_once __DIR__ . '/ViewTrait_Logs.php';
20
21 class ABJ_404_Solution_View {
22
23 use ViewTrait_Shared,
24 ViewTrait_UI,
25 ViewTrait_Stats,
26 ViewTrait_Settings,
27 ViewTrait_Redirects,
28 ViewTrait_RedirectsTable,
29 ViewTrait_RedirectTypeUI,
30 ViewTrait_RedirectConditions,
31 ViewTrait_Logs;
32
33 /** @var self|null */
34 private static $instance = null;
35
36 /** @var ABJ_404_Solution_Functions */
37 private $f;
38
39 /** @var ABJ_404_Solution_PluginLogic */
40 private $logic;
41
42 /** @var ABJ_404_Solution_Logging */
43 private $logger;
44
45 /** @var ABJ_404_Solution_ViewReadServiceInterface */
46 private $viewReadService;
47
48 /** @var ABJ_404_Solution_ViewBuildOrchestratorInterface */
49 private $viewBuildOrchestrator;
50
51 /** @var ABJ_404_Solution_LogsRepositoryInterface */
52 private $logsRepository;
53
54 /** @var ABJ_404_Solution_RedirectsRepositoryInterface */
55 private $redirectsRepository;
56
57 /** @var ABJ_404_Solution_ContentRepositoryInterface */
58 private $contentRepository;
59
60 /** @var ABJ_404_Solution_StatsRepositoryInterface */
61 private $statsRepository;
62
63 /** @var array<string,string> Latest table data signatures by subpage. */
64 private $tableDataSignatures = array();
65
66 /**
67 * Constructor with dependency injection.
68 * Dependencies are now explicit and visible.
69 *
70 * @param ABJ_404_Solution_Functions|null $functions String manipulation utilities
71 * @param ABJ_404_Solution_PluginLogic|null $pluginLogic Business logic service
72 * @param mixed $dataAccessOrViewReadService Data access (legacy) or ViewReadService
73 * @param ABJ_404_Solution_Logging|null $logging Logging service
74 */
75 public function __construct($functions = null, $pluginLogic = null, $dataAccessOrViewReadService = null, $logging = null) {
76 // Use injected dependencies or fall back to service container
77 $this->f = $functions !== null ? $functions : abj_service('functions');
78 $this->logic = $pluginLogic !== null ? $pluginLogic : abj_service('plugin_logic');
79 $this->logger = $logging !== null ? $logging : abj_service('logging');
80
81 // When a DataAccess facade is injected (legacy callers / tests),
82 // use it directly for all module interfaces -- it delegates to the
83 // real modules internally. This keeps existing tests working while
84 // production code migrates to direct module calls.
85 $dao = $dataAccessOrViewReadService;
86 // A ViewReadService also exposes getRedirectsForView() but is NOT a
87 // legacy DataAccess facade: it implements only ViewReadServiceInterface,
88 // not the other five repository interfaces. The modern bootstrap.php
89 // wiring passes a ViewReadService here and must take the else branch
90 // so each repository field gets the correct dedicated service.
91 // (Regression caught by ViewServiceBindingTest after the General
92 // Settings admin tab crashed with "undefined method
93 // ABJ_404_Solution_ViewReadService::getEarliestLogTimestamp()" on
94 // 2026-05-23.)
95 $isLegacyDao = ($dao !== null && is_object($dao)
96 && !($dao instanceof ABJ_404_Solution_ViewReadServiceInterface)
97 && (method_exists($dao, 'getRedirectsForView') || $dao instanceof ABJ_404_Solution_DataAccess));
98
99 if ($isLegacyDao) {
100 // DataAccess facade implements all module interfaces via delegation.
101 // Tests may also pass a Mockery mock that handles all methods.
102 /** @var ABJ_404_Solution_ViewReadServiceInterface&ABJ_404_Solution_ViewBuildOrchestratorInterface&ABJ_404_Solution_LogsRepositoryInterface&ABJ_404_Solution_RedirectsRepositoryInterface&ABJ_404_Solution_ContentRepositoryInterface&ABJ_404_Solution_StatsRepositoryInterface $dao */
103 $this->viewReadService = $dao;
104 $this->viewBuildOrchestrator = $dao;
105 $this->logsRepository = $dao;
106 $this->redirectsRepository = $dao;
107 $this->contentRepository = $dao;
108 $this->statsRepository = $dao;
109 } else {
110 // Resolve module dependencies from container. Container returns
111 // DataAccess::getInstance() as a facade for all module services.
112 /** @var ABJ_404_Solution_ViewReadServiceInterface $vrs */
113 $vrs = abj_service('view_read_service');
114 $this->viewReadService = $vrs;
115 /** @var ABJ_404_Solution_ViewBuildOrchestratorInterface $vbo */
116 $vbo = abj_service('view_build_orchestrator');
117 $this->viewBuildOrchestrator = $vbo;
118 /** @var ABJ_404_Solution_LogsRepositoryInterface $lr */
119 $lr = abj_service('logs_repository');
120 $this->logsRepository = $lr;
121 /** @var ABJ_404_Solution_RedirectsRepositoryInterface $rr */
122 $rr = abj_service('redirects_repository');
123 $this->redirectsRepository = $rr;
124 /** @var ABJ_404_Solution_ContentRepositoryInterface $cr */
125 $cr = abj_service('content_repository');
126 $this->contentRepository = $cr;
127 /** @var ABJ_404_Solution_StatsRepositoryInterface $sr */
128 $sr = abj_service('stats_repository');
129 $this->statsRepository = $sr;
130 }
131 }
132
133 /** @return self */
134 public static function getInstance() {
135 if (self::$instance !== null) {
136 return self::$instance;
137 }
138
139 // If the DI container is initialized, prefer it.
140 if (class_exists('ABJ_404_Solution_ServiceContainer')) {
141 $resolved = ABJ_404_Solution_ServiceContainer::safeGet('view');
142 if ($resolved instanceof self) {
143 self::$instance = $resolved;
144 return self::$instance;
145 }
146 }
147
148 self::$instance = new ABJ_404_Solution_View();
149
150 return self::$instance;
151 }
152
153 }
154