1: <?php
2: namespace Pharborist;
3:
4: use Pharborist\Types\ScalarNode;
5: use Pharborist\Variables\VariableExpressionNode;
6:
7: /**
8: * An array lookup.
9: *
10: * For example $array[0]
11: */
12: class ArrayLookupNode extends ParentNode implements VariableExpressionNode {
13: /**
14: * @var Node
15: */
16: protected $array;
17:
18: /**
19: * @var Node
20: */
21: protected $key;
22:
23: /**
24: * Creates a new array lookup.
25: *
26: * @param \Pharborist\ExpressionNode $array
27: * The expression representing the array (usually a VariableNode).
28: * @param \Pharborist\ExpressionNode $key
29: * The expression representing the key (usually a string).
30: *
31: * @return static
32: */
33: public static function create(ExpressionNode $array, ExpressionNode $key) {
34: $node = new static();
35: /** @var Node $array */
36: $node->addChild($array, 'array');
37: $node->addChild(Token::openBracket());
38: /** @var Node $key */
39: $node->addChild($key, 'key');
40: $node->addChild(Token::closeBracket());
41: return $node;
42: }
43:
44: /**
45: * @return Node
46: */
47: public function getArray() {
48: return $this->array;
49: }
50:
51: /**
52: * @return \Pharborist\Node[]
53: */
54: public function getKeys() {
55: $keys = [];
56:
57: if ($this->key) {
58: $keys[] = clone $this->key;
59: }
60: if ($this->array instanceof ArrayLookupNode) {
61: $keys = array_merge($this->array->getKeys(), $keys);
62: }
63:
64: return $keys;
65: }
66:
67: /**
68: * Returns a specific key in the lookup.
69: *
70: * @param integer $index
71: * The index of the key to return.
72: *
73: * @return \Pharborist\Node
74: *
75: * @throws
76: * \InvalidArgumentException if $index is not an integer.
77: * \OutOfBoundsException if $index is less than zero or greater than the
78: * number of keys in the lookup.
79: */
80: public function getKey($index = 0) {
81: $keys = $this->getKeys();
82:
83: if (!is_integer($index)) {
84: throw new \InvalidArgumentException();
85: }
86: if ($index < 0 || $index >= count($keys)) {
87: throw new \OutOfBoundsException();
88: }
89: return $keys[$index];
90: }
91:
92: /**
93: * Returns TRUE if all keys in the lookup are scalar. So a lookup like
94: * $foo['bar']['baz'][0] will be TRUE, but $foo[$bar]['baz'][0] won't.
95: *
96: * @return boolean
97: */
98: public function hasScalarKeys() {
99: if ($this->key instanceof ScalarNode) {
100: return $this->array instanceof ArrayLookupNode ? $this->array->hasScalarKeys() : TRUE;
101: }
102: else {
103: return FALSE;
104: }
105: }
106:
107: /**
108: * Returns every key in the lookup. For example, $foo['bar']['baz'][5] will
109: * return ['bar', 'baz', 5].
110: *
111: * @return mixed[]
112: *
113: * @throws \DomainException if the lookup contains any non-scalar keys.
114: */
115: public function extractKeys() {
116: if (!$this->hasScalarKeys()) {
117: throw new \DomainException('Cannot extract non-scalar keys from array lookup ' . $this);
118: }
119: return array_map(function(ScalarNode $key) { return $key->toValue(); }, $this->getKeys());
120: }
121:
122: /**
123: * Returns the root of the lookup.
124: *
125: * For example, given an expression like $foo['bar']['baz'],
126: * this method will return a VariableNode for $foo.
127: *
128: * @return Node
129: */
130: public function getRootArray() {
131: return $this->array instanceof ArrayLookupNode ? $this->array->getRootArray() : $this->array;
132: }
133: }
134: