Issues (1686)

sources/ElkArte/Action.php (1 issue)

1
<?php
2
3
/**
4
 * Defines an action with its associated sub-actions
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * @version 2.0 dev
11
 *
12
 */
13
14
namespace ElkArte;
15
16
use ElkArte\Helper\HttpReq;
17
18
/**
19
 * Action class defines an action with its associated sub-actions.
20
 *
21
 * Object-oriented controllers (with sub-actions) use it to set their action-subaction arrays, and have it call the
22
 * right function or method handlers.
23
 *
24
 * Replaces the sub-actions arrays in every dispatching function.
25
 * (the $subActions = ... etc, and calls for $_REQUEST['sa'])
26
 *
27
 */
28
class Action
29
{
30
	/** @var array All the subactions we understand */
31
	protected $_subActions = [];
32
33
	/** @var string The default subAction. */
34
	protected $_default;
35
36
	/** @var string A (unique !!) id that triggers a hook */
37
	protected $_name;
38
39
	/** @var HttpReq Access to post/get data */
40
	protected $req;
41
42
	/**
43
	 * Constructor!
44
	 *
45
	 * @param string|null $name Hook name
46
	 * @param HttpReq $req Access to post/get data
47
	 */
48
	public function __construct(string $name = null, $req = null)
49
	{
50
		$this->_name = $name;
51
		$this->req = $req ?: HttpReq::instance();
52
	}
53
54
	/**
55
	 * Initialize the instance with an array of sub-actions.
56
	 *
57 18
	 * - Sets a valid default action if none is supplied.
58
	 * - Returns the cleaned subaction or the default action if the subaction is not valid / available
59 18
	 * - Calls generic integration hook integrate_sa_XYZ where XYZ is the optional named passed via new Action('XYZ')
60 18
	 *
61 18
	 * @param array $subActions array of known subactions
62
	 * The accepted array format is:
63
	 *   'sub_action name' => 'function name'
64
	 *  or
65
	 *   'sub_action name' => array('function' => 'function name')
66
	 *  or
67
	 *   'sub_action name' => array(
68
	 *      'controller' => 'controller name',
69
	 *      'function' => 'method name',
70
	 *      'enabled' => true/false,
71
	 *      'permission' => area),
72
	 *  or
73
	 *   'sub_action name' => array(
74
	 *      'controller object, i.e. $this',
75
	 *      'method name',
76
	 *      'enabled' => true/false
77
	 *      'permission' => area),
78
	 *  or
79
	 *   'sub_action name' => array(
80
	 *      'controller' => 'controller name',
81
	 *      'function' => 'method name',
82
	 *      'enabled' => true/false,
83
	 *      'permission' => area)
84
	 *
85
	 *  If `enabled` is not present, it is assumed to be true.
86
	 *
87
	 * @param string $default default action if an unknown sa is requested
88
	 * @param string $requestParam key to check for the HTTP GET value, defaults to `sa`
89
	 *
90
	 * @event  integrate_sa_ the name specified in the constructor is appended to this
91
	 *
92
	 * @return string the valid subaction
93
	 */
94
	public function initialize(array $subActions, string $default = '', string $requestParam = 'sa'): string
95
	{
96
		// Controller action initialized as new Action('xyz'), then call xyz integration hook
97
		if ($this->_name !== null)
98
		{
99
			call_integration_hook('integrate_sa_' . $this->_name, [&$subActions]);
100
		}
101 18
102
		$this->_subActions = array_filter(
103 18
			$subActions,
104
			static function ($subAction) {
105 12
				if (isset($subAction['disabled']) && ($subAction['disabled'] === true || $subAction['disabled'] === 'true'))
106
				{
107
					return false;
108 18
				}
109 18
110
				return !(isset($subAction['enabled']) && ($subAction['enabled'] === false || $subAction['enabled'] === 'false'));
111 18
			}
112
		);
113
114
		$this->_default = $default ?: key($this->_subActions);
115
116 18
		$subAction = $this->req->getRequest($requestParam, 'trim|strval', $this->_default);
117
118
		return isset($this->_subActions[$subAction]) ? $subAction : $this->_default;
0 ignored issues
show
Bug Best Practice introduced by
The expression return IssetNode ? $subAction : $this->_default could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
119
	}
120
121 18
	/**
122 18
	 * Call the function or method for the selected subaction.
123
	 *
124
	 * - Both the controller and the method are set up in the subactions array.
125 18
	 * - If a controller is not specified, the function is assumed to be a regular callable.
126
	 * - Checks on permission of the $sub_id IF a permission area/check was passed.
127 18
	 *
128
	 * @param string $sub_id a valid index in the subactions array
129 18
	 */
130
	public function dispatch(string $sub_id): void
131
	{
132
		$subAction = $this->_subActions[$sub_id] ?? $this->_subActions[$this->_default];
133
		$this->isAllowedTo($sub_id);
134
135
		// Start off by assuming that this is a callable of some kind.
136
		$call = $subAction['function'] ?? $subAction;
137
138
		// Calling a method within a controller?
139
		if (isset($subAction['controller'], $subAction['function']))
140
		{
141 18
			// Instance of a class
142
			if (is_object($subAction['controller']))
143 18
			{
144 18
				$controller = $subAction['controller'];
145
			}
146
			else
147 18
			{
148
				// Pointer to a controller to load
149 18
				$controller = new $subAction['controller'](new EventManager());
150
151
				// always set up the environment
152 6
				$controller->getHook();
153
				$controller->setUser(User::$info);
154
				$controller->pre_dispatch();
155
			}
156 18
157
			// Modify the call accordingly
158
			$call = [$controller, $subAction['function']];
159 6
		}
160
		// Callable directly within the array? Discard invalid entries.
161
		elseif (isset($subAction[0], $subAction[1]))
162
		{
163
			$call = [$subAction[0], $subAction[1]];
164
		}
165
166 6
		$call();
167 6
	}
168
169
	/**
170 6
	 * 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 6
	 */
175
	protected function isAllowedTo(string $sub_id): bool
176
	{
177 12
		if (isset($this->_subActions[$sub_id]['permission']))
178
		{
179 12
			isAllowedTo($this->_subActions[$sub_id]['permission']);
180
		}
181
182 18
		return true;
183 18
	}
184
}
185