Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
event_manager.inc
1 <?php
60 {
61 
66  var $_runtime_event_listeners = Array();
67 
72  var $_runtime_listener_objects = Array();
73 
78  function Event_Manager()
79  {
80  parent::MySource_Object();
81 
82  }//end constructor
83 
84 
98  function addEventListener(&$listener, $events=Array(), $broadcast_polling_events=true)
99  {
100  if (empty($events)) return false;
101 
102  foreach ($events as $event_name) {
103  $event_name = trim($event_name);
104  if (empty($event_name)) continue;
105 
106  if (!array_key_exists($event_name, $this->_runtime_event_listeners)) {
107  $this->_runtime_event_listeners[$event_name] = Array();
108  }
109  $index = count($this->_runtime_listener_objects);
110  $this->_runtime_listener_objects[] = $listener;
111  $this->_runtime_event_listeners[$event_name][$index] = true;
112  }
113  return true;
114 
115  }//end addEventListener()
116 
117 
128  function _broadcastRuntimeEvents(&$broadcaster, $event_name, $vars=Array())
129  {
130  if (!isset($this->_runtime_event_listeners[$event_name])) {
131  return false;
132  }
133 
134  $event_name = trim($event_name);
135  $function_name = 'on'.$event_name;
136 
137  foreach (array_keys($this->_runtime_event_listeners[$event_name]) as $index) {
138  if (!isset($this->_runtime_listener_objects[$index])) {
139  continue;
140  }
141  $listener = $this->_runtime_listener_objects[$index];
142  if (is_null($listener)) continue;
143  if (!method_exists($listener, $function_name)) {
144  continue;
145  }
146  $listener->$function_name($broadcaster, $vars);
147  }
148 
149  return true;
150 
151  }//end _broadcastRuntimeEvents()
152 
153 
170  function broadcastEvent(&$broadcaster, $event_name='', $vars=Array())
171  {
172 
173  // note: event names should be cammel topped (first letter of each word is a capital, excluding the first word).
174  // the listener needs to have a function of the event name prepended with 'on'
175  // eg. for an event name contentsUpdated the listener needs a function called onContentsUpdated()
176  // The function needs to accept an array in the arguments list (for vars)
177 
178  // check the runtime events first
179  $this->_broadcastRuntimeEvents($broadcaster, $event_name, $vars);
180 
181  // create a temp entry for the event listeners (runtime wide as event_manager is cached)
182  if (!isset($this->_tmp['event_listeners'])) {
183  $this->_loadCachedEventListeners();
184  }
185 
186  // we don't have an entry for this event
187  if (!isset($this->_tmp['event_listeners']['global'][$event_name]) && !isset($this->_tmp['event_listeners'][strtolower(get_class($broadcaster))][$event_name])) {
188  return false;
189  }
190 
191  $listeners = Array();
192  $listener_codes = Array();
193 
194  if (isset($this->_tmp['event_listeners']['global'][$event_name])) {
195  $listener_codes += $this->_tmp['event_listeners']['global'][$event_name];
196  }
197  if (isset($this->_tmp['event_listeners'][get_class_lower($broadcaster)][$event_name])) {
198  $listener_codes += $this->_tmp['event_listeners'][get_class_lower($broadcaster)][$event_name];
199  }
200 
207  $possible_listeners_parents = array();
208  $possible_listeners_children = array();
209 
210  foreach ($listener_codes as $listener_type_code => $options) {
211  // these $options never seem to be set, force them to be set to avoid notices from PHP
212  if (empty($options['type_code'])) {
213  $options['type_code'] = '';
214  }
215  if (empty($options['strict_type_code'])) {
216  $options['strict_type_code'] = false;
217  }
218  if (empty($options['global'])) {
219  $options['global'] = false;
220  }
221 
222  $side_of_link = (isset($options['side_of_link']) && ($options['side_of_link'] == 'minor')) ? 'major' : 'minor';
223 
224  if ($options['global']) {
225  // Global: all global events should be heard even if outside the chain
226  $assetids = $GLOBALS['SQ_SYSTEM']->am->getTypeAssetids($listener_type_code, true);
227  } else if ($options['indirect']) {
228 
229  // no point continuing if this object does not inherit from asset
230  if (!($broadcaster instanceof asset)) return true;
231  if (!$broadcaster->id) return;
232 
233  // let's not query the tree unless we have to
234  if ($options['is_dependant'] && !$options['is_exclusive']) {
235  if (isset($found_listener)) {
236  continue;
237  }
238 
239  // do this until one of the returned assets matches a listener type code
240  if ($side_of_link === 'minor') {
241  if (empty($possible_listeners_parents)) {
242  $possible_listeners_parents = $GLOBALS['SQ_SYSTEM']->am->getDependantParents($broadcaster->id);
243  }
244  $possible_listeners = $possible_listeners_parents;
245  } else {
246  if (empty($possible_listeners_children)) {
247  $possible_listeners_children = $GLOBALS['SQ_SYSTEM']->am->getDependantChildren($broadcaster->id);
248  }
249  $possible_listeners = $possible_listeners_children;
250  }
251 
252  // get type codes of our assetids (only need to do it once)
253  if (!isset($types)) {
254  for ($i=0; $i<count($possible_listeners); $i++) {
255  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($possible_listeners[$i]);
256  $types[$possible_listeners[$i]] = $asset->type();
257  }
258  }
259 
260  } else if ($options['is_dependant'] || $options['is_exclusive'] || $options['type_code']) {
261  $assetids = $this->_getDiscreteAssets($broadcaster->id, $listener_type_code, $side_of_link, $options);
262  } else {
263 
264  // this is in the reverse to what is normal.
265  // eg. if a form wants to listen to events broadcasted from a bodycopy (which is a child of the form)
266  // then the form wants to hear from minor. When the bodycopy broadcasts, the form is on the major of
267  // the bodycopy, comprende?
268 
269  $function = 'get'.(($options['side_of_link'] == 'minor') ? 'Parents' : 'Children');
270  $assetids = $GLOBALS['SQ_SYSTEM']->am->$function($broadcaster->id, $listener_type_code, $options['strict_type_code']);
271  if (empty($assetids)) continue;
272  }
273 
274  } else {
275 
276  $links = $GLOBALS['SQ_SYSTEM']->am->getLinks($broadcaster->id, $options['link_type'], $listener_type_code, true, $options['side_of_link'], $options['value'], $options['is_dependant'], $options['is_exclusive']);
277  if (empty($links)) continue;
278 
279  $assetids = Array();
280  foreach ($links as $link) {
281  $assetids[] = $link[$side_of_link.'id'];
282  }
283 
284  }// end if
285 
286  // are we filtering our listeners?
287  if (isset($types)) {
288  // get our listener
289  if (in_array($listener_type_code, $types)) {
290  foreach ($types as $assetid => $type_code) {
291  if ($type_code === $listener_type_code) {
292  $found_listener = TRUE;
293  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
294  if (is_null($asset)) continue;
295  $listeners[] = $asset;
296  }
297  }
298  }
299  } else {
300  foreach ($assetids as $assetid) {
301  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
302  if (is_null($asset)) continue;
303  $listeners[] = $asset;
304  }
305  }
306 
307  }// end foreach
308 
309  if (empty($listeners)) return false;
310 
311  $function = 'on'.$event_name;
312  for (reset($listeners); null !== ($key = key($listeners)); next($listeners)) {
313  $listener = $listeners[$key];
314  if (!method_exists($listener, $function)) {
315  trigger_localised_error('SYS0127', E_USER_WARNING, $function, $listener->type(), $asset->id);
316  return false;
317  }
318  $listener->$function($broadcaster, $vars);
319  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($listener);
320  }// end for
321 
322  return true;
323 
324  }//end broadcastEvent()
325 
326 
340  function _getDiscreteAssets($assetid='', $type_code='', $side_of_link='minor', $info=Array())
341  {
342  if (empty($assetid) || empty($type_code)) {
343  return Array();
344  }
345 
346  $sql = 'SELECT (t2.treeid || \'%\')
347  FROM sq_ast_lnk l1
348  INNER JOIN sq_ast_lnk_tree t1 ON l1.linkid = t1.linkid,
349  sq_ast_lnk l2
350  INNER JOIN sq_ast_lnk_tree t2 ON l2.linkid = t2.linkid,
351  sq_ast a
352  WHERE l1.'.$side_of_link.'id = :assetid
353  AND SUBSTR(t1.treeid, 1, LENGTH(t2.treeid)) = t2.treeid
354  AND a.assetid=l2.'.$side_of_link.'id
355  AND a.type_code= :type_code
356  ORDER BY t2.treeid DESC';
357 
358  $result = NULL;
359  try {
360  $query = MatrixDAL::preparePdoQuery($sql);
361  MatrixDAL::bindValueToPdo($query, 'assetid', $assetid);
362  MatrixDAL::bindValueToPdo($query, 'type_code', $type_code);
363  $result = MatrixDAL::executePdoAssoc($query, 0);
364  } catch (Exception $e) {
365  throw new Exception('Unable to get Discrete Assets due to database error: '.$e->getMessage());
366  }
367 
368  if (empty($result)) return Array();
369 
370  $treeid_string = '';
371  foreach ($result as $treeid) {
372  $treeid_string .= 't2.treeid LIKE \''.$treeid.'\' OR ';
373  }
374  $treeid_string = substr($treeid_string, 0, -4);
375 
376 
377  $sql = 'SELECT a.assetid, t2.treeid, a.type_code, l2.link_type, l2.is_dependant, l2.is_exclusive
378  FROM sq_ast_lnk l1
379  INNER JOIN sq_ast_lnk_tree t1 ON l1.linkid = t1.linkid,
380  sq_ast_lnk l2
381  INNER JOIN sq_ast_lnk_tree t2 ON l2.linkid = t2.linkid,
382  sq_ast a
383  WHERE l1.'.$side_of_link.'id = :assetid
384  AND SUBSTR(t1.treeid, 1, LENGTH(t2.treeid)) = t2.treeid
385  AND a.assetid = l2.'.$side_of_link.'id
386  AND ('.$treeid_string.')
387  ORDER BY t2.treeid';
388 
389  $result = NULL;
390 
391  try {
392  $query = MatrixDAL::preparePdoQuery($sql);
393  MatrixDAL::bindValueToPdo($query, 'assetid', $assetid);
394  $result = MatrixDAL::executePdoAll($query);
395  } catch (Exception $e) {
396  throw new Exception('Unable to get Discrete Assets due to database error: '.$e->getMessage());
397  }//end try
398 
399  $assetids = Array();
400  $start_treeid = true;
401  $popped_treeid = false;
402 
403  for ($i = 0; $i < count($result); $i++) {
404 
405  if (isset($result[$i+1]) && strpos($result[$i+1]['treeid'], $result[$i]['treeid']) === false) {
406 
407  // the next treeid is not based on our treeid, so we will be starting a new branch
408  $start_treeid = true;
409  $popped_treeid = false;
410 
411  } else {
412 
413  if (!$popped_treeid) {
414 
415  if (in_array($result[$i]['assetid'], $assetids)) {
416  // we have already approved this asset to get notified
417  // so lets skip the rest of the branch
418  $popped_treeid = true;
419  continue;
420  }
421 
422  if ($side_of_link == 'minor') {
423  if (!isset($result[$i +1])) continue;
424  $test_info = $result[$i +1];
425  } else {
426  $test_info = $result[$i];
427  }
428 
429  $valid_assetid = true;
430  if ($valid_assetid && $info['value'] && $info['value'] != $test_info['value']) {
431  $valid_assetid = false;
432  }
433  if ($valid_assetid && !$test_info['is_dependant'] && $info['is_dependant']) {
434  $valid_assetid = false;
435  }
436  if ($valid_assetid && !$test_info['is_exclusive'] && $info['is_exclusive']) {
437  $valid_assetid = false;
438  }
439  if ($valid_assetid && $test_info['link_type'] & $info['link_type'] <= 0) {
440  $valid_assetid = false;
441  }
442 
443  if ($start_treeid && $valid_assetid) {
444  $assetids[] = $result[$i]['assetid'];
445  $start_treeid = false;
446  } else if (!$start_treeid && !$valid_assetid) {
447  // the last assetid we added failed the test, so get rid of it
448  array_pop($assetids);
449  $popped_treeid = true;
450  }
451 
452  }//end if !$popped_treeid
453 
454  }//end if not starting again next time around
455 
456  }//end for
457 
458  return $assetids;
459 
460  }//end _getDiscreteAssets()
461 
462 
471  function writeStaticEventsCacheFile()
472  {
473  if (!isset($this->_tmp['event_listeners']) || empty($this->_tmp['event_listeners'])) {
474  return false;
475  }
476 
477  $output = '<'.'?php'."\n".' $cached_event_listeners_array = ';
478  $output .= var_export($this->_tmp['event_listeners'], true);
479  $output .= "\n?".'>';
480 
481  if (!string_to_file($output, SQ_DATA_PATH.'/private/events/event_listeners.inc')) {
482  return false;
483  }
484  unset($this->_tmp['event_listeners']);
485 
486  return true;
487 
488  }//end writeStaticEventsCacheFile()
489 
490 
504  function installStaticEvents($listeners=Array(), $listener_type_code='', $recursive_call=false)
505  {
506  if (!is_array($listeners)|| empty($listeners)) {
507  return false;
508  }
509  if (!isset($this->_tmp['event_listeners'])) {
510  $this->_loadCachedEventListeners();
511  }
512 
513  $listener_name = Array();
514  foreach ($listeners as $index => $info) {
515  if (!isset($this->_tmp['event_listeners'][$info['broadcast_type_code']])) {
516  $this->_tmp['event_listeners'][$info['broadcast_type_code']] = Array();
517  }
518  if (!isset($this->_tmp['event_listeners'][$info['broadcast_type_code']][$info['event_name']])) {
519  $this->_tmp['event_listeners'][$info['broadcast_type_code']][$info['event_name']] = Array();
520  }
521 
522  // if they have been lazy, do some cleanup
523  if (!isset($info['options']['value'])) {
524  $info['options']['value'] = '';
525  }
526 
527  if (!is_array($info['options'])) {
528  trigger_localised_error('SYS0143', E_USER_WARNING, $listener_type_code, gettype($info['options']));
529  return false;
530  }
531 
532  $this->_tmp['event_listeners'][$info['broadcast_type_code']][$info['event_name']][$listener_type_code] = $info['options'];
533 
534  // if the broadcast type code can be of multiple types, then we have to add some more
535  // entries for these in the array
536  if (!$info['broadcast_strict_type_code'] && !$recursive_call) {
537  // only broadcast if the listener is installed, fail silently
538  if ($GLOBALS['SQ_SYSTEM']->am->installed($info['broadcast_type_code'])) {
539  $type_codes = $GLOBALS['SQ_SYSTEM']->am->getTypeDescendants($info['broadcast_type_code']);
540  if (empty($type_codes)) continue;
541  foreach ($type_codes as $type_code) {
542  $info['broadcast_type_code'] = $type_code;
543  $this->installStaticEvents(Array($info), $listener_type_code, true);
544  }
545  }
546  }
547  $listener_name[] = $info['event_name'];
548  }
549 
550  // done with everything ?
551  // remove any of the listeners that might have been deleted from our asset
552  foreach ($this->_tmp['event_listeners'] as $broadcast_type_code => $listener_info) {
553  foreach ($listener_info as $name => $asset_array) {
554  foreach ($asset_array as $asset_type => $info) {
555  if ($asset_type == $listener_type_code && !in_array($name, $listener_name)) {
556  unset($this->_tmp['event_listeners'][$broadcast_type_code][$name][$asset_type]);
557  }
558  }
559  }
560  }
561 
562  return true;
563 
564  }//end installStaticEvents()
565 
566 
573  function _loadCachedEventListeners()
574  {
575  if (!file_exists(SQ_DATA_PATH.'/private/events/event_listeners.inc')) {
576  $this->_tmp['event_listeners'] = Array();
577  return;
578  }
579  require SQ_DATA_PATH.'/private/events/event_listeners.inc';
580  if (!isset($cached_event_listeners_array)) {
581  trigger_error('Failed loading event listeners from SQ_DATA_PATH/private/events/event_listeners.inc; Event listeners file may be corrupt', E_USER_ERROR);
582  return;
583  }
584  $this->_tmp['event_listeners'] = $cached_event_listeners_array;
585 
586  }//end _loadCachedEventListeners()
587 
588 
589 }//end class
590 ?>