PluginProbe
Advanced Access Manager – Access Governance for WordPress / 6.9.0
Advanced Access Manager – Access Governance for WordPress v6.9.0
7.1.4 7.1.2 7.1.3 6.8.4 6.8.5 6.9.0 6.9.1 6.9.10 6.9.11 6.9.12 6.9.13 6.9.14 6.9.15 6.9.16 6.9.17 6.9.18 6.9.19 6.9.2 6.9.20 6.9.21 6.9.22 6.9.23 6.9.24 6.9.25 6.9.26 All 210 releases
advanced-access-manager / application / Core / Subject.php
Subject.php
475 lines 11.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * ======================================================================
5 * LICENSE: This file is subject to the terms and conditions defined in *
6 * file 'license.txt', which is part of this source code package. *
7 * ======================================================================
8 */
9
10 /**
11 * Abstract subject class
12 *
13 * Subject is a user or thing that invokes WordPress resources like posts, menus,
14 * URIs, etc. In other words, subject is the abstract access and security layer that
15 * contains set of options that define how end user or visitor access a requested
16 * resource.
17 *
18 * Subjects are related in the hierarchical way where "Default" subject supersede all
19 * other subjects and access & security settings are propagated down the tree.
20 *
21 * Subject sibling is thing that is located on the same hierarchical level and access
22 * settings get merged based on predefined preference. The example of sibling is a
23 * user that has two or more roles. In this case the first role is primary while all
24 * other roles are siblings to it.
25 *
26 * Subject principal is underlying WordPress core user or role. Not all Subjects have
27 * principals (e.g. Visitor or Default).
28 *
29 * @since 6.7.0 https://github.com/aamplugin/advanced-access-manager/issues/152
30 * @since 6.3.2 Added new hook `aam_initialized_{$type}_object_filter`
31 * @since 6.1.0 Fixed bug with incorrectly managed internal cache
32 * @since 6.0.0 Initial implementation of the class
33 *
34 * @package AAM
35 * @version 6.7.0
36 */
37 abstract class AAM_Core_Subject
38 {
39
40 /**
41 * Subject ID
42 *
43 * Whether it is User ID or Role ID
44 *
45 * @var string|int
46 *
47 * @access private
48 * @version 6.0.0
49 */
50 private $_id;
51
52 /**
53 * WordPres core principal
54 *
55 * It can be WP_User or WP_Role, based on what class has been used
56 *
57 * @var WP_Role|WP_User
58 *
59 * @access private
60 * @version 6.0.0
61 */
62 private $_principal;
63
64 /**
65 * Principal's siblings
66 *
67 * For example this is quite typical for the multi-roles
68 *
69 * @var array
70 *
71 * @access private
72 * @version 6.0.0
73 */
74 private $_siblings = array();
75
76 /**
77 * List of Objects to be access controlled for current subject
78 *
79 * All access control objects like Admin Menu, Metaboxes, Posts etc
80 *
81 * @var array
82 *
83 * @access private
84 * @version 6.0.0
85 */
86 private $_objects = array();
87
88 /**
89 * Fallback for any principal native methods
90 *
91 * @param string $name
92 * @param array $args
93 *
94 * @return mixed
95 *
96 * @access public
97 * @version 6.0.0
98 */
99 public function __call($name, $args)
100 {
101 $response = null;
102 $principal = $this->getPrincipal();
103
104 // Make sure that method is callable
105 if (method_exists($principal, $name)) {
106 $response = call_user_func_array(array($principal, $name), $args);
107 } else {
108 _doing_it_wrong(
109 static::class . '::' . $name,
110 'Subject does not have method defined',
111 AAM_VERSION
112 );
113 }
114
115 return $response;
116 }
117
118 /**
119 * Fallback for the principal native properties
120 *
121 * @param string $name
122 *
123 * @return mixed
124 *
125 * @access public
126 * @version 6.0.0
127 */
128 public function __get($name)
129 {
130 return $this->getPrincipal()->$name;
131 }
132
133 /**
134 * Fallback for the principal native properties
135 *
136 * @param string $name
137 *
138 * @return mixed
139 *
140 * @access public
141 * @version 6.0.0
142 */
143 public function __set($name, $value)
144 {
145 $principal = $this->getPrincipal();
146 $principal->$name = $value;
147 }
148
149 /**
150 * Set subject ID
151 *
152 * @param string|int
153 *
154 * @return void
155 *
156 * @access public
157 * @version 6.0.0
158 */
159 public function setId($id)
160 {
161 $this->_id = $id;
162 }
163
164 /**
165 * Get subject ID
166 *
167 * @return string|int
168 *
169 * @access public
170 * @version 6.0.0
171 */
172 public function getId()
173 {
174 return $this->_id;
175 }
176
177 /**
178 * Get subject name
179 *
180 * @return string
181 *
182 * @access public
183 * @version 6.0.0
184 */
185 abstract public function getName();
186
187 /**
188 * Get maximum subject User level
189 *
190 * @return int
191 *
192 * @access public
193 * @version 6.0.0
194 */
195 public function getMaxLevel()
196 {
197 return 0;
198 }
199
200 /**
201 * Get WP core principal
202 *
203 * @return WP_Role|WP_User
204 *
205 * @access public
206 * @version 6.0.0
207 */
208 public function getPrincipal()
209 {
210 return $this->_principal;
211 }
212
213 /**
214 * Set WP core principal
215 *
216 * @param WP_Role|WP_User $principal
217 *
218 * @return void
219 *
220 * @access public
221 * @version 6.0.0
222 */
223 public function setPrincipal($principal)
224 {
225 $this->_principal = $principal;
226 }
227
228 /**
229 * Get subject siblings
230 *
231 * @param array $siblings
232 *
233 * @return void
234 *
235 * @access public
236 * @version 6.0.0
237 */
238 public function setSiblings(array $siblings)
239 {
240 $this->_siblings = $siblings;
241 }
242
243 /**
244 * Check if subject has siblings
245 *
246 * @return boolean
247 *
248 * @access public
249 * @version 6.0.0
250 */
251 public function hasSiblings()
252 {
253 return (count($this->_siblings) > 0);
254 }
255
256 /**
257 * Get list of subject siblings
258 *
259 * @return array
260 *
261 * @access public
262 * @version 6.0.0
263 */
264 public function getSiblings()
265 {
266 return $this->_siblings;
267 }
268
269 /**
270 * Get AAM core object
271 *
272 * This method will instantiate requested AAM core object with pre-populated
273 * access settings for the subject that requested the object.
274 *
275 * @param string $type
276 * @param mixed $id
277 * @param boolean $skipInheritance
278 *
279 * @return AAM_Core_Object
280 *
281 * @since 6.3.2 Added new hook `aam_initialized_{$type}_object_filter` to solve
282 * https://github.com/aamplugin/advanced-access-manager/issues/52
283 * @since 6.1.0 Fixed the bug where initialize object was not cached correctly
284 * due to $skipInheritance flag
285 * @since 6.0.0 Initial implementation of the method
286 *
287 * @access public
288 * @version 6.3.2
289 */
290 public function getObject($type, $id = null, $skipInheritance = false)
291 {
292 $suffix = ($skipInheritance ? '_direct' : '_full');
293
294 // Check if there is an object with specified ID
295 if (!isset($this->_objects[$type . $id . $suffix])) {
296 $class_name = 'AAM_Core_Object_' . ucfirst($type);
297
298 // If requested object is part of the core, instantiate it
299 if (class_exists($class_name)) {
300 $object = new $class_name($this, $id, $skipInheritance);
301 } else {
302 $object = apply_filters(
303 'aam_object_filter', null, $this, $type, $id, $skipInheritance
304 );
305 }
306
307 if (is_a($object, 'AAM_Core_Object')) {
308 // Kick in the inheritance chain if needed
309 if ($skipInheritance === false) {
310 $this->inheritFromParent($object);
311 }
312
313 // Finally cache the object
314 $this->_objects[$type . $id . $suffix] = apply_filters(
315 "aam_initialized_{$type}_object_filter", $object
316 );
317 }
318 } else {
319 $object = $this->_objects[$type . $id . $suffix];
320 }
321
322 return $object;
323 }
324
325 /**
326 * Inherit access settings for provided object from the parent subject(s)
327 *
328 * @param AAM_Core_Object $object
329 *
330 * @return array
331 *
332 * @since 6.7.0 https://github.com/aamplugin/advanced-access-manager/issues/152
333 * @since 6.0.0 Initial implementation of the method
334 *
335 * @access protected
336 * @version 6.7.0
337 */
338 protected function inheritFromParent(AAM_Core_Object $object)
339 {
340 $subject = $this->getParent();
341
342 if (is_a($subject, 'AAM_Core_Subject')) {
343 $option = $subject->getObject(
344 $object::OBJECT_TYPE,
345 $object->getId()
346 )->getOption();
347
348 // Merge access settings if multi-roles option is enabled
349 $multi = AAM::api()->getConfig('core.settings.multiSubject', false);
350
351 if ($multi && $subject->hasSiblings()) {
352 foreach ($subject->getSiblings() as $sibling) {
353 $option = $sibling->getObject(
354 $object::OBJECT_TYPE,
355 $object->getId()
356 )->mergeOption(
357 $option
358 );
359 }
360 }
361
362 // Merge access settings while reading hierarchical chain
363 $option = array_replace_recursive($option, $object->getOption());
364
365 // Finally set the option for provided object
366 $object->setOption($option);
367 }
368
369 return $object->getOption();
370 }
371
372 /**
373 * Retrieve parent subject
374 *
375 * If there is no parent subject, return null
376 *
377 * @return AAM_Core_Subject|null
378 *
379 * @access public
380 * @version 6.0.0
381 */
382 abstract public function getParent();
383
384 /**
385 * Update subject access option
386 *
387 * @param mixed $value
388 * @param string $object
389 * @param mixed $id
390 *
391 * @return boolean
392 *
393 * @access public
394 * @version 6.0.0
395 */
396 public function updateOption($value, $object, $id = null)
397 {
398 return AAM_Core_AccessSettings::getInstance()->set(
399 $this->getOptionName($object, $id), $value
400 )->save();
401 }
402
403 /**
404 * Read subject access option
405 *
406 * @param string $object
407 * @param mixed $id
408 *
409 * @return array
410 *
411 * @access public
412 * @version 6.0.0
413 */
414 public function readOption($object, $id = null)
415 {
416 return AAM_Core_AccessSettings::getInstance()->get(
417 $this->getOptionName($object, $id)
418 );
419 }
420
421 /**
422 * Delete subject access option
423 *
424 * @param string $object
425 * @param mixed $id
426 *
427 * @return boolean
428 *
429 * @access public
430 * @version 6.0.0
431 */
432 public function deleteOption($object, $id = null)
433 {
434 return AAM_Core_AccessSettings::getInstance()->delete(
435 $this->getOptionName($object, $id)
436 )->save();
437 }
438
439 /**
440 * Compute access option name based on object type
441 *
442 * @param string $object
443 * @param mixed $id
444 *
445 * @return string
446 *
447 * @access protected
448 * @version 6.0.0
449 */
450 public function getOptionName($object, $id)
451 {
452 $subjectId = $this->getId();
453
454 $name = static::UID . ($subjectId ? ".{$subjectId}" : '') . '.';
455 $name .= $object . ($id ? ".{$id}" : '');
456
457 return $name;
458 }
459
460 /**
461 * Reset object cache
462 *
463 * Subject caches all instantiated object for performance reasons
464 *
465 * @return void
466 *
467 * @access public
468 * @version 6.0.0
469 */
470 public function flushCache()
471 {
472 $this->_objects = array();
473 }
474
475 }