Completed
Push — master ( 1df032...2ea341 )
by Robbie
11s queued 11s
created

ControllerExtension   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 124
Duplicated Lines 40.32 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 5
dl 50
loc 124
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A onBeforeInit() 17 17 3
B onAfterInit() 14 23 5
C beforeCallActionHandler() 0 32 8
A afterCallActionHandler() 19 19 3
A clearBuffer() 0 11 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace LeKoala\DebugBar\Extension;
4
5
use LeKoala\DebugBar\Collector\SilverStripeCollector;
6
use LeKoala\DebugBar\DebugBar;
7
use SilverStripe\Control\Controller;
8
use SilverStripe\Core\Extension;
9
use SilverStripe\Security\Security;
10
11
/**
12
 * A controller extension to log times and render the Debug Bar
13
 */
14
class ControllerExtension extends Extension
15
{
16 View Code Duplication
    public function onBeforeInit()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
17
    {
18
        DebugBar::withDebugBar(function (\DebugBar\DebugBar $debugbar) {
19
            // We must set the current controller when it's available and before it's pushed out of stack
20
            $debugbar->getCollector('silverstripe')->setController(Controller::curr());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface DebugBar\DataCollector\DataCollectorInterface as the method setController() does only exist in the following implementations of said interface: LeKoala\DebugBar\Collector\SilverStripeCollector.

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...
21
22
            /** @var $timeData DebugBar\DataCollector\TimeDataCollector */
23
            $timeData = $debugbar->getCollector('time');
24
            if (!$timeData) {
25
                return;
26
            }
27
            if ($timeData->hasStartedMeasure('pre_request')) {
28
                $timeData->stopMeasure("pre_request");
29
            }
30
            $timeData->startMeasure("init", get_class($this->owner) . ' init');
31
        });
32
    }
33
34
    public function onAfterInit()
35
    {
36
        // On Security, onAfterInit is called before init() in your Page method
37
        // jQuery is most likely not included yet
38
        if (!$this->owner instanceof Security) {
39
            DebugBar::includeRequirements();
40
        }
41
42 View Code Duplication
        DebugBar::withDebugBar(function (\DebugBar\DebugBar $debugbar) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
43
            /** @var $timeData DebugBar\DataCollector\TimeDataCollector */
44
            $timeData = $debugbar->getCollector('time');
45
            if (!$timeData) {
46
                return;
47
            }
48
            if ($timeData->hasStartedMeasure("cms_init")) {
49
                $timeData->stopMeasure("cms_init");
50
            }
51
            if ($timeData->hasStartedMeasure("init")) {
52
                $timeData->stopMeasure("init");
53
            }
54
            $timeData->startMeasure("handle", get_class($this->owner) . ' handle request');
55
        });
56
    }
57
58
    /**
59
     * Due to a bug, this could be called twice before 4.0,
60
     * see https://github.com/silverstripe/silverstripe-framework/pull/5173
61
     *
62
     * @param HTTPRequest $request
63
     * @param string $action
64
     */
65
    public function beforeCallActionHandler($request, $action)
66
    {
67
        // This could be called twice
68
        if ($this->owner->beforeCallActionHandlerCalled) {
69
            return;
70
        }
71
72
        // If we don't have an action, getViewer will be called immediatly
73
        // If we have custom routes, request action is different than action
74
        $allParams     = $request->allParams();
75
        $requestAction = null;
76
        if (!empty($allParams['Action'])) {
77
            $requestAction = $allParams['Action'];
78
        }
79
        if (!$this->owner->hasMethod($action) || ($requestAction && $requestAction != $action)) {
80
            self::clearBuffer();
81
        }
82
83
        DebugBar::withDebugBar(function (\DebugBar\DebugBar $debugBar) use ($action) {
84
            /** @var $timeData DebugBar\DataCollector\TimeDataCollector */
85
            $timeData = $debugBar->getCollector('time');
86
            if (!$timeData) {
87
                return;
88
            }
89
            if ($timeData->hasStartedMeasure("handle")) {
90
                $timeData->stopMeasure("handle");
91
            }
92
            $timeData->startMeasure("action", get_class($this->owner) . " action $action");
93
        });
94
95
        $this->owner->beforeCallActionHandlerCalled = true;
96
    }
97
98
    /**
99
     * Due to a bug, this is not always called before 4.0,
100
     * see https://github.com/silverstripe/silverstripe-framework/pull/5173
101
     *
102
     * @param HTTPRequest $request
103
     * @param string $action
104
     * @param mixed $result (only in v4.0)
105
     */
106 View Code Duplication
    public function afterCallActionHandler($request, $action, $result)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $result is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
107
    {
108
        self::clearBuffer();
109
110
        DebugBar::withDebugBar(function (\DebugBar\DebugBar $debugBar) use ($action) {
111
            /** @var $timeData DebugBar\DataCollector\TimeDataCollector */
112
            $timeData = $debugBar->getCollector('time');
113
            if (!$timeData) {
114
                return;
115
            }
116
            if ($timeData->hasStartedMeasure("action")) {
117
                $timeData->stopMeasure("action");
118
            }
119
            $timeData->startMeasure(
120
                "after_action",
121
                get_class($this->owner) . " after action $action"
122
            );
123
        });
124
    }
125
126
    protected static function clearBuffer()
0 ignored issues
show
Coding Style introduced by
clearBuffer uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
127
    {
128
        if (!DebugBar::$bufferingEnabled) {
129
            return;
130
        }
131
        $buffer = ob_get_clean();
132
        if (!empty($buffer)) {
133
            unset($_REQUEST['debug_request']); // Disable further messages that we can't intercept
134
            SilverStripeCollector::setDebugData($buffer);
135
        }
136
    }
137
}
138