1<?php 2/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */ 3 4namespace Icinga\Data\DataArray; 5 6use ArrayIterator; 7use Icinga\Data\Selectable; 8use Icinga\Data\SimpleQuery; 9 10class ArrayDatasource implements Selectable 11{ 12 /** 13 * The array being used as data source 14 * 15 * @var array 16 */ 17 protected $data; 18 19 /** 20 * The current result 21 * 22 * @var array 23 */ 24 protected $result; 25 26 /** 27 * The result of a counted query 28 * 29 * @var int 30 */ 31 protected $count; 32 33 /** 34 * The name of the column to map array keys on 35 * 36 * In case the array being used as data source provides keys of type string,this name 37 * will be used to set such as column on each row, if the column is not set already. 38 * 39 * @var string 40 */ 41 protected $keyColumn; 42 43 /** 44 * Create a new data source for the given array 45 * 46 * @param array $data The array you're going to use as a data source 47 */ 48 public function __construct(array $data) 49 { 50 $this->data = $data; 51 } 52 53 /** 54 * Set the name of the column to map array keys on 55 * 56 * @param string $name 57 * 58 * @return $this 59 */ 60 public function setKeyColumn($name) 61 { 62 $this->keyColumn = $name; 63 return $this; 64 } 65 66 /** 67 * Return the name of the column to map array keys on 68 * 69 * @return string 70 */ 71 public function getKeyColumn() 72 { 73 return $this->keyColumn; 74 } 75 76 /** 77 * Provide a query for this data source 78 * 79 * @return SimpleQuery 80 */ 81 public function select() 82 { 83 return new SimpleQuery(clone $this); 84 } 85 86 /** 87 * Fetch and return all rows of the given query's result set using an iterator 88 * 89 * @param SimpleQuery $query 90 * 91 * @return ArrayIterator 92 */ 93 public function query(SimpleQuery $query) 94 { 95 return new ArrayIterator($this->fetchAll($query)); 96 } 97 98 /** 99 * Fetch and return a column of all rows of the result set as an array 100 * 101 * @param SimpleQuery $query 102 * 103 * @return array 104 */ 105 public function fetchColumn(SimpleQuery $query) 106 { 107 $result = array(); 108 foreach ($this->getResult($query) as $row) { 109 $arr = (array) $row; 110 $result[] = array_shift($arr); 111 } 112 113 return $result; 114 } 115 116 /** 117 * Fetch and return all rows of the given query's result as a flattened key/value based array 118 * 119 * @param SimpleQuery $query 120 * 121 * @return array 122 */ 123 public function fetchPairs(SimpleQuery $query) 124 { 125 $result = array(); 126 $keys = null; 127 foreach ($this->getResult($query) as $row) { 128 if ($keys === null) { 129 $keys = array_keys((array) $row); 130 if (count($keys) < 2) { 131 $keys[1] = $keys[0]; 132 } 133 } 134 135 $result[$row->{$keys[0]}] = $row->{$keys[1]}; 136 } 137 138 return $result; 139 } 140 141 /** 142 * Fetch and return the first row of the given query's result 143 * 144 * @param SimpleQuery $query 145 * 146 * @return object|false The row or false in case the result is empty 147 */ 148 public function fetchRow(SimpleQuery $query) 149 { 150 $result = $this->getResult($query); 151 if (empty($result)) { 152 return false; 153 } 154 155 return array_shift($result); 156 } 157 158 /** 159 * Fetch and return all rows of the given query's result as an array 160 * 161 * @param SimpleQuery $query 162 * 163 * @return array 164 */ 165 public function fetchAll(SimpleQuery $query) 166 { 167 return $this->getResult($query); 168 } 169 170 /** 171 * Count all rows of the given query's result 172 * 173 * @param SimpleQuery $query 174 * 175 * @return int 176 */ 177 public function count(SimpleQuery $query) 178 { 179 if ($this->count === null) { 180 $this->count = count($this->createResult($query)); 181 } 182 183 return $this->count; 184 } 185 186 /** 187 * Create and return the result for the given query 188 * 189 * @param SimpleQuery $query 190 * 191 * @return array 192 */ 193 protected function createResult(SimpleQuery $query) 194 { 195 $columns = $query->getColumns(); 196 $filter = $query->getFilter(); 197 $offset = $query->hasOffset() ? $query->getOffset() : 0; 198 $limit = $query->hasLimit() ? $query->getLimit() : 0; 199 200 $foundStringKey = false; 201 $result = array(); 202 $skipped = 0; 203 foreach ($this->data as $key => $row) { 204 if ($this->keyColumn !== null && !isset($row->{$this->keyColumn})) { 205 $row = clone $row; // Make sure that this won't affect the actual data 206 $row->{$this->keyColumn} = $key; 207 } 208 209 if (! $filter->matches($row)) { 210 continue; 211 } elseif ($skipped < $offset) { 212 $skipped++; 213 continue; 214 } 215 216 // Get only desired columns if asked so 217 if (! empty($columns)) { 218 $filteredRow = (object) array(); 219 foreach ($columns as $alias => $name) { 220 if (! is_string($alias)) { 221 $alias = $name; 222 } 223 224 if (isset($row->$name)) { 225 $filteredRow->$alias = $row->$name; 226 } else { 227 $filteredRow->$alias = null; 228 } 229 } 230 } else { 231 $filteredRow = $row; 232 } 233 234 $foundStringKey |= is_string($key); 235 $result[$key] = $filteredRow; 236 237 if (count($result) === $limit) { 238 break; 239 } 240 } 241 242 // Sort the result 243 if ($query->hasOrder()) { 244 if ($foundStringKey) { 245 uasort($result, array($query, 'compare')); 246 } else { 247 usort($result, array($query, 'compare')); 248 } 249 } elseif (! $foundStringKey) { 250 $result = array_values($result); 251 } 252 253 return $result; 254 } 255 256 /** 257 * Return whether a query result exists 258 * 259 * @return bool 260 */ 261 protected function hasResult() 262 { 263 return $this->result !== null; 264 } 265 266 /** 267 * Set the current result 268 * 269 * @param array $result 270 * 271 * @return $this 272 */ 273 protected function setResult(array $result) 274 { 275 $this->result = $result; 276 return $this; 277 } 278 279 /** 280 * Return the result for the given query 281 * 282 * @param SimpleQuery $query 283 * 284 * @return array 285 */ 286 protected function getResult(SimpleQuery $query) 287 { 288 if (! $this->hasResult()) { 289 $this->setResult($this->createResult($query)); 290 } 291 292 return $this->result; 293 } 294} 295