Completed
Push — master ( c9b7ea...c1fd74 )
by Mihail
02:38
created

Manager::addMessage()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
rs 8.8571
cc 5
eloc 9
nc 6
nop 2
1
<?php
2
3
namespace Ffcms\Core\Debug;
4
5
use Cassandra\Exception;
6
use DebugBar\DataCollector\ConfigCollector;
7
use DebugBar\DebugBarException;
8
use DebugBar\StandardDebugBar;
9
use Ffcms\Core\App;
10
use Ffcms\Core\Helper\Type\Any;
11
use Ffcms\Core\Helper\Type\Obj;
12
13
/**
14
 * Class Debug. Provide methods of display information about debug and collected data in debug bar
15
 * @package Ffcms\Core
16
 */
17
class Manager
18
{
19
20
    public $bar;
21
    public $render;
22
23
    /**
24
     * Manager constructor. Construct debug manager - build debug bar, javascripts and initialize config
25
     */
26
    public function __construct()
27
    {
28
        $this->bar = new StandardDebugBar();
29
        $this->render = $this->bar->getJavascriptRenderer();
30
        try {
31
            $this->bar->addCollector(new ConfigCollector());
32
        } catch (\Exception $oe){}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
33
    }
34
35
    /**
36
     * Render debug bar header
37
     * @return string
38
     */
39
    public function renderHead()
40
    {
41
        return $this->render->renderHead();
42
    }
43
44
    /**
45
     * Render debug bar code
46
     * @return string
47
     * @throws \DebugBar\DebugBarException
48
     */
49
    public function renderOut()
50
    {
51
        if (!$this->bar->hasCollector('queries')) {
52
            $timeCollector = null;
53
            $log = App::$Database->connection()->getQueryLog();
54
            if ($this->bar->hasCollector('time')) {
55
                $timeCollector = $this->bar->getCollector('time');
56
            }
57
            $queryCollector = new LaravelDatabaseCollector($timeCollector, $log);
0 ignored issues
show
Bug introduced by
It seems like $timeCollector defined by $this->bar->getCollector('time') on line 55 can also be of type object<DebugBar\DataColl...DataCollectorInterface>; however, Ffcms\Core\Debug\Laravel...ollector::__construct() does only seem to accept null|object<DebugBar\Dat...ctor\TimeDataCollector>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
58
            $this->bar->addCollector($queryCollector);
59
        }
60
        return $this->render->render();
61
    }
62
63
    /**
64
     * Add exception into debug bar and stop execute
65
     * @param \Exception $e
66
     */
67
    public function addException($e)
68
    {
69
        if ($e instanceof \Exception) {
70
            try {
71
                $this->bar->getCollector('exceptions')->addException($e);
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 addException() does only exist in the following implementations of said interface: DebugBar\DataCollector\ExceptionsCollector.

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...
72
            } catch (\Exception $ie) {} // mute exceptions there
73
        }
74
    }
75
76
    /**
77
     * Add message into debug bar
78
     * @param string $m
79
     * @param string $type
80
     */
81
    public function addMessage($m, $type = 'info')
82
    {
83
        if (!Any::isStr($m) || !Any::isStr($type))
84
            return;
85
86
        $m = App::$Security->secureHtml($m);
87
        try {
88
            $mCollector = $this->bar->getCollector('messages');
89
90
            if (method_exists($mCollector, $type)) {
91
                $this->bar->getCollector('messages')->{$type}($m);
92
            }
93
        } catch (\Exception $e) {} // mute exceptions there
94
    }
95
96
    /**
97
     * Add message debug data to bar
98
     * @param mixed $data
99
     */
100
    public function vardump($data)
101
    {
102
        try {
103
            $this->bar->getCollector('messages')->info($data);
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 info() does only exist in the following implementations of said interface: DebugBar\Bridge\PropelCollector, DebugBar\Bridge\SlimCollector, DebugBar\Bridge\SwiftMailer\SwiftLogCollector, DebugBar\DataCollector\MessagesCollector.

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...
104
        } catch (\Exception $e) {}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
105
    }
106
107
    /**
108
     * Start timeline measure
109
     * @param string $key
110
     */
111
    public function startMeasure(string $key): void
112
    {
113
        $this->bar['time']->startMeasure($key);
114
    }
115
116
    /**
117
     * Stop timeline measure
118
     * @param string $key
119
     */
120
    public function stopMeasure(string $key): void
121
    {
122
        $this->bar['time']->stopMeasure($key);
123
    }
124
125
    /**
126
     * Check if debug bar is enabled. Method called before __construct() is initiated!!
127
     * @return bool
128
     */
129
    public static function isEnabled()
130
    {
131
        $property = App::$Properties->get('debug');
132
        // $_COOKIE used insted of symfony request, cuz debug initialize early
133
        return ($property['all'] === true || $_COOKIE[$property['cookie']['key']] === $property['cookie']['value']);
134
    }
135
}