Guard   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 101
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 90.48%

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 0
dl 0
loc 101
ccs 19
cts 21
cp 0.9048
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A authorized() 0 20 5
A can_edit_others_posts() 0 3 2
A user_logged_in() 0 3 2
A can_manage_options() 0 3 2
A set_defaults() 0 4 1
A make_error() 0 3 1
1
<?php
2
namespace Intraxia\Jaxion\Http;
3
4
use Intraxia\Jaxion\Contract\Http\Guard as GuardContract;
5
use Intraxia\Jaxion\Utility\Str;
6
use WP_Error;
7
8
/**
9
 * Class Guard
10
 *
11
 * Protects routes by validating that
12
 * the accessing user has required permissions.
13
 *
14
 * @package Intraxia\Jaxion
15
 * @subpackage Http
16
 */
17
class Guard implements GuardContract {
18
	/**
19
	 * Default options.
20
	 *
21
	 * @var array
22
	 */
23
	protected $defaults = array(
24
		'rule'     => 'public',
25
		'callback' => null,
26
	);
27
28
	/**
29
	 * Guard options.
30
	 *
31
	 * @var array
32
	 */
33
	protected $options;
34
35
	/**
36
	 * Instantiate a new Guard with provided options.
37
	 *
38
	 * @param array $options
39
	 */
40 21
	public function __construct( array $options = array() ) {
41 21
		$this->options = $this->set_defaults( $options );
42 21
	}
43
44
	/**
45
	 * Validates whether the current user is authorized.
46
	 *
47
	 * @return true|WP_Error
48
	 */
49 21
	public function authorized() {
50
		// if the rule is public, always authorized
51 21
		if ( 'public' === $this->options['rule'] ) {
52 3
			return true;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return true; (boolean) is incompatible with the return type documented by Intraxia\Jaxion\Http\Guard::authorized of type Intraxia\Jaxion\Http\true|WP_Error.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
53
		}
54
55
		// enable passing in callback
56 18
		if ( 'callback' === $this->options['rule'] && is_callable( $this->options['callback'] ) ) {
57 3
			return call_user_func( $this->options['callback'] );
58
		}
59
60
		// map rule to method
61 15
		if ( method_exists( $this, $method = $this->options['rule'] ) ) {
62 12
			return $this->{$method}();
63
		}
64
65
		// disable in rule is misconfigused
66
		// @todo set up internal translations
67 3
		return new WP_Error( 'misconfigured_guard', __( 'Misconfigured guard rule', 'jaxion' ), [ 'status' => 500 ] );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \WP_Error('mi...rray('status' => 500)); (WP_Error) is incompatible with the return type declared by the interface Intraxia\Jaxion\Contract\Http\Guard::authorized of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
68
	}
69
70
	/**
71
	 * Checks whether the current user can edit other's posts.
72
	 *
73
	 * @return bool|WP_Error
74
	 */
75 6
	protected function can_edit_others_posts() {
76 6
		return current_user_can( 'edit_others_posts' ) ?: $this->make_error();
77
	}
78
79
	/**
80
	 * Checks whether the user is currently logged in.
81
	 *
82
	 * @return bool|WP_Error
83
	 */
84 6
	protected function user_logged_in() {
85 6
		return is_user_logged_in() ?: $this->make_error();
86
	}
87
88
	/**
89
	 * Checks whether the user can manage the site options.
90
	 *
91
	 * @return bool|WP_Error
92
	 */
93
	protected function can_manage_options() {
94
		return current_user_can( 'manage_options' ) ?: $this->make_error();
95
	}
96
97
	/**
98
	 * Sets the default params for the Guard options.
99
	 *
100
	 * @param array $options
101
	 *
102
	 * @return array
103
	 */
104 21
	protected function set_defaults( $options ) {
105
		// these are the valid options
106 21
		return wp_parse_args( $options, $this->defaults );
107
	}
108
109
	/**
110
	 * Create an unauthorized error object.
111
	 *
112
	 * @return WP_Error
113
	 */
114 6
	protected function make_error() {
115 6
		return new WP_Error( 'unauthorized', __( 'Unauthorized user', 'jaxion' ), array( 'status' => 401 ) );
116
	}
117
}
118