Elgg  Version 2.3
AttributeLoader.php
Go to the documentation of this file.
1 <?php
2 namespace Elgg;
3 
13 
19  protected static $primary_attr_names = array(
20  'guid',
21  'type',
22  'subtype',
23  'owner_guid',
24  'container_guid',
25  'site_guid',
26  'access_id',
27  'time_created',
28  'time_updated',
29  'last_action',
30  'enabled',
31  );
32 
36  protected static $integer_attr_names = array(
37  'guid',
38  'owner_guid',
39  'container_guid',
40  'site_guid',
41  'access_id',
42  'time_created',
43  'time_updated',
44  'last_action',
45  // \ElggUser
46  'prev_last_action',
47  'last_login',
48  'prev_last_login'
49  );
50 
54  protected static $null_attr_names = array(
55  'name',
56  'title',
57  'description',
58  'url',
59  );
60 
64  protected $secondary_attr_names = array();
65 
69  protected $required_type;
70 
75 
79  protected $class;
80 
84  public $requires_access_control = true;
85 
89  public $primary_loader = 'get_entity_as_row';
90 
94  public $secondary_loader = '';
95 
99  public $full_loader = '';
100 
104  protected $additional_select_values = array();
105 
114  public function __construct($class, $required_type, array $initialized_attrs) {
115  if (!is_string($class)) {
116  throw new \InvalidArgumentException('$class must be a class name.');
117  }
118  $this->class = $class;
119 
120  if (!is_string($required_type)) {
121  throw new \InvalidArgumentException('$requiredType must be a system entity type.');
122  }
123  $this->required_type = $required_type;
124 
125  $this->initialized_attributes = $initialized_attrs;
126  $all_attr_names = array_keys($initialized_attrs);
127  $this->secondary_attr_names = array_diff($all_attr_names, self::$primary_attr_names);
128  }
129 
136  protected function isMissingPrimaries($row) {
137  return array_diff(self::$primary_attr_names, array_keys($row)) !== array();
138  }
139 
146  protected function isMissingSecondaries($row) {
147  return array_diff($this->secondary_attr_names, array_keys($row)) !== array();
148  }
149 
157  protected function checkType($row) {
158  if ($row['type'] !== $this->required_type) {
159  $msg = "GUID:" . $row['guid'] . " is not a valid " . $this->class;
160  throw new \InvalidClassException($msg);
161  }
162  }
163 
169  public function getAdditionalSelectValues() {
170  return $this->additional_select_values;
171  }
172 
186  public function getRequiredAttributes($row) {
187  if (!is_array($row) && !($row instanceof \stdClass)) {
188  // assume row is the GUID
189  $row = array('guid' => $row);
190  }
191  $row = (array) $row;
192  if (empty($row['guid'])) {
193  throw new \InvalidArgumentException('$row must be or contain a GUID');
194  }
195 
196  $was_missing_primaries = $this->isMissingPrimaries($row);
197  $was_missing_secondaries = $this->isMissingSecondaries($row);
198 
199  // some types have a function to load all attributes at once, it should be faster
200  if (($was_missing_primaries || $was_missing_secondaries) && is_callable($this->full_loader)) {
201  $fetched = (array) call_user_func($this->full_loader, $row['guid']);
202  if (!$fetched) {
203  return array();
204  }
205  $row = array_merge($row, $fetched);
206  $this->checkType($row);
207  } else {
208  if ($was_missing_primaries) {
209  if (!is_callable($this->primary_loader)) {
210  throw new \LogicException('Primary attribute loader must be callable');
211  }
212  if ($this->requires_access_control) {
213  $fetched = (array) call_user_func($this->primary_loader, $row['guid']);
214  } else {
215  $ignoring_access = elgg_set_ignore_access();
216  $fetched = (array) call_user_func($this->primary_loader, $row['guid']);
217  elgg_set_ignore_access($ignoring_access);
218  }
219  if (!$fetched) {
220  return array();
221  }
222  $row = array_merge($row, $fetched);
223  }
224 
225  // We must test type before trying to load the secondaries so that InvalidClassException
226  // gets thrown. Otherwise the secondary loader will fail and return false.
227  $this->checkType($row);
228 
229  if ($was_missing_secondaries) {
230  if (!is_callable($this->secondary_loader)) {
231  throw new \LogicException('Secondary attribute loader must be callable');
232  }
233  $fetched = (array) call_user_func($this->secondary_loader, $row['guid']);
234  if (!$fetched) {
235  throw new \IncompleteEntityException("Secondary loader failed to return row for {$row['guid']}");
236  }
237  $row = array_merge($row, $fetched);
238  }
239  }
240 
241  $row = $this->filterAddedColumns($row);
242 
243  $row['subtype'] = (int)$row['subtype'];
244 
245  // set to null when reading empty value, to match default empty value; See #5456
246  foreach (self::$null_attr_names as $key) {
247  if (isset($row[$key]) && !$row[$key]) {
248  $row[$key] = null;
249  }
250  }
251 
252  // Note: If there are still missing attributes, we're running on a 1.7 or earlier schema. We let
253  // this pass so the upgrades can run.
254 
255  // guid needs to be an int https://github.com/elgg/elgg/issues/4111
256  foreach (self::$integer_attr_names as $key) {
257  if (isset($row[$key])) {
258  $row[$key] = (int) $row[$key];
259  }
260  }
261  return $row;
262  }
263 
270  protected function filterAddedColumns($row) {
271  // make an array with keys as acceptable attribute names
272  $acceptable_attrs = self::$primary_attr_names;
273  array_splice($acceptable_attrs, count($acceptable_attrs), 0, $this->secondary_attr_names);
274  $acceptable_attrs = array_combine($acceptable_attrs, $acceptable_attrs);
275 
276  foreach ($row as $key => $val) {
277  if (!isset($acceptable_attrs[$key])) {
278  $this->additional_select_values[$key] = $val;
279  unset($row[$key]);
280  }
281  }
282  return $row;
283  }
284 }
285 
getAdditionalSelectValues()
Get values selected from the database that are not attributes.
checkType($row)
Check that the type is correct.
$class
Definition: field.php:20
Save menu items.
$key
Definition: summary.php:34
elgg_set_ignore_access($ignore=true)
Set if Elgg&#39;s access system should be ignored.
Definition: access.php:43
__construct($class, $required_type, array $initialized_attrs)
Constructor.
getRequiredAttributes($row)
Get all required attributes for the entity, validating any that are passed in.
filterAddedColumns($row)
Filter non-attribute keys into $this->additional_select_values.
isMissingPrimaries($row)
Get primary attributes missing that are missing.
isMissingSecondaries($row)
Get secondary attributes that are missing.
$row