ComponentStateTrait::setState()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace WebinoViewLib\Component;
4
5
use WebinoAppLib\Event\AppEvent;
6
use WebinoAppLib\Event\DispatchEvent;
7
use WebinoAppLib\Feature\HttpListener;
8
use WebinoAppLib\Service\Initializer\RoutingAwareTrait;
9
use WebinoConfigLib\Feature\FeatureInterface;
10
use WebinoDomLib\Dom\Config\SpecConfigAggregateInterface;
11
use WebinoDomLib\Dom\NodeInterface;
12
use WebinoDomLib\Event\RenderEvent;
13
use WebinoEventLib\ListenerAggregateTrait;
14
use WebinoViewLib\Feature\NodeView;
15
use WebinoViewLib\Feature\ViewListener;
16
use Zend\EventManager\ListenerAggregateInterface;
17
use Zend\Stdlib\CallbackHandler;
18
19
/**
20
 * Class ComponentStateTrait
21
 * @TODO concept
22
 */
23
trait ComponentStateTrait
24
{
25
    use RoutingAwareTrait;
26
27
    /**
28
     * @var \stdClass
29
     */
30
    private $state;
31
32
    /**
33
     * @TODO concept
34
     * @var NodeInterface[]
35
     */
36
    private $nodes = [];
37
38
    /**
39
     * @return RenderEvent
40
     */
41
    abstract protected function getRenderEvent();
42
43
    /**
44
     * TODO concept
45
     * @param DispatchEvent $event
46
     */
47
    public function onDispatchState(DispatchEvent $event)
48
    {
49
        // TODO resolve state
50
        $state = $event->getRequest()->getQuery()->counter[$this->getIndex()];
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Zend\Stdlib\RequestInterface as the method getQuery() does only exist in the following implementations of said interface: Zend\Http\PhpEnvironment\Request, Zend\Http\Request.

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 getIndex() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
51
52
        empty($state) or $this->mergeState($state);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
53
    }
54
55
    /**
56
     * @TODO concept
57
     * @param string $name Node name
58
     * @param string|\Closure $method
59
     * @return $this
60
     */
61
    public function onStateChange($name, $method)
62
    {
63
        // TODO create href concept
64
        $clone = clone $this;
65
66
        // TODO refactor
67
        if (is_string($method)) {
68
            $clone->$method();
69
        } elseif ($method instanceof \Closure) {
70
            call_user_func($method->bindTo($clone));
71
        } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
72
            // TODO invalid argument exception
73
        }
74
75
        // TODO common
76
        $query = ['counter' => [$this->getIndex() => (array) $clone->getState()]];
0 ignored issues
show
Bug introduced by
It seems like getIndex() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
77
78
        $href = $this->getRouter()->url()->setOption('query', $query);
0 ignored issues
show
Bug introduced by
The call to url() misses a required argument $name.

This check looks for function calls that miss required arguments.

Loading history...
79
80
        // TODO concept
81
        $node = $this->getRenderEvent()->getNode($name);
82
        $node->setAttribute('href', $href);
83
84
        return $this;
85
    }
86
87
    /**
88
     * @return \stdClass
89
     */
90
    public function getState()
91
    {
92
        if (null === $this->state) {
93
            $this->state = new \stdClass;
94
            $this->initState($this->state);
95
        }
96
        return $this->state;
97
    }
98
99
    /**
100
     * @param \stdClass $state
101
     * @return $this
102
     */
103
    public function setState(\stdClass $state)
104
    {
105
        $this->state = $state;
106
        return $this;
107
    }
108
109
    /**
110
     * @param array|\stdClass $state
111
     * @return $this
112
     */
113
    public function mergeState($state)
114
    {
115
        $this->state = (object) array_replace_recursive((array) $this->getState(), (array) $state);
116
        return $this;
117
    }
118
119
    /**
120
     * @param \stdClass $state
121
     */
122
    public function initState(\stdClass $state)
0 ignored issues
show
Unused Code introduced by
The parameter $state 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...
123
    {
124
    }
125
126
    /**
127
     * @param string $name
128
     * @return ComponentNode
129
     */
130
//    protected function getNode($name)
0 ignored issues
show
Unused Code Comprehensibility introduced by
61% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
131
//    {
132
//        if (empty($this->nodes[$name])) {
133
//            $this->nodes[$name] = new ComponentNode($this->getRenderEvent()->getNode($name), $this);
134
//        }
135
//        return $this->nodes[$name];
136
//    }
137
138
    /**
139
     * @TODO concept
140
     */
141
    public function __clone()
142
    {
143
        $this->state = clone $this->getState();
144
    }
145
}
146