Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
73.29% covered (warning)
73.29%
236 / 322
33.33% covered (danger)
33.33%
4 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
permission
73.29% covered (warning)
73.29%
236 / 322
33.33% covered (danger)
33.33%
4 / 12
182.16
0.00% covered (danger)
0.00%
0 / 1
 __construct
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
2.01
 get_name
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 exists
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
3
 add
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
9
 remove
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
7
 role_exists
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
3
 role_add
94.12% covered (success)
94.12%
16 / 17
0.00% covered (danger)
0.00%
0 / 1
3.00
 role_update
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 role_remove
95.24% covered (success)
95.24%
40 / 42
0.00% covered (danger)
0.00%
0 / 1
7
 permission_set
95.24% covered (success)
95.24%
80 / 84
0.00% covered (danger)
0.00%
0 / 1
18
 permission_unset
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 1
110
 reverse
32.26% covered (danger)
32.26%
10 / 31
0.00% covered (danger)
0.00%
0 / 1
41.09
1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14namespace phpbb\db\migration\tool;
15
16/**
17* Migration permission management tool
18*/
19class permission implements \phpbb\db\migration\tool\tool_interface
20{
21    /** @var \phpbb\auth\auth */
22    protected $auth;
23
24    /** @var \auth_admin */
25    protected $auth_admin;
26
27    /** @var \phpbb\cache\service */
28    protected $cache;
29
30    /** @var \phpbb\db\driver\driver_interface */
31    protected $db;
32
33    /** @var string */
34    protected $phpbb_root_path;
35
36    /** @var string */
37    protected $php_ext;
38
39    /**
40    * Constructor
41    *
42    * @param \phpbb\db\driver\driver_interface $db
43    * @param \phpbb\cache\service $cache
44    * @param \phpbb\auth\auth $auth
45    * @param string $phpbb_root_path
46    * @param string $php_ext
47    */
48    public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\cache\service $cache, \phpbb\auth\auth $auth, $phpbb_root_path, $php_ext)
49    {
50        $this->db = $db;
51        $this->cache = $cache;
52        $this->auth = $auth;
53        $this->phpbb_root_path = $phpbb_root_path;
54        $this->php_ext = $php_ext;
55
56        if (!class_exists('auth_admin'))
57        {
58            include($this->phpbb_root_path . 'includes/acp/auth.' . $this->php_ext);
59        }
60        $this->auth_admin = new \auth_admin();
61    }
62
63    /**
64    * {@inheritdoc}
65    */
66    public function get_name()
67    {
68        return 'permission';
69    }
70
71    /**
72    * Permission Exists
73    *
74    * Check if a permission (auth) setting exists
75    *
76    * @param string $auth_option The name of the permission (auth) option
77    * @param bool $global True for checking a global permission setting,
78    *     False for a local permission setting
79    * @return bool true if it exists, false if not
80    */
81    public function exists($auth_option, $global = true)
82    {
83        if ($global)
84        {
85            $type_sql = ' AND is_global = 1';
86        }
87        else
88        {
89            $type_sql = ' AND is_local = 1';
90        }
91
92        $sql = 'SELECT auth_option_id
93            FROM ' . ACL_OPTIONS_TABLE . "
94            WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'"
95                . $type_sql;
96        $result = $this->db->sql_query($sql);
97
98        $row = $this->db->sql_fetchrow($result);
99        $this->db->sql_freeresult($result);
100
101        if ($row)
102        {
103            return true;
104        }
105
106        return false;
107    }
108
109    /**
110    * Permission Add
111    *
112    * Add a permission (auth) option
113    *
114    * @param string $auth_option The name of the permission (auth) option
115    * @param bool $global True for checking a global permission setting,
116    *     False for a local permission setting
117    * @param int|false $copy_from If set, contains the id of the permission from which to copy the new one.
118    * @return void
119    */
120    public function add($auth_option, $global = true, $copy_from = false)
121    {
122        if ($this->exists($auth_option, $global))
123        {
124            return;
125        }
126
127        // We have to add a check to see if the !$global (if global, local, and if local, global) permission already exists.  If it does, acl_add_option currently has a bug which would break the ACL system, so we are having a work-around here.
128        if ($this->exists($auth_option, !$global))
129        {
130            $sql_ary = array(
131                'is_global'    => 1,
132                'is_local'    => 1,
133            );
134            $sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
135                SET ' . $this->db->sql_build_array('UPDATE', $sql_ary) . "
136                WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'";
137            $this->db->sql_query($sql);
138        }
139        else
140        {
141            if ($global)
142            {
143                $this->auth_admin->acl_add_option(array('global' => array($auth_option)));
144            }
145            else
146            {
147                $this->auth_admin->acl_add_option(array('local' => array($auth_option)));
148            }
149        }
150
151        // The permission has been added, now we can copy it if needed
152        if ($copy_from && isset($this->auth_admin->acl_options['id'][$copy_from]))
153        {
154            $old_id = $this->auth_admin->acl_options['id'][$copy_from];
155            $new_id = $this->auth_admin->acl_options['id'][$auth_option];
156
157            $tables = array(ACL_GROUPS_TABLE, ACL_ROLES_DATA_TABLE, ACL_USERS_TABLE);
158
159            foreach ($tables as $table)
160            {
161                $sql = 'SELECT *
162                    FROM ' . $table . '
163                    WHERE auth_option_id = ' . $old_id;
164                $result = $this->db->sql_query($sql);
165
166                $sql_ary = array();
167                while ($row = $this->db->sql_fetchrow($result))
168                {
169                    $row['auth_option_id'] = $new_id;
170                    $sql_ary[] = $row;
171                }
172                $this->db->sql_freeresult($result);
173
174                if (!empty($sql_ary))
175                {
176                    $this->db->sql_multi_insert($table, $sql_ary);
177                }
178            }
179
180            $this->auth_admin->acl_clear_prefetch();
181        }
182    }
183
184    /**
185    * Permission Remove
186    *
187    * Remove a permission (auth) option
188    *
189    * @param string $auth_option The name of the permission (auth) option
190    * @param bool $global True for checking a global permission setting,
191    *     False for a local permission setting
192    * @return void
193    */
194    public function remove($auth_option, $global = true)
195    {
196        if (!$this->exists($auth_option, $global))
197        {
198            return;
199        }
200
201        if ($global)
202        {
203            $type_sql = ' AND is_global = 1';
204        }
205        else
206        {
207            $type_sql = ' AND is_local = 1';
208        }
209        $sql = 'SELECT auth_option_id, is_global, is_local
210            FROM ' . ACL_OPTIONS_TABLE . "
211            WHERE auth_option = '" . $this->db->sql_escape($auth_option) . "'" .
212                $type_sql;
213        $result = $this->db->sql_query($sql);
214        $row = $this->db->sql_fetchrow($result);
215        $this->db->sql_freeresult($result);
216
217        $id = (int) $row['auth_option_id'];
218
219        // If it is a local and global permission, do not remove the row! :P
220        if ($row['is_global'] && $row['is_local'])
221        {
222            $sql = 'UPDATE ' . ACL_OPTIONS_TABLE . '
223                SET ' . (($global) ? 'is_global = 0' : 'is_local = 0') . '
224                WHERE auth_option_id = ' . $id;
225            $this->db->sql_query($sql);
226        }
227        else
228        {
229            // Delete time
230            $tables = array(ACL_GROUPS_TABLE, ACL_ROLES_DATA_TABLE, ACL_USERS_TABLE, ACL_OPTIONS_TABLE);
231            foreach ($tables as $table)
232            {
233                $this->db->sql_query('DELETE FROM ' . $table . '
234                    WHERE auth_option_id = ' . $id);
235            }
236        }
237
238        // Purge the auth cache
239        $this->cache->destroy('_acl_options');
240        $this->auth->acl_clear_prefetch();
241    }
242
243    /**
244     * Check if a permission role exists
245     *
246     * @param string $role_name The role name
247     *
248     * @return int The id of the role if it exists, 0 otherwise
249     */
250    public function role_exists($role_name)
251    {
252        $sql = 'SELECT role_id
253            FROM ' . ACL_ROLES_TABLE . "
254            WHERE role_name = '" . $this->db->sql_escape($role_name) . "'";
255        $result = $this->db->sql_query($sql);
256        $role_id = (int) $this->db->sql_fetchfield('role_id');
257        $this->db->sql_freeresult($result);
258
259        // Try falling back to searching by role description for standard role titles
260        if (!$role_id && preg_match('/ROLE_(?<title>([A-Z]+_?)+)/', $role_name, $matches))
261        {
262            $role_description = 'ROLE_DESCRIPTION_' . $matches['title'];
263            $sql = 'SELECT role_id
264                FROM ' . ACL_ROLES_TABLE . "
265                WHERE role_description = '" . $this->db->sql_escape($role_description) . "'";
266            $result = $this->db->sql_query($sql);
267            $role_id = (int) $this->db->sql_fetchfield('role_id');
268            $this->db->sql_freeresult($result);
269        }
270
271        return $role_id;
272    }
273
274    /**
275    * Add a new permission role
276    *
277    * @param string $role_name The new role name
278    * @param string $role_type The type (u_, m_, a_)
279    * @param string $role_description Description of the new role
280    *
281    * @return int|null Inserted SQL id or null if role already exists
282    */
283    public function role_add($role_name, $role_type, $role_description = '')
284    {
285        if ($this->role_exists($role_name))
286        {
287            return null;
288        }
289
290        $sql = 'SELECT MAX(role_order) AS max_role_order
291            FROM ' . ACL_ROLES_TABLE . "
292            WHERE role_type = '" . $this->db->sql_escape($role_type) . "'";
293        $this->db->sql_query($sql);
294        $role_order = (int) $this->db->sql_fetchfield('max_role_order');
295        $role_order = (!$role_order) ? 1 : $role_order + 1;
296
297        $sql_ary = array(
298            'role_name'            => $role_name,
299            'role_description'    => $role_description,
300            'role_type'            => $role_type,
301            'role_order'        => $role_order,
302        );
303
304        $sql = 'INSERT INTO ' . ACL_ROLES_TABLE . ' ' . $this->db->sql_build_array('INSERT', $sql_ary);
305        $this->db->sql_query($sql);
306
307        return (int) $this->db->sql_nextid();
308    }
309
310    /**
311    * Update the name on a permission role
312    *
313    * @param string $old_role_name The old role name
314    * @param string $new_role_name The new role name
315    * @return void
316    * @throws \phpbb\db\migration\exception
317    */
318    public function role_update($old_role_name, $new_role_name)
319    {
320        if (!$this->role_exists($old_role_name))
321        {
322            throw new \phpbb\db\migration\exception('ROLE_NOT_EXIST', $old_role_name);
323        }
324
325        $sql = 'UPDATE ' . ACL_ROLES_TABLE . "
326            SET role_name = '" . $this->db->sql_escape($new_role_name) . "'
327            WHERE role_name = '" . $this->db->sql_escape($old_role_name) . "'";
328        $this->db->sql_query($sql);
329    }
330
331    /**
332    * Remove a permission role
333    *
334    * @param string $role_name The role name to remove
335    * @return void
336    */
337    public function role_remove($role_name)
338    {
339        if (!($role_id = $this->role_exists($role_name)))
340        {
341            return;
342        }
343
344        // Get the role type
345        $sql = 'SELECT role_type
346            FROM ' . ACL_ROLES_TABLE . '
347            WHERE role_id = ' . (int) $role_id;
348        $result = $this->db->sql_query($sql);
349        $role_type = $this->db->sql_fetchfield('role_type');
350        $this->db->sql_freeresult($result);
351
352        // Get complete auth array
353        $sql = 'SELECT auth_option, auth_option_id
354            FROM ' . ACL_OPTIONS_TABLE . "
355            WHERE auth_option " . $this->db->sql_like_expression($role_type . $this->db->get_any_char());
356        $result = $this->db->sql_query($sql);
357
358        $auth_settings = [];
359        while ($row = $this->db->sql_fetchrow($result))
360        {
361            $auth_settings[$row['auth_option']] = ACL_NO;
362        }
363        $this->db->sql_freeresult($result);
364
365        // Get the role auth settings we need to re-set...
366        $sql = 'SELECT o.auth_option, r.auth_setting
367            FROM ' . ACL_ROLES_DATA_TABLE . ' r, ' . ACL_OPTIONS_TABLE . ' o
368            WHERE o.auth_option_id = r.auth_option_id
369                AND r.role_id = ' . (int) $role_id;
370        $result = $this->db->sql_query($sql);
371
372        while ($row = $this->db->sql_fetchrow($result))
373        {
374            $auth_settings[$row['auth_option']] = $row['auth_setting'];
375        }
376        $this->db->sql_freeresult($result);
377
378        // Get role assignments
379        $hold_ary = $this->auth_admin->get_role_mask($role_id);
380
381        // Re-assign permissions
382        foreach ($hold_ary as $forum_id => $forum_ary)
383        {
384            if (isset($forum_ary['users']))
385            {
386                $this->auth_admin->acl_set('user', $forum_id, $forum_ary['users'], $auth_settings, 0, false);
387            }
388
389            if (isset($forum_ary['groups']))
390            {
391                $this->auth_admin->acl_set('group', $forum_id, $forum_ary['groups'], $auth_settings, 0, false);
392            }
393        }
394
395        // Remove role from users and groups just to be sure (happens through acl_set)
396        $sql = 'DELETE FROM ' . ACL_USERS_TABLE . '
397            WHERE auth_role_id = ' . $role_id;
398        $this->db->sql_query($sql);
399
400        $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
401            WHERE auth_role_id = ' . $role_id;
402        $this->db->sql_query($sql);
403
404        $sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
405            WHERE role_id = ' . $role_id;
406        $this->db->sql_query($sql);
407
408        $sql = 'DELETE FROM ' . ACL_ROLES_TABLE . '
409            WHERE role_id = ' . $role_id;
410        $this->db->sql_query($sql);
411
412        $this->auth->acl_clear_prefetch();
413    }
414
415    /**
416    * Permission Set
417    *
418    * Allows you to set permissions for a certain group/role
419    *
420    * @param string $name The name of the role/group
421    * @param string|array $auth_option The auth_option or array of
422    *     auth_options you would like to set
423    * @param string $type The type (role|group)
424    * @param bool $has_permission True if you want to give them permission,
425    *     false if you want to deny them permission
426    * @return void
427    * @throws \phpbb\db\migration\exception
428    */
429    public function permission_set($name, $auth_option, $type = 'role', $has_permission = true)
430    {
431        if (!is_array($auth_option))
432        {
433            $auth_option = array($auth_option);
434        }
435
436        $new_auth = array();
437        $sql = 'SELECT auth_option_id
438            FROM ' . ACL_OPTIONS_TABLE . '
439            WHERE ' . $this->db->sql_in_set('auth_option', $auth_option);
440        $result = $this->db->sql_query($sql);
441        while ($row = $this->db->sql_fetchrow($result))
442        {
443            $new_auth[] = (int) $row['auth_option_id'];
444        }
445        $this->db->sql_freeresult($result);
446
447        $type = (string) $type; // Prevent PHP bug.
448        if (empty($new_auth) || !in_array($type, ['role','group']))
449        {
450            return;
451        }
452
453        $current_auth = array();
454
455        switch ($type)
456        {
457            case 'role':
458                if (!($role_id = $this->role_exists($name)))
459                {
460                    throw new \phpbb\db\migration\exception('ROLE_NOT_EXIST', $name);
461                }
462
463                $sql = 'SELECT auth_option_id, auth_setting
464                    FROM ' . ACL_ROLES_DATA_TABLE . '
465                    WHERE role_id = ' . $role_id;
466                $result = $this->db->sql_query($sql);
467                while ($row = $this->db->sql_fetchrow($result))
468                {
469                    $current_auth[$row['auth_option_id']] = $row['auth_setting'];
470                }
471                $this->db->sql_freeresult($result);
472            break;
473
474            case 'group':
475                $sql = 'SELECT group_id
476                    FROM ' . GROUPS_TABLE . "
477                    WHERE group_name = '" . $this->db->sql_escape($name) . "'";
478                $this->db->sql_query($sql);
479                $group_id = (int) $this->db->sql_fetchfield('group_id');
480
481                if (!$group_id)
482                {
483                    throw new \phpbb\db\migration\exception('GROUP_NOT_EXIST', $name);
484                }
485
486                // If the group has a role set for them we will add the requested permissions to that role.
487                $sql = 'SELECT auth_role_id
488                    FROM ' . ACL_GROUPS_TABLE . '
489                    WHERE group_id = ' . $group_id . '
490                        AND auth_role_id <> 0
491                        AND forum_id = 0';
492                $this->db->sql_query($sql);
493                $role_id = (int) $this->db->sql_fetchfield('auth_role_id');
494                if ($role_id)
495                {
496                    $sql = 'SELECT role_name, role_type
497                        FROM ' . ACL_ROLES_TABLE . '
498                        WHERE role_id = ' . $role_id;
499                    $this->db->sql_query($sql);
500                    $role_data = $this->db->sql_fetchrow();
501                    if (!$role_data)
502                    {
503                        throw new \phpbb\db\migration\exception('ROLE_ASSIGNED_NOT_EXIST', $name, $role_id);
504                    }
505
506                    $role_name = $role_data['role_name'];
507                    $role_type = $role_data['role_type'];
508
509                    // Filter new auth options to match the role type: a_ | f_ | m_ | u_
510                    // Set new auth options to the role only if options matching the role type were found
511                    $auth_option = array_filter($auth_option,
512                        function ($option) use ($role_type)
513                        {
514                            return strpos($option, $role_type) === 0;
515                        }
516                    );
517
518                    if (count($auth_option))
519                    {
520                        $this->permission_set($role_name, $auth_option, 'role', $has_permission);
521                        return;
522                    }
523                }
524
525                $sql = 'SELECT auth_option_id, auth_setting
526                    FROM ' . ACL_GROUPS_TABLE . '
527                    WHERE group_id = ' . $group_id;
528                $result = $this->db->sql_query($sql);
529                while ($row = $this->db->sql_fetchrow($result))
530                {
531                    $current_auth[$row['auth_option_id']] = $row['auth_setting'];
532                }
533                $this->db->sql_freeresult($result);
534            break;
535        }
536
537        $sql_ary = $auth_update_list = [];
538        $table = $type == 'role' ? ACL_ROLES_DATA_TABLE : ACL_GROUPS_TABLE;
539        foreach ($new_auth as $auth_option_id)
540        {
541            if (!isset($current_auth[$auth_option_id]))
542            {
543                $sql_ary[] = [
544                    $type . '_id'        => ${$type . '_id'},
545                    'auth_option_id'    => $auth_option_id,
546                    'auth_setting'        => (int) $has_permission,
547                ];
548            }
549            else
550            {
551                $auth_update_list[] = $auth_option_id;
552            }
553        }
554        $this->db->sql_multi_insert($table, $sql_ary);
555
556        if (count($auth_update_list))
557        {
558            $sql = 'UPDATE ' . $table . '
559                SET auth_setting = ' . (int) $has_permission . '
560                WHERE ' . $this->db->sql_in_set('auth_option_id', $auth_update_list) . '
561                    AND ' . $type . '_id = ' .  (int) ${$type . '_id'};
562            $this->db->sql_query($sql);
563        }
564
565        $this->auth->acl_clear_prefetch();
566    }
567
568    /**
569    * Permission Unset
570    *
571    * Allows you to unset (remove) permissions for a certain group/role
572    *
573    * @param string $name The name of the role/group
574    * @param string|array $auth_option The auth_option or array of
575    *     auth_options you would like to set
576    * @param string $type The type (role|group)
577    * @return void
578    * @throws \phpbb\db\migration\exception
579    */
580    public function permission_unset($name, $auth_option, $type = 'role')
581    {
582        if (!is_array($auth_option))
583        {
584            $auth_option = array($auth_option);
585        }
586
587        $to_remove = array();
588        $sql = 'SELECT auth_option_id
589            FROM ' . ACL_OPTIONS_TABLE . '
590            WHERE ' . $this->db->sql_in_set('auth_option', $auth_option);
591        $result = $this->db->sql_query($sql);
592        while ($row = $this->db->sql_fetchrow($result))
593        {
594            $to_remove[] = (int) $row['auth_option_id'];
595        }
596        $this->db->sql_freeresult($result);
597
598        if (empty($to_remove))
599        {
600            return;
601        }
602
603        $type = (string) $type; // Prevent PHP bug.
604
605        switch ($type)
606        {
607            case 'role':
608                if (!($role_id = $this->role_exists($name)))
609                {
610                    throw new \phpbb\db\migration\exception('ROLE_NOT_EXIST', $name);
611                }
612
613                $sql = 'DELETE FROM ' . ACL_ROLES_DATA_TABLE . '
614                    WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove) . '
615                        AND role_id = ' . (int) $role_id;
616                $this->db->sql_query($sql);
617            break;
618
619            case 'group':
620                $sql = 'SELECT group_id
621                    FROM ' . GROUPS_TABLE . "
622                    WHERE group_name = '" . $this->db->sql_escape($name) . "'";
623                $this->db->sql_query($sql);
624                $group_id = (int) $this->db->sql_fetchfield('group_id');
625
626                if (!$group_id)
627                {
628                    throw new \phpbb\db\migration\exception('GROUP_NOT_EXIST', $name);
629                }
630
631                // If the group has a role set for them we will remove the requested permissions from that role.
632                $sql = 'SELECT auth_role_id
633                    FROM ' . ACL_GROUPS_TABLE . '
634                    WHERE group_id = ' . $group_id . '
635                        AND auth_role_id <> 0';
636                $this->db->sql_query($sql);
637                $role_id = (int) $this->db->sql_fetchfield('auth_role_id');
638                if ($role_id)
639                {
640                    $sql = 'SELECT role_name
641                        FROM ' . ACL_ROLES_TABLE . '
642                        WHERE role_id = ' . $role_id;
643                    $this->db->sql_query($sql);
644                    $role_name = $this->db->sql_fetchfield('role_name');
645                    if (!$role_name)
646                    {
647                        throw new \phpbb\db\migration\exception('ROLE_ASSIGNED_NOT_EXIST', $name, $role_id);
648                    }
649
650                    $this->permission_unset($role_name, $auth_option, 'role');
651                    return;
652                }
653
654                $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
655                    WHERE ' . $this->db->sql_in_set('auth_option_id', $to_remove);
656                $this->db->sql_query($sql);
657            break;
658        }
659
660        $this->auth->acl_clear_prefetch();
661    }
662
663    /**
664    * {@inheritdoc}
665    */
666    public function reverse()
667    {
668        $arguments = func_get_args();
669        $original_call = array_shift($arguments);
670
671        $call = false;
672        switch ($original_call)
673        {
674            case 'add':
675                $call = 'remove';
676            break;
677
678            case 'remove':
679                $call = 'add';
680            break;
681
682            case 'permission_set':
683                $call = 'permission_unset';
684            break;
685
686            case 'permission_unset':
687                $call = 'permission_set';
688            break;
689
690            case 'role_add':
691                $call = 'role_remove';
692            break;
693
694            case 'role_remove':
695                $call = 'role_add';
696            break;
697
698            case 'role_update':
699                // Set to the original value if the current value is what we compared to originally
700                $arguments = array(
701                    $arguments[1],
702                    $arguments[0],
703                );
704            break;
705
706            case 'reverse':
707                // Reversing a reverse is just the call itself
708                $call = array_shift($arguments);
709            break;
710        }
711
712        return $call ? call_user_func_array(array(&$this, $call), $arguments) : null;
713    }
714}