Completed
Push — master ( f1c7a7...774eb2 )
by Richard
07:53 queued 02:10
created

CompilesVars::compile_var()   D

Complexity

Conditions 16
Paths 52

Size

Total Lines 104
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 51
CRAP Score 17.7698

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 104
ccs 51
cts 63
cp 0.8095
rs 4.8736
cc 16
eloc 56
nc 52
nop 3
crap 17.7698

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/******************************************************************************
3
 * An implementation of dicto (scg.unibe.ch/dicto) in and for PHP.
4
 *
5
 * Copyright (c) 2016, 2015 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\Analysis;
12
13
use Lechimp\Dicto\Variables as Vars;
14
use Lechimp\Dicto\Variables\Variable;
15
16
/**
17
 * Implementation for Query::compile_var.
18
 */
19
trait CompilesVars {
20
    /**
21
     * Get a builder to create queries.
22
     *
23
     * @return  QueryBuilder
24
     */
25
    abstract public function builder();
26
27
    /**
28
     * Compile a variable to an SQL statement over a named table.
29
     *
30
     * @param   string          $table_name
31
     * @param   Vars\Variable   $var
32
     * @param   bool            $negate
33
     * @return  string|CompositeExpression
34
     */ 
35 48
    public function compile_var($table_name, Vars\Variable $var, $negate = false) {
36 48
        $b = $this->builder()->expr();
37
        // Since SQL does not have a statement for negating while expressions,
38
        // we need to negate the single conditions in the expression, which
39
        // most often is the equality operator here.
40 48
        if (!$negate) {
41
            $eq_op = function($l, $r) use ($b) {
42 48
                return $b->eq($l, $r);
43 48
            };
44 48
        }
45
        else {
46
            $eq_op = function($l, $r) use ($b) {
47 18
                return $b->neq($l, $r);
48 18
            };
49
        }
50
51
        // sugar:
52
        $compile_left = function($negate = false) use ($table_name, $var) {
53 21
            return $this->compile_var($table_name, $var->left(), $negate);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Lechimp\Dicto\Variables\Variable as the method left() does only exist in the following sub-classes of Lechimp\Dicto\Variables\Variable: Lechimp\Dicto\Variables\AsWellAs, Lechimp\Dicto\Variables\ButNot. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
54 48
        };
55 48
        $compile_right = function($negate = false) use ($table_name, $var) {
56 21
            return $this->compile_var($table_name, $var->right(), $negate);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Lechimp\Dicto\Variables\Variable as the method right() does only exist in the following sub-classes of Lechimp\Dicto\Variables\Variable: Lechimp\Dicto\Variables\AsWellAs, Lechimp\Dicto\Variables\ButNot. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends 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 sub-classes 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 parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
57 48
        };
58
59
        // Pattern matching on variable type.
60
61 48
        if ($var instanceof Vars\AsWellAs) {
62
            // normal case: left_condition or right_condition
63 3
            if (!$negate) {
64 3
                return $b->orX
65 3
                    ( $compile_left()
66 3
                    , $compile_right()
67 3
                    );
68
            }
69
            // negated case: not (left_condition or right_condition)
70
            //             = not left_condition and not right_condition
71
            if ($negate) {
72
                return $b->andX
73
                    ( $compile_left(true)
74
                    , $compile_right(true)
75
                    );
76
            }
77
        }
78 48
        if ($var instanceof Vars\ButNot) {
79 18
            return $b->andX
80 18
                ( $compile_left()
81 18
                , $compile_right(true)
82 18
                );
83
        }
84 48
        if ($var instanceof Vars\Classes) {
85 42
            return $eq_op("$table_name.type", $b->literal(Variable::CLASS_TYPE));
86
        }
87 44
        if ($var instanceof Vars\Everything) {
88 23
            return $eq_op($b->literal(1), $b->literal(1));
89
        }
90 44
        if ($var instanceof Vars\Files) {
91
            return $eq_op("$table_name.type", $b->literal(Variable::FILE_TYPE));
92
        }
93 44
        if ($var instanceof Vars\Functions) {
94 13
            return $eq_op("$table_name.type", $b->literal(Variable::FUNCTION_TYPE));
95
        }
96 41
        if ($var instanceof Vars\Globals) {
97 19
            return $eq_op("$table_name.type", $b->literal(Variable::GLOBAL_TYPE));
98
        }
99 35
        if ($var instanceof Vars\LanguageConstruct) {
100
            // normal case : language construct and name matches
101 7
            if (!$negate) {
102 7
                return $b->andX
103 7
                    ( $eq_op("$table_name.type", $b->literal(Variable::LANGUAGE_CONSTRUCT_TYPE))
104 7
                    , $eq_op("$table_name.name", $b->literal($var->construct_name()))
105 7
                    );
106
            }
107
            // negated case: not (language construct and name matches)
108
            //             = not language construct or not name matches
109
            else {
110
111
                return $b->orX
112
                    ( $eq_op("$table_name.type", $b->literal(Variable::LANGUAGE_CONSTRUCT_TYPE))
113
                    , $eq_op("$table_name.name", $b->literal($var->construct_name()))
114
                    );
115
            }
116
        }
117 30
        if ($var instanceof Vars\Methods) {
118 1
            return $eq_op("$table_name.type", $b->literal(Variable::METHOD_TYPE));
119
        }
120 29
        if ($var instanceof Vars\WithName) {
121
            // normal case : left_condition AND regexp matches
122 29
            if (!$negate) {
123 11
                return $b->andX
124 11
                    ( $this->compile_var($table_name, $var->variable())
125 11
                    , "$table_name.name REGEXP ".$b->literal('^'.$var->regexp().'$')
126 11
                    );
127
            }
128
            // negated case: not (left_condition_left and regexp matches)
129
            //             = not left_condition and not regexp matches
130
            else {
131 18
                return $b->orX
132 18
                    ( $this->compile_var($table_name, $var->variable(), true)
133 18
                    , "$table_name.name NOT REGEXP ".$b->literal('^'.$var->regexp().'$')
134 18
                    );
135
            }
136
        }
137
        throw new \LogicException("Can't compile var-type '".get_class($var)."'");
138
    }
139
140
}
141