Passed
Push — master ( b26d44...e096d1 )
by
unknown
01:42
created

Route   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 13
c 1
b 0
f 0
lcom 1
cbo 7
dl 0
loc 119
ccs 0
cts 42
cp 0
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 21 5
A condition() 0 17 3
A satisfied() 0 6 2
A handle() 0 6 1
A rewrite() 0 15 2
1
<?php
2
3
namespace CarbonFramework\Routing;
4
5
use Closure;
6
use ReflectionClass;
7
use Exception;
8
use CarbonFramework\Framework;
9
use CarbonFramework\Request;
10
use CarbonFramework\Routing\Conditions\ConditionInterface;
11
use CarbonFramework\Routing\Conditions\Url as UrlCondition;
12
use CarbonFramework\Routing\Conditions\Custom as CustomCondition;
13
use CarbonFramework\Routing\Middleware\HasMiddlewareTrait;
14
15
/**
16
 * Represent a route
17
 */
18
class Route implements RouteInterface {
19
	use HasMiddlewareTrait;
20
21
	/**
22
	 * Allowed methods
23
	 *
24
	 * @var string[]
25
	 */
26
	protected $methods = [];
27
28
	/**
29
	 * Route target
30
	 *
31
	 * @var ConditionInterface
32
	 */
33
	protected $target = null;
34
35
	/**
36
	 * Route handler
37
	 *
38
	 * @var Handler|null
39
	 */
40
	protected $handler = null;
41
42
	/**
43
	 * Constructor
44
	 *
45
	 * @param string[]        $methods
46
	 * @param mixed           $target
47
	 * @param string|\Closure $handler
48
	 */
49
	public function __construct( $methods, $target, $handler ) {
50
		if ( is_string( $target ) ) {
51
			$target = new UrlCondition( $target );
52
		}
53
54
		if ( is_a( $target, Closure::class ) ) {
55
			$target = new CustomCondition( $target );
56
		}
57
58
		if ( is_array( $target ) ) {
59
			$target = $this->condition( $target );
60
		}
61
62
		if ( ! is_a( $target, ConditionInterface::class ) ) {
63
			throw new Exception( 'Route target is not a valid route string or condition.' );
64
		}
65
66
		$this->methods = $methods;
67
		$this->target = $target;
68
		$this->handler = new Handler( $handler );
69
	}
70
71
	/**
72
	 * Create and return a new condition
73
	 *
74
	 * @param  array              $options
75
	 * @return ConditionInterface
76
	 */
77
	protected function condition( $options ) {
78
		if ( count( $options ) === 0 ) {
79
			throw new Exception( 'No condition type specified.' );
80
		}
81
82
		$condition_type = $options[0];
83
		$arguments = array_slice( $options, 1 );
84
85
		$condition_class = Framework::resolve( 'framework.routing.conditions.' . $condition_type );
86
		if ( $condition_class === null ) {
87
			throw new Exception( 'Unknown condition type specified: ' . $condition_type );
88
		}
89
90
		$reflection = new ReflectionClass( $condition_class );
91
		$condition = $reflection->newInstanceArgs( $arguments );
92
		return $condition;
93
	}
94
95
	/**
96
	 * {@inheritDoc}
97
	 */
98
	public function satisfied( Request $request ) {
99
		if ( ! in_array( $request->getMethod(), $this->methods) ) {
100
			return false;
101
		}
102
		return $this->target->satisfied( $request );
103
	}
104
105
	/**
106
	 * {@inheritDoc}
107
	 */
108
	public function handle( Request $request ) {
109
		$arguments = array_merge( [$request], $this->target->getArguments( $request ) );
110
		return $this->executeMiddleware( $this->getMiddleware(), $request, function() use ( $arguments ) {
111
			return call_user_func_array( [$this->handler, 'execute'], $arguments );
112
		} );
113
	}
114
115
	/**
116
	 * Add a rewrite rule to WordPress for url-based routes
117
	 *
118
	 * @param  string $rewrite_to
119
	 * @return RouteInterface
120
	 */
121
	public function rewrite( $rewrite_to ) {
122
		if ( ! is_a( $this->target, UrlCondition::class ) ) {
123
			throw new Exception( 'Only routes with url targets can add rewrite rules.' );
124
		}
125
126
		$regex = $this->target->getValidationRegex( $this->target->getUrl(), false );
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface CarbonFramework\Routing\...ions\ConditionInterface as the method getUrl() does only exist in the following implementations of said interface: CarbonFramework\Routing\Conditions\Url.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
Bug introduced by
It seems like you code against a concrete implementation and not the interface CarbonFramework\Routing\...ions\ConditionInterface as the method getValidationRegex() does only exist in the following implementations of said interface: CarbonFramework\Routing\Conditions\Url.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
127
		$regex = preg_replace( '~^\^/~', '^', $regex ); // rewrite rules require NO leading slash
128
129
		add_filter( 'carbon_framework_routing_rewrite_rules', function( $rules ) use ( $regex, $rewrite_to ) {
130
			$rules[ $regex ] = $rewrite_to;
131
			return $rules;
132
		} );
133
134
		return $this;
135
	}
136
}
137