Passed
Push — master ( fd358c...339072 )
by Ondřej
03:23
created

Tuple::getIterator()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 3
nc 2
nop 0
1
<?php
2
namespace Ivory\Relation;
3
4
use Ivory\Exception\AmbiguousException;
5
use Ivory\Exception\ImmutableException;
6
use Ivory\Exception\UndefinedColumnException;
7
use Ivory\Relation\Alg\ITupleEvaluator;
8
use Ivory\Utils\ValueUtils;
9
10
/**
11
 * {@inheritdoc}
12
 *
13
 * This implementation is immutable, i.e., once constructed, the tuple values cannot be changed. Thus, both `__set()`
14
 * and `ArrayAccess` write operations (namely {@link \ArrayAccess::offsetSet()} and {@link \ArrayAccess::offsetUnset()})
15
 * throw an {@link \Ivory\Exception\ImmutableException}.
16
 */
17
class Tuple implements ITuple
18
{
19
    const AMBIGUOUS_COL = false;
20
21
    /** @var array list of data for the corresponding columns; already converted */
22
    private $data;
23
    /** @var int[] map: column name => offset of the first column of the name */
24
    private $colNameMap;
25
26
27
    /**
28
     * Creates a tuple from an associative array.
29
     *
30
     * @param array|\Traversable $map
31
     * @return Tuple
32
     */
33
    public static function fromMap($map)
34
    {
35
        $data = [];
36
        $colNameMap = [];
37
        foreach ($map as $k => $v) {
38
            $data[] = $v;
39
            $colNameMap[$k] = count($data) - 1;
40
        }
41
        return new Tuple($data, $colNameMap);
42
    }
43
44
    /**
45
     * @param array $data list of data for the corresponding columns
46
     * @param array $colNameMap map: column name => zero-based offset of the column of the given name, or
47
     *                            <tt>Tuple::AMBIGUOUS_COL</tt> for denoting the name of the column is used multiple
48
     *                            times within the originating relation
49
     */
50
    public function __construct(array $data, array $colNameMap)
51
    {
52
        $this->data = $data;
53
        $this->colNameMap = $colNameMap;
54
    }
55
56
57
    //region ITuple
58
59
    public function toMap(): array
60
    {
61
        $res = [];
62
        foreach ($this->colNameMap as $name => $i) {
63
            if ($i === self::AMBIGUOUS_COL) {
64
                throw new AmbiguousException(
65
                    "There is an ambiguous column `$name`, preventing the tuple to be converted to a map"
66
                );
67
            }
68
            $res[$name] = $this->data[$i];
69
        }
70
        return $res;
71
    }
72
73
    public function toList(): array
74
    {
75
        return $this->data;
76
    }
77
78
    public function value($colOffsetOrNameOrEvaluator)
79
    {
80
        if (is_scalar($colOffsetOrNameOrEvaluator)) {
81
            if (filter_var($colOffsetOrNameOrEvaluator, FILTER_VALIDATE_INT) !== false) {
82
                return $this[$colOffsetOrNameOrEvaluator];
83
            } else {
84
                return $this->{$colOffsetOrNameOrEvaluator};
85
            }
86
        } elseif ($colOffsetOrNameOrEvaluator instanceof ITupleEvaluator) {
87
            return $colOffsetOrNameOrEvaluator->evaluate($this);
88
        } elseif ($colOffsetOrNameOrEvaluator instanceof \Closure) {
89
            return call_user_func($colOffsetOrNameOrEvaluator, $this);
90
        } else {
91
            throw new \InvalidArgumentException('$colOffsetOrNameOrEvaluator');
92
        }
93
    }
94
95
    //endregion
96
97
    //region dynamic properties
98
99
    public function __get($name)
100
    {
101
        if (!isset($this->colNameMap[$name])) {
102
            throw new UndefinedColumnException("No column named $name");
103
        } elseif ($this->colNameMap[$name] === self::AMBIGUOUS_COL) {
104
            throw new AmbiguousException("There are multiple columns named `$name` in the tuple");
105
        } else {
106
            return $this->data[$this->colNameMap[$name]];
107
        }
108
    }
109
110
    public function __isset($name)
111
    {
112
        return isset($this->colNameMap[$name]);
113
    }
114
115
    public function __set($name, $value)
116
    {
117
        throw new ImmutableException();
118
    }
119
120
    public function __unset($name)
121
    {
122
        throw new ImmutableException();
123
    }
124
125
    //endregion
126
127
    //region \ArrayAccess
128
129
    public function offsetExists($offset)
130
    {
131
        return array_key_exists($offset, $this->data);
132
    }
133
134
    public function offsetGet($offset)
135
    {
136
        if (array_key_exists($offset, $this->data)) {
137
            return $this->data[$offset];
138
        } else {
139
            throw new UndefinedColumnException("There is no column at offset `$offset` in the tuple");
140
        }
141
    }
142
143
    final public function offsetSet($offset, $value)
144
    {
145
        throw new ImmutableException();
146
    }
147
148
    final public function offsetUnset($offset)
149
    {
150
        throw new ImmutableException();
151
    }
152
153
    //endregion
154
155
    //region IEqualable
156
157
    public function equals($object)
158
    {
159
        if (!$object instanceof ITuple) {
160
            return false;
161
        }
162
        return ValueUtils::equals($this->data, $object->data);
0 ignored issues
show
Bug introduced by
Accessing data on the interface Ivory\Relation\ITuple suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
163
    }
164
165
    //endregion
166
}
167