Completed
Push — master ( 51a843...7b3f43 )
by Richard
05:32
created

Relation   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 129
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 8

Test Coverage

Coverage 85.87%

Importance

Changes 13
Bugs 0 Features 1
Metric Value
c 13
b 0
f 1
dl 0
loc 129
wmc 13
lcom 0
cbo 8
ccs 79
cts 92
cp 0.8587
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A fluid_interface() 0 7 2
A check_arguments() 0 11 3
A pprint() 0 3 1
B compile() 0 71 4
A insert_relation_into() 0 13 3
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 as Def;
14
use Lechimp\Dicto\Indexer\Insert;
15
use Lechimp\Dicto\Indexer\Location;
16
use Lechimp\Dicto\Analysis\Query;
17
use Lechimp\Dicto\Analysis\Violation;
18
use \Lechimp\Dicto\Variables\Variable;
19
20
/**
21
 * This is a rule that checks a relation between two entities
22
 * in the code.
23
 */
24
abstract class Relation extends Schema {
25
    /**
26
     * @inheritdoc
27
     */
28 146
    public function fluid_interface(Def\RT $rt, $name, $mode, array $arguments) {
29 146
        if (count($arguments) != 0) {
30
            throw new \InvalidArgumentException(
31
                "No arguments are allowed when using a relational rule schema.");
32
        }
33 146
        return new Def\Fluid\Relation($rt, $name, $mode, $this);
34
    }
35
36 188
    public function check_arguments(array $arguments) {
37 188
         if (count($arguments) != 1) {
38
            throw new \InvalidArgumentException(
39
                "One argument is required when using a relational rule schema.");
40
        }
41 188
       if (!($arguments[0] instanceof Variable)) {
42
            throw new \InvalidArgumentException(
43
                "Expected variable, got '".get_class($arguments[0])."' when using a relational schema.");
44
        }
45
46 188
    }
47
48
    /**
49
     * @inheritdoc
50
     */
51 3
    public function pprint(Rule $rule) {
52 3
        return $this->printable_name()." ".$rule->argument(0)->name();
53
    }
54
55
    /**
56
     * @inheritdoc
57
     */
58 37
    public function compile(Query $query, Rule $rule) {
59 37
        $builder = $query->builder();
60 37
        $b = $builder->expr();
61 37
        $mode = $rule->mode();
62 37
        $entity = $rule->checked_on();
63 37
        $reference = $rule->argument(0);
64 37
        if ($mode == Rule::MODE_CANNOT || $mode == Rule::MODE_ONLY_CAN) {
65
            return $builder
66 29
                ->select
67 29
                    ( "rel.entity_id as entity_id"
68 29
                    , "rel.reference_id as reference_id"
69 29
                    , "r.file as file"
70 29
                    , "r.line as line"
71 29
                    , "src.source as source"
72 29
                    )
73 29
                ->from($query->relations_table(), "rel")
74 29
                ->innerJoin("rel", $query->entity_table(), "e", "rel.entity_id = e.id")
75 29
                ->innerJoin("rel", $query->reference_table(), "r", "rel.reference_id = r.id")
76
                ->innerJoin
77 29
                    ( "rel", $query->source_file_table(), "src"
78 29
                    , $b->andX
79 29
                        ( $b->eq("src.line", "r.line")
80 29
                        , $b->eq("src.name", "r.file")
81 29
                        )
82 29
                    )
83
                ->where
84 29
                    ( $b->eq("rel.name", $b->literal($this->name()))
85 29
                    , $query->compile_var("e", $entity)
86 29
                    , $query->compile_var("r", $reference)
87 29
                    )
88 29
                ->execute();
89
        }
90 8
        if ($mode == Rule::MODE_MUST) {
91
            return $builder
92 8
                ->select
93 8
                    ( "e.id as entity_id"
94 8
                    , "e.file as file"
95 8
                    , "e.start_line as line"
96 8
                    , "src.source as source"
97 8
                    )
98 8
                ->from($query->entity_table(), "e")
99
                ->leftJoin
100 8
                    ("e", $query->relations_table(), "rel"
101 8
                    , $b->andX
102 8
                        ( $b->eq("rel.name", $b->literal($this->name()))
103 8
                        , $b->eq("rel.entity_id", "e.id")
104 8
                        )
105 8
                    )
106
                ->leftJoin
107 8
                    ("rel", $query->reference_table(), "r"
108 8
                    , $b->andX
109 8
                        ( $b->eq("rel.reference_id", "r.id")
110 8
                        , $query->compile_var("r", $reference)
111 8
                        )
112 8
                    )
113
                ->innerJoin
114 8
                    ( "e", $query->source_file_table(), "src"
115 8
                    , $b->andX
116 8
                        ( $b->eq("src.line", "e.start_line")
117 8
                        , $b->eq("src.name", "e.file")
118 8
                        )
119 8
                    )
120
121
                ->where
122 8
                    ( $query->compile_var("e", $entity)
123 8
                    , $b->isNull("r.id")
124 8
                    )
125 8
                ->execute();
126
        }
127
        throw new \LogicException("Unknown rule mode: '$mode'");
128
    }
129
130
    /**
131
     * Insert this relation somewhere, where it is recorded for all
132
     * entities that the current location is in, except for files.
133
     *
134
     * @param   Insert      $insert
135
     * @param   Location    $location
136
     * @param   int         $ref_id
137
     * @return  null
138
     */
139 17
    protected function insert_relation_into(Insert $insert, Location $location, $ref_id) {
140 17
        assert('is_int($ref_id)');
141 17
        foreach ($location->in_entities() as $entity) {
142 17
            if ($entity[0] == Variable::FILE_TYPE) {
143 17
                continue;
144
            }
145 17
            $insert->relation
146 17
                ( $this->name()
147 17
                , $entity[1]
148 17
                , $ref_id
149 17
                );
150 17
        }
151 17
    }
152
}
153