Completed
Pull Request — development (#3073)
by John
09:09
created

Action::dispatch()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 41
Code Lines 16

Duplication

Lines 12
Ratio 29.27 %

Code Coverage

Tests 17
CRAP Score 5.4439

Importance

Changes 0
Metric Value
cc 5
eloc 16
nc 5
nop 1
dl 12
loc 41
ccs 17
cts 23
cp 0.7391
crap 5.4439
rs 8.439
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Defines an action with its associated sub-actions
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * @version 1.1
11
 *
12
 */
13
14
/**
15
 * Action class defines an action with its associated sub-actions.
16
 * Object-oriented controllers (with sub-actions) uses it to set their action-subaction arrays, and have it call the
17
 * right function or method handlers.
18
 *
19
 * Replaces the sub-actions arrays in every dispatching function.
20
 * (the $subActions = ... etc, and calls for $_REQUEST['sa'])
21
 *
22
 */
23
class Action
24
{
25
	/**
26
	 * All the subactions we understand
27
	 * @var array
28
	 */
29
	protected $_subActions = [];
30
31
	/**
32
	 * The default subAction.
33
	 * @var string
34
	 */
35
	protected $_default;
36
37
	/**
38
	 * An (unique) id that triggers a hook
39
	 * @var string
40
	 */
41
	protected $_name;
42
43
	/** @var HttpReq Access to post/get data */
44
	protected $req;
45
46
	/**
47
	 * Constructor!
48
	 *
49
	 * @param string  $name Hook name
50
	 * @param HttpReq $req  Access to post/get data
0 ignored issues
show
Documentation introduced by
Should the type for parameter $req not be null|HttpReq?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
51
	 */
52
	public function __construct(string $name = '', HttpReq $req = null)
53
	{
54
		$this->_name = $name;
55
		$this->req = $req ?: HttpReq::instance();
56
	}
57
58
	/**
59
	 * Initialize the instance with an array of sub-actions.
60
	 *
61
	 * @param array  $subActions    array of known subactions
62
	 *
63
	 *                              The accepted array format is:
64
	 *                              'sub_action name' => 'function name',
65
	 *                              or
66
	 *                              'sub_action name' => array(
67
	 *                              'function' => 'function name'),
68
	 *                              or
69
	 *                              'sub_action name' => array(
70
	 *                              'controller' => 'controller name',
71
	 *                              'function' => 'method name',
72
	 *                              'enabled' => true/false,
73
	 *                              'permission' => area),
74
	 *                              or
75
	 *                              'sub_action name' => array(
76
	 *                              'controller object, i.e. $this',
77 6
	 *                              'method name',
78
	 *                              'enabled' => true/false
79 6
	 *                              'permission' => area),
80 6
	 *                              or
81
	 *                              'sub_action name' => array(
82
	 *                              'controller' => 'controller name',
83
	 *                              'function' => 'method name',
84
	 *                              'enabled' => true/false,
85
	 *                              'permission' => area)
86
	 *
87
	 *                              If `enabled` is not present, it is aassumed to be true.
88
	 *
89
	 * @param string $default       default action if unknown sa is requested
90
	 * @param string $requestParam  key to check HTTP GET value, defaults to `sa`
91
	 *
92
	 * @event  integrate_sa_ the name specified in the constructor is appended to this
93
	 *
94
	 * @return string
95
	 */
96 6
	public function initialize(array $subActions, string $default = '', string $requestParam = 'sa'): string
97
	{
98 6
		if ($this->_name !== '')
99 6
		{
100
			call_integration_hook('integrate_sa_' . $this->_name, [&$subActions]);
101 6
		}
102
103 6
		$this->_subActions = array_filter(
104 4
			$subActions,
105
			function ($subAction)
106 6
			{
107
				return
108 6
					!isset($subAction['enabled'])
109 6
					|| isset($subAction['enabled'])
110
					&& $subAction['enabled'] == true;
111 6
			}
112
		);
113
114
		$this->_default = $default ?: key($this->_subActions);
115
116
		return $this->req->getQuery($requestParam, 'trim|strval', $this->_default);
117
	}
118
119
	/**
120
	 * Call the function or method for the selected subaction.
121
	 *
122
	 * Both the controller and the method are set up in the subactions array. If a
123 6
	 * controller is not specified, the function is assumed to be a regular callable.
124
	 *
125
	 * @param string $sub_id a valid index in the subactions array
126 6
	 */
127 4
	public function dispatch(string $sub_id): void
128
	{
129
		$subAction = $this->_subActions[$sub_id] ?? $this->_subActions[$this->_default];
130
		$this->isAllowedTo($sub_id);
131
132 6
		// Start off by assuming that this is a callable of some kind.
133
		$call = $subAction;
134
135 6
		if (isset($subAction['function']))
136 4
		{
137
			// This is just a good ole' function
138
			$call = $subAction['function'];
139
		}
140
		// Calling a method within a controller?
141
		elseif (isset($subAction['controller'], $subAction['function']))
142
		{
143 6
			// Instance of a class
144 4 View Code Duplication
			if (is_object($subAction['controller']))
145
			{
146
				$controller = $subAction['controller'];
147 6
			}
148 4
			else
149 6
			{
150 6
				// Pointer to a controller to load
151
				$controller = new $subAction['controller'](new Event_Manager());
152
153
				// always set up the environment
154
				$controller->pre_dispatch();
155 6
			}
156 4
157
			// Modify the call accordingly
158 6
			$call = [$controller, $subAction['function']];
159 6
		}
160
		// Callable directly within the array? Discard invalid entries.
161 6
		elseif (isset($subAction[0], $subAction[1]))
162 4
		{
163
			$call = [$subAction[0], $subAction[1]];
164
		}
165
166 2
		call_user_func($call);
167
	}
168 4
169
	/**
170
	 * Security check: verify that the user has the permission to perform the
171
	 * given action, and throw an error otherwise.
172
	 *
173
	 * @param string $sub_id The sub action
174
	 *
175
	 * @return bool
176
	 */
177
	protected function isAllowedTo(string $sub_id): bool
178
	{
179
		if (isset($this->_subActions[$sub_id], $this->_subActions[$sub_id]['permission']))
180
		{
181
			isAllowedTo($this->_subActions[$sub_id]['permission']);
182
		}
183
184
		return true;
185
	}
186
}
187