Completed
Push — master ( 389cae...29b40e )
by Richard
06:27
created

Rule::pprint()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5.1576

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 13
ccs 7
cts 12
cp 0.5833
rs 9.2
nc 4
cc 4
eloc 11
nop 0
crap 5.1576
1
<?php
2
/******************************************************************************
3
 * An implementation of dicto (scg.unibe.ch/dicto) in and for PHP.
4
 * 
5
 * Copyright (c) 2016 Richard Klees <[email protected]>
6
 *
7
 * This software is licensed under The MIT License. You should have received 
8
 * a copy of the licence along with the code.
9
 */
10
11
namespace Lechimp\Dicto\Rules;
12
13
use Lechimp\Dicto\Definition\Definition;
14
use Lechimp\Dicto\Variables\Variable;
15
use Lechimp\Dicto\Variables\ButNot;
16
use Lechimp\Dicto\Variables\Everything;
17
use Lechimp\Dicto\Analysis\Query;
18
use Doctrine\DBAL\Driver\Statement;
19
20
class Rule extends Definition {
21
    const MODE_CANNOT   = "CANNOT";
22
    const MODE_MUST     = "MUST";
23
    const MODE_ONLY_CAN = "ONLY_CAN";
24
25
    static $modes = array
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $modes.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
26
        ( Rule::MODE_CANNOT
27
        , Rule::MODE_MUST
28
        , Rule::MODE_ONLY_CAN
29
        );
30
31
    /**
32
     * @var string
33
     */
34
    private $mode;
35
36
    /**
37
     * @var Vars\Variable
38
     */
39
    private $subject;
40
41
    /**
42
     * @var R\Schema
43
     */
44
    private $schema;
45
46
    /**
47
     * @var array
48
     */
49
    private $arguments;
50
51 239
    public function __construct($mode, Variable $subject, Schema $schema, array $arguments) {
52 239
        assert('in_array($mode, self::$modes)');
53 239
        $schema->check_arguments($arguments);
54 238
        $this->mode = $mode;
55 238
        $this->subject = $subject;
0 ignored issues
show
Documentation Bug introduced by
It seems like $subject of type object<Lechimp\Dicto\Variables\Variable> is incompatible with the declared type object<Lechimp\Dicto\Rules\Vars\Variable> of property $subject.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
56 238
        $this->schema = $schema;
0 ignored issues
show
Documentation Bug introduced by
It seems like $schema of type object<Lechimp\Dicto\Rules\Schema> is incompatible with the declared type object<Lechimp\Dicto\Rules\R\Schema> of property $schema.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
57 238
        $this->arguments = $arguments;
58 238
    }
59
60
    public function explain($explanation) {
61
        $r = new Rule($this->mode, $this->subject, $this->schema, $this->arguments);
62
        $r->setExplanation($r);
63
        return $r;
64
    }
65
66
    /**
67
     * @return string
68
     */
69 51
    public function mode() {
70 51
        return $this->mode;
71
    }
72
73
    /**
74
     * Definition of the entities this rule was defined for.
75
     *
76
     * @return  Vars\Variable
77
     */
78 54
    public function subject() {
79 54
        return $this->subject;
80
    }
81
82
    /**
83
     * Definition of the entities this rule needs to be checked on.
84
     *
85
     * In the default case the rule needs to be checked on every entity that
86
     * is not subject() if the mode is MODE_ONLY_CAN, as this really says
87
     * something about the other entities.
88
     *
89
     * @return  Vars\Variable
90
     */
91 48
    public function checked_on() {
92 48
        if ($this->mode() == self::MODE_ONLY_CAN) {
93
            return new ButNot
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \Lechimp\Dict...G'), $this->subject()); (Lechimp\Dicto\Variables\ButNot) is incompatible with the return type documented by Lechimp\Dicto\Rules\Rule::checked_on of type Lechimp\Dicto\Rules\Vars\Variable.

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...
94 12
                ( "ONLY_CAN_INVERSION"
95 12
                , new Everything("EVERYTHING")
96 12
                , $this->subject()
97 12
                );
98
        }
99 36
        return $this->subject();
100
    }
101
102
    /**
103
     * Get all variables referenced by the rule.
104
     *
105
     * @return  Vars\Variable[]
106
     */
107 3
    public function variables() {
108 3
        $vars = array($this->subject());
109 3
        foreach ($this->arguments as $argument) {
110 3
            if ($argument instanceof Variable) {
111 2
                $vars[] = $argument;
112 2
            }
113 3
        }
114 3
        return $vars;
115
    }
116
117
    /**
118
     * Get the schema that was used for the rule.
119
     *
120
     * @return R\Schema
121
     */
122 3
    public function schema() {
123 3
        return $this->schema;
124
    }
125
126
    /**
127
     * Pretty print the rule.
128
     *
129
     * @return string
130
     */
131 3
    public function pprint() {
132 3
        $name = $this->subject()->name();
133 3
        switch ($this->mode()) {
134 3
            case self::MODE_CANNOT:
135 2
                return "$name cannot ".$this->schema()->pprint($this);
136 1
            case self::MODE_MUST:
137 1
                return "$name must ".$this->schema()->pprint($this);
138
            case self::MODE_ONLY_CAN:
139
                return "only $name can ".$this->schema()->pprint($this);
140
            default:
141
                throw new \Exception("Unknown rule mode '".$this->mode()."'");
142
        }
143
    }
144
145
    /**
146
     * Compile the rule to SQL.
147
     *
148
     * @param   Query       $query
149
     * @return Statement
150
     */
151 46
    public function compile(Query $query) {
152 46
        return $this->schema->compile($query, $this);
153
    }
154
155
    /**
156
     * Get the argument at the index.
157
     *
158
     * @throws  \OutOfRangeException
159
     * @param   int     $index
160
     * @return  mixed 
161
     */
162 49
    public function argument($index) {
163 49
        if ($index < 0 || $index >= count($this->arguments)) {
164
            throw new \OutOfRangeException("'$index' out of range.");
165
        }
166 49
        return $this->arguments[$index];
167
    }
168
}
169
170