Controller::applyFilters()   D
last analyzed

Complexity

Conditions 10
Paths 10

Size

Total Lines 34
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
c 0
b 0
f 0
rs 4.8196
cc 10
eloc 17
nc 10
nop 4

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php /** MicroController */
2
3
namespace Micro\Mvc\Controllers;
4
5
use Micro\Base\Exception;
6
use Micro\Mvc\Module;
7
use Micro\Web\ResponseInjector;
8
use Psr\Http\Message\ResponseInterface;
9
10
/**
11
 * Class Controller
12
 *
13
 * @author Oleg Lunegov <[email protected]>
14
 * @link https://github.com/linpax/microphp-framework
15
 * @copyright Copyright (c) 2013 Oleg Lunegov
16
 * @license https://github.com/linpax/microphp-framework/blob/master/LICENSE
17
 * @package Micro
18
 * @subpackage Mvc\Controllers
19
 * @version 1.0
20
 * @since 1.0
21
 * @abstract
22
 */
23
abstract class Controller implements IController
24
{
25
    /** @var Module $module */
26
    public $module;
27
    /** @var ResponseInterface $response Response HTTP data */
28
    public $response;
29
30
31
    /**
32
     * Constructor controller
33
     *
34
     * @access public
35
     *
36
     * @param string $modules
37
     *
38
     * @result void
39
     * @throws Exception
40
     */
41
    public function __construct($modules = '')
42
    {
43
        // if module defined
44
        if ($modules) {
45
            $className = '\\App'.$modules.'\\'.ucfirst(basename(str_replace('\\', '/', $modules))).'Module';
46
47
            if (class_exists($className) && is_subclass_of($className, '\Micro\Mvc\Module')) {
48
                $this->module = new $className();
49
            }
50
        }
51
52
        if (!$this->response = (new ResponseInjector)->build()) {
53
            throw new Exception('Component `response` not configured');
54
        }
55
    }
56
57
    /**
58
     * Apply filters
59
     *
60
     * @access public
61
     *
62
     * @param string $action current action name
63
     * @param bool $isPre is pre or post
64
     * @param array $filters defined filters
65
     * @param string $data data to parse
66
     *
67
     * @return null|string
68
     * @throws Exception|\InvalidArgumentException
69
     */
70
    public function applyFilters($action, $isPre = true, array $filters = [], $data = null)
71
    {
72
        if (!$filters) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filters of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
73
            return $data;
74
        }
75
76
        foreach ($filters as $filter) {
77
            if (empty($filter['class']) || !class_exists($filter['class'])) {
78
                continue;
79
            }
80
81
            if (empty($filter['actions']) || !in_array($action, $filter['actions'], true)) {
82
                continue;
83
            }
84
85
            /** @var \Micro\Filter\IFilter $_filter */
86
            $_filter = new $filter['class']($action);
87
            /** @var ResponseInterface $response */
88
            $response = $isPre ? $_filter->pre($filter) : $_filter->post($filter + ['data' => $data]);
0 ignored issues
show
Bug Compatibility introduced by
The expression $isPre ? $_filter->pre($...rray('data' => $data)); of type mixed adds the type Psr\Http\Message\ResponseInterface to the return on line 102 which is incompatible with the return type declared by the interface Micro\Mvc\Controllers\IController::applyFilters of type null|string.
Loading history...
89
90
            if (!$response) {
91
                if (!empty($_filter->result['redirect'])) {
0 ignored issues
show
Bug introduced by
Accessing result on the interface Micro\Filter\IFilter suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
92
                    /** @var ResponseInterface $redirect */
93
                    $redirect = (new ResponseInjector)->build();
94
                    return $redirect->withHeader('Location', $_filter->result['redirect']);
0 ignored issues
show
Bug introduced by
Accessing result on the interface Micro\Filter\IFilter suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
Bug Best Practice introduced by
The return type of return $redirect->withHe...r->result['redirect']); (Psr\Http\Message\ResponseInterface) is incompatible with the return type declared by the interface Micro\Mvc\Controllers\IController::applyFilters of type null|string.

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...
95
                }
96
                throw new Exception($_filter->result['message']);
0 ignored issues
show
Bug introduced by
Accessing result on the interface Micro\Filter\IFilter suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
97
            }
98
            /** @noinspection CallableParameterUseCaseInTypeContextInspection */
99
            $data = $response;
100
        }
101
102
        return $data;
103
    }
104
105
    /**
106
     * Get action class by name
107
     *
108
     * @access public
109
     *
110
     * @param string $name action name
111
     *
112
     * @return bool
113
     */
114
    public function getActionClassByName($name)
115
    {
116
        if (method_exists($this, 'actions')) {
117
            $actions = $this->actions();
0 ignored issues
show
Bug introduced by
The method actions() does not exist on Micro\Mvc\Controllers\Controller. Did you maybe mean action()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
118
119
            if (
120
                !empty($actions[$name]) &&
121
                class_exists($actions[$name]) &&
122
                is_subclass_of($actions[$name], '\Micro\Mvc\Action')
123
            ) {
124
                return $actions[$name];
125
            }
126
        }
127
128
        return false;
129
    }
130
}
131