Completed
Push — master ( dd28ca...b2bdac )
by Ondřej
03:18
created

ProjectedRelation::recognizeColsByName()   C

Complexity

Conditions 13
Paths 84

Size

Total Lines 42
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 42
rs 5.1234
c 0
b 0
f 0
cc 13
eloc 28
nc 84
nop 4

How to fix   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
namespace Ivory\Relation;
3
4
use Ivory\Exception\AmbiguousException;
5
use Ivory\Exception\InternalException;
6
use Ivory\Exception\UndefinedColumnException;
7
use Ivory\Relation\Alg\ITupleEvaluator;
8
9
class ProjectedRelation extends ProjectedRelationBase
10
{
11
    /** @var array list: either index to the source tuple data to take, or a tuple evaluator */
12
    private $projectionList;
13
14
    public function __construct(IRelation $source, $colDef)
15
    {
16
        parent::__construct($source, $this->defineColumns($source, $colDef));
17
    }
18
19
    private function defineColumns(IRelation $source, $colDef)
20
    {
21
        $srcCols = $source->getColumns();
22
23
        $columns = [];
24
        $this->projectionList = [];
25
26
        foreach ($colDef as $key => $value) {
27
            $nameSpecified = (filter_var($key, FILTER_VALIDATE_INT) === false || is_object($value));
28
            $colName = ($nameSpecified ? $key : (isset($srcCols[$value]) ? $srcCols[$value]->getName() : $value));
29
30
            if (filter_var($value, FILTER_VALIDATE_INT) !== false) { // column offset
31
                if (!isset($srcCols[$value])) {
32
                    throw new UndefinedColumnException($value);
33
                }
34
                $columns[] = new Column($this, count($this->projectionList), $colName, $srcCols[$value]->getType());
35
                $this->projectionList[] = (int)$value;
36
            } elseif (is_string($value)) { // column name
37
                $matched = $this->recognizeColsByName($srcCols, $nameSpecified, $key, $value);
38
                foreach ($matched as $i => $cn) {
39
                    $columns[] = new Column($this, count($this->projectionList), $cn, $srcCols[$i]->getType());
40
                    $this->projectionList[] = $i;
41
                }
42
            } elseif ($value instanceof ITupleEvaluator || $value instanceof \Closure) {
43
                $columns[] = new Column($this, $value, $colName, null);
44
                $this->projectionList[] = $value;
45
            } else {
46
                throw new \InvalidArgumentException("Invalid specification of the projection item '$key'");
47
            }
48
        }
49
50
        return $columns;
51
    }
52
53
    /**
54
     * @param IColumn[] $srcCols
55
     * @param bool $nameSpecified
56
     * @param $key
57
     * @param string $value
58
     * @return string[]
59
     */
60
    private function recognizeColsByName($srcCols, bool $nameSpecified, $key, string $value)
61
    {
62
        if ($value[0] == '/') { // PCRE macro
63
            $pcre = $value;
64
            $matchAll = true;
65
            $repl = ($nameSpecified ? $key : null);
66
        } else {
67
            $pcre = self::simpleMacroPatternToPcre($value, $starCnt);
68
            $matchAll = ($starCnt > 0);
69
            $repl = ($nameSpecified ? self::simpleMacroReplacementToPcre($key) : null);
70
        }
71
72
        if ($repl === null) {
73
            $cns = [];
74
            foreach ($srcCols as $i => $c) {
75
                $name = $c->getName();
76
                if ($name !== null) {
77
                    $cns[$i] = $name;
78
                }
79
            }
80
            $matched = preg_grep($pcre, $cns);
81
        } else {
82
            $matched = [];
83
            foreach ($srcCols as $i => $c) {
84
                if ($c->getName() !== null) {
85
                    $newName = preg_replace($pcre, $repl, $c->getName(), 1, $cnt);
86
                    if ($cnt > 0) {
87
                        $matched[$i] = $newName;
88
                    }
89
                }
90
            }
91
        }
92
93
        if (!$matched) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $matched of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
94
            throw new UndefinedColumnException($value);
95
        }
96
        if (!$matchAll && count($matched) > 1) {
97
            throw new AmbiguousException($value);
98
        }
99
100
        return $matched;
101
    }
102
103
    public function tuple(int $offset = 0): ITuple
104
    {
105
        $srcTuple = parent::tuple($offset);
106
107
        $data = [];
108
        foreach ($this->projectionList as $i => $spec) {
109
            if (is_int($spec)) {
110
                $data[$i] = $srcTuple[$spec];
111
            } elseif ($spec instanceof ITupleEvaluator) {
112
                $data[$i] = $spec->evaluate($srcTuple);
113
            } elseif ($spec instanceof \Closure) {
114
                $data[$i] = call_user_func($spec, $srcTuple);
115
            } else {
116
                throw new InternalException("The type of projection list item $i is not supported");
117
            }
118
        }
119
120
        return new Tuple($data, $this->getColNameMap());
121
    }
122
}
123