Continued:
[core.git] / framework / main / classes / controller / class_BaseController.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Controller;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory;
7 use Org\Mxchange\CoreFramework\Filter\Filterable;
8 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
9 use Org\Mxchange\CoreFramework\Object\BaseFrameworkSystem;
10 use Org\Mxchange\CoreFramework\Registry\Object\ObjectRegistry;
11 use Org\Mxchange\CoreFramework\Registry\Registerable;
12 use Org\Mxchange\CoreFramework\Request\Requestable;
13 use Org\Mxchange\CoreFramework\Response\Responseable;
14 use Org\Mxchange\CoreFramework\Traits\Resolver\ResolverTrait;
15
16 // Import SPL stuff
17 use \BadMethodCallException;
18 use \InvalidArgumentException;
19
20 /**
21  * A generic controller class. You should extend this base class if you want to
22  * write your own controller. You get the advantage that you can use the pre and
23  * post filters.
24  *
25  * @author              Roland Haeder <webmaster@shipsimu.org>
26  * @version             0.0.0
27  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
28  * @license             GNU GPL 3.0 or any newer version
29  * @link                http://www.shipsimu.org
30  *
31  * This program is free software: you can redistribute it and/or modify
32  * it under the terms of the GNU General Public License as published by
33  * the Free Software Foundation, either version 3 of the License, or
34  * (at your option) any later version.
35  *
36  * This program is distributed in the hope that it will be useful,
37  * but WITHOUT ANY WARRANTY; without even the implied warranty of
38  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
39  * GNU General Public License for more details.
40  *
41  * You should have received a copy of the GNU General Public License
42  * along with this program. If not, see <http://www.gnu.org/licenses/>.
43  */
44 abstract class BaseController extends BaseFrameworkSystem implements Registerable {
45         // Load traits
46         use ResolverTrait;
47
48         // Exception constants
49         const EXCEPTION_FILTER_CHAIN_INVALID = 0xf10;
50
51         // Names of controller's own filter chains
52         const FILTER_CHAIN_PRE_COMMAND  = 'controller_pre_command';
53         const FILTER_CHAIN_POST_COMMAND = 'controller_post_command';
54         const FILTER_CHAIN_SHUTDOWN     = 'shutdown';
55
56         /**
57          * Generic filter chains
58          */
59         private $filterChains = [];
60
61         /**
62          * Protected constructor
63          *
64          * @param       $className      Name of the class
65          * @return      void
66          */
67         protected function __construct (string $className) {
68                 // Call parent constructor
69                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: className=%s - CONSTRUCTED!', $className));
70                 parent::__construct($className);
71
72                 // Initialize both filter chains
73                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage('BASE-CONTROLLER: Initializing filter chains ...');
74                 foreach([self::FILTER_CHAIN_PRE_COMMAND, self::FILTER_CHAIN_POST_COMMAND] as $filterChain) {
75                         // Init it
76                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: Invoking this->initFilterChain(=%s) ...', $filterChain));
77                         $this->initFilterChain($filterChain);
78                 }
79
80                 // Add this controller to the registry
81                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-CONTROLLER: Registering this=%s ...', $this->__toString()));
82                 ObjectRegistry::getRegistry('generic')->addInstance('controller', $this);
83
84                 // Trace message
85                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
86         }
87
88         /**
89          * Executes a command with pre and post filters
90          *
91          * @param       $requestInstance        A Requestable class
92          * @param       $responseInstance       A Responseable class
93          * @return      void
94          */
95         public function executeGenericPrePostCommand (Requestable $requestInstance, Responseable $responseInstance) {
96                 // Get the command instance from the resolver by sending a request instance to the resolver
97                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: requestInstance=%s,responseInstance=%s - CALLED!', $requestInstance->__toString(), $responseInstance->__toString()));
98                 $commandInstance = $this->getResolverInstance()->resolveCommandByRequest($requestInstance);
99
100                 // Add more filters by the command
101                 $commandInstance->addExtraFilters($this, $requestInstance);
102
103                 // Run the pre filters
104                 $this->executePreFilters($requestInstance, $responseInstance);
105
106                 // This request was valid! :-D
107                 $requestInstance->setIsRequestValid(TRUE);
108
109                 // Execute the command
110                 $commandInstance->execute($requestInstance, $responseInstance);
111
112                 // Run the post filters
113                 $this->executePostFilters($requestInstance, $responseInstance);
114
115                 // Flush the response out
116                 $responseInstance->flushBuffer();
117
118                 // Trace message
119                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
120         }
121
122         /**
123          * Handles the given request and response, redirects to login_failed if
124          * UserAuthorizationException is thrown.
125          *
126          * @param       $requestInstance        An instance of a Requestable class
127          * @param       $responseInstance       An instance of a Responseable class
128          * @return      void
129          */
130         public function genericHanleRequestLoginFailedRedirect (Requestable $requestInstance, Responseable $responseInstance) {
131                 // Get the "form action"
132                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: requestInstance=%s,responseInstance=%s - CALLED!', $requestInstance->__toString(), $responseInstance->__toString()));
133                 $formAction = $requestInstance->getRequestElement('form');
134
135                 // Get command instance from resolver
136                 $commandInstance = $this->getResolverInstance()->resolveCommand('Org\Mxchange\CoreFramework\Command\Failed', $formAction);
137
138                 // Add more filters by the command
139                 $commandInstance->addExtraFilters($this, $requestInstance);
140
141                 // Try to run the pre filters, if auth exceptions come through redirect here
142                 try {
143                         // Run the pre filters
144                         $this->executePreFilters($requestInstance, $responseInstance);
145                 } catch (UserAuthorizationException $e) {
146                         // Redirect to main page
147                         $responseInstance->redirectToConfiguredUrl('login_failed');
148
149                         // Exit here
150                         exit;
151                 }
152
153                 /*
154                  * Is the request still valid? Post filters shall only be executed of
155                  * the request is valid
156                  */
157                 if ($requestInstance->isRequestValid()) {
158                         // Execute the command
159                         $commandInstance->execute($requestInstance, $responseInstance);
160
161                         // Execute *very* generic post filters
162                         $this->executePostFilters($requestInstance, $responseInstance);
163                 }
164
165                 // Flush the buffer out
166                 $responseInstance->flushBuffer();
167
168                 // Trace message
169                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
170         }
171
172         /**
173          * Generic execute of the command: pre and post filters with redirect
174          * but request becomes valid after pre-filters run.
175          *
176          * @param       $requestInstance        An instance of a Requestable class
177          * @param       $responseInstance       An instance of a Responseable class
178          * @return      void
179          */
180         public function genericHanleRequestLoginAreaFailedRedirect (Requestable $requestInstance, Responseable $responseInstance) {
181                 // Get the command instance from the resolver by sending a request instance to the resolver
182                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: requestInstance=%s,responseInstance=%s - CALLED!', $requestInstance->__toString(), $responseInstance->__toString()));
183                 $commandInstance = $this->getResolverInstance()->resolveCommandByRequest($requestInstance);
184
185                 // Add more filters by the command
186                 $commandInstance->addExtraFilters($this, $requestInstance);
187
188                 // Try to run the pre filters, if auth exceptions come through redirect here
189                 try {
190                         // Run the pre filters
191                         $this->executePreFilters($requestInstance, $responseInstance);
192                 } catch (UserAuthorizationException $e) {
193                         // Redirect to main page
194                         $responseInstance->redirectToConfiguredUrl('login_failed');
195
196                         // Exit here
197                         exit;
198                 }
199
200                 // This request was valid! :-D
201                 $requestInstance->setIsRequestValid(TRUE);
202
203                 // Execute the command
204                 $commandInstance->execute($requestInstance, $responseInstance);
205
206                 // Run the post filters
207                 $this->executePostFilters($requestInstance, $responseInstance);
208
209                 // Flush the response out
210                 $responseInstance->flushBuffer();
211
212                 // Trace message
213                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
214         }
215
216         /**
217          * Private method to initialize a given filter chain
218          *
219          * @param       $filterChain    Name of the filter chain
220          * @return      void
221          * @throws      InvalidArgumentException        If a parameter has an invalid value
222          * @throws      BadMethodCallException  If the given filter chain is already initialized
223          */
224         protected function initFilterChain (string $filterChain) {
225                 // Check parameter
226                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: filterChain=%s - CALLED!', $filterChain));
227                 if (empty($filterChain)) {
228                         // Throw IAE
229                         throw new InvalidArgumentException('Parameter "filterChain" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
230                 } elseif (isset($this->filterChains[$filterChain])) {
231                         // Throw BMCE
232                         throw new BadMethodCallException(sprintf('filterChain=%s is already initialized', $filterChain), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
233                 }
234
235                 // Initialize filter chain
236                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-CONTROLLER: Initializing filterChain=%s ...', $filterChain));
237                 $this->filterChains[$filterChain] = ObjectFactory::createObjectByConfiguredName('filter_chain_class');
238
239                 // Trace message
240                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
241         }
242
243         /**
244          * Adds a filter to a given filter chain
245          *
246          * @param       $filterChain    Chain of the filter
247          * @param       $filterInstance         An instance of a filter
248          * @return      void
249          * @throws      InvalidArgumentException        If a parameter has an invalid value
250          * @throws      BadMethodCallException  If the given filter chain is not yet initialized
251          */
252         protected function addFilter (string $filterChain, Filterable $filterInstance) {
253                 // Check parameter
254                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: filterChain=%s,filterInstance=%s - CALLED!', $filterChain, $filterInstance->__toString()));
255                 if (empty($filterChain)) {
256                         // Throw IAE
257                         throw new InvalidArgumentException('Parameter "filterChain" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
258                 } elseif (!isset($this->filterChains[$filterChain])) {
259                         // Throw IAE
260                         throw new BadMethodCallException(sprintf('filterChain=%s is not a valid chain', $filterChain), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
261                 }
262
263                 // Add the filter
264                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-CONTROLLER: Adding filterInstance=%s to filterChain=%s ...', $filterInstance->__toString(), $filterChain));
265                 $this->filterChains[$filterChain]->addFilter($filterInstance);
266
267                 // Trace message
268                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
269         }
270
271         /**
272          * Adds a filter to the pre filter chain
273          *
274          * @param       $filterInstance         An instance of a filter
275          * @return      void
276          */
277         public function addPreFilter (Filterable $filterInstance) {
278                 // Add the pre filter
279                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: filterInstance=%s - CALLED!', $filterInstance->__toString()));
280                 $this->addFilter(self::FILTER_CHAIN_PRE_COMMAND, $filterInstance);
281
282                 // Trace message
283                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
284         }
285
286         /**
287          * Adds a filter to the post filter chain
288          *
289          * @param       $filterInstance         An instance of a filter
290          * @return      void
291          */
292         public function addPostFilter (Filterable $filterInstance) {
293                 // Add post filter
294                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: filterInstance=%s - CALLED!', $filterInstance->__toString()));
295                 $this->addFilter(self::FILTER_CHAIN_POST_COMMAND, $filterInstance);
296
297                 // Trace message
298                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
299         }
300
301         /**
302          * Add a shutdown filter
303          *
304          * @param       $filterInstance         A Filterable class
305          * @return      void
306          */
307         public function addShutdownFilter (Filterable $filterInstance) {
308                 // Add shutdown filter
309                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: filterInstance=%s - CALLED!', $filterInstance->__toString()));
310                 $this->addFilter(self::FILTER_CHAIN_SHUTDOWN, $filterInstance);
311
312                 // Trace message
313                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
314         }
315
316         /**
317          * Executes given filter chain chain
318          *
319          * @param       $filterChain            Chain of the filter to execute
320          * @param       $requestInstance        An instance of a Requestable class
321          * @param       $responseInstance       An instance of a Responseable class
322          * @return      void
323          * @throws      InvalidArgumentException        If the filter chain is invalid
324          */
325         protected function executeFilters (string $filterChain, Requestable $requestInstance, Responseable $responseInstance) {
326                 // Check parameter
327                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: filterChain=%s,requestInstance=%s,responseInstance=%s - CALLED!', $filterChain, $requestInstance->__toString(), $responseInstance->__toString()));
328                 if (empty($filterChain)) {
329                         // Throw IAE
330                         throw new InvalidArgumentException('Parameter "filterChain" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
331                 } elseif (!isset($this->filterChains[$filterChain])) {
332                         // Throw an exception here
333                         throw new BadMethodCallException(sprintf('filterChain=%s is not a valid chain', $filterChain), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
334                 }
335
336                 // Run all filters
337                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-CONTROLLER: Processing filterChain=%s...', $filterChain));
338                 $this->filterChains[$filterChain]->processFilters($requestInstance, $responseInstance);
339
340                 // Trace message
341                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
342         }
343
344         /**
345          * Executes all pre filters
346          *
347          * @param       $requestInstance        An instance of a Requestable class
348          * @param       $responseInstance       An instance of a Responseable class
349          * @return      void
350          */
351         protected function executePreFilters (Requestable $requestInstance, Responseable $responseInstance) {
352                 // Execute all pre filters
353                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: requestInstance=%s,responseInstance=%s - CALLED!', $requestInstance->__toString(), $responseInstance->__toString()));
354                 $this->executeFilters(self::FILTER_CHAIN_PRE_COMMAND, $requestInstance, $responseInstance);
355
356                 // Trace message
357                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
358         }
359
360         /**
361          * Executes all post filters
362          *
363          * @param       $requestInstance        An instance of a Requestable class
364          * @param       $responseInstance       An instance of a Responseable class
365          * @return      void
366          */
367         protected function executePostFilters (Requestable $requestInstance, Responseable $responseInstance) {
368                 // Execute all post filters
369                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: requestInstance=%s,responseInstance=%s - CALLED!', $requestInstance->__toString(), $responseInstance->__toString()));
370                 $this->executeFilters(self::FILTER_CHAIN_POST_COMMAND, $requestInstance, $responseInstance);
371
372                 // Trace message
373                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
374         }
375
376         /**
377          * Executes all shutdown filters
378          *
379          * @param       $requestInstance        A Requestable class
380          * @param       $responseInstance       A Responseable class
381          * @return      void
382          */
383         public function executeShutdownFilters (Requestable $requestInstance, Responseable $responseInstance) {
384                 // Execute all shutdown filter
385                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-CONTROLLER: requestInstance=%s,responseInstance=%s - CALLED!', $requestInstance->__toString(), $responseInstance->__toString()));
386                 $this->executeFilters(self::FILTER_CHAIN_SHUTDOWN, $requestInstance, $responseInstance);
387
388                 // Trace message
389                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-CONTROLLER: EXIT!');
390         }
391
392 }