Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
system_update_lookups.php
1 <?php
30 error_reporting(E_ALL);
31 if (ini_get('memory_limit') != '-1') ini_set('memory_limit', '-1');
32 if ((php_sapi_name() != 'cli')) {
33  trigger_error("You can only run this script from the command line\n", E_USER_ERROR);
34 }//end if
35 
36 $config = array();
37 
38 define('LOG_FILE_NAME', 'update_lookups.log');
39 
40 // Get config from command line args.
41 process_args($config);
42 
43 require_once $config['system_root'].'/core/include/init.inc';
44 require_once SQ_INCLUDE_PATH.'/assertions.inc';
45 
46 define('LOG_FILE', SQ_SYSTEM_ROOT.'/data/private/logs/'.LOG_FILE_NAME); // This is the log file
47 define('SYNCH_FILE', SQ_TEMP_PATH.'/update_lookups.assetid'); // We need this file to store the assetids
48 
49 if (empty($config['assetids'])) {
50  // We are running the script over the whole system
51  $rootnodes = $GLOBALS['SQ_SYSTEM']->am->getTypeAssetids('site', FALSE);
52 } else {
53  // Replace space with empty string
54  $assetids = preg_replace('/[\s]*/', '', $config['assetids']);
55 
56  // Explode them so we have the list in array
57  $rootnodes = getRootNodes($assetids);
58 }
59 
60 _disconnectFromMatrixDatabase();
61 
62 // This ridiculousness allows us to workaround Oracle, forking and CLOBs
63 // if a query is executed that returns more than 1 LOB before a fork occurs,
64 // the Oracle DB connection will be lost inside the fork.
65 $pid_prepare = pcntl_fork();
66  switch ($pid_prepare) {
67  case -1:
68  break;
69  case 0:
70  // Connect to DB within the child process
71  _connectToMatrixDatabase();
72 
73  // log in as root
74  $root_user = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('root_user');
75  if (!$GLOBALS['SQ_SYSTEM']->setCurrentUser($root_user)) {
76  trigger_error("Failed logging in as root user\n", E_USER_ERROR);
77  exit(1);
78  }//end if
79 
80  echo "Determining assets to process...\n";
81  $children = getTreeSortedChildren($rootnodes);
82 
83  // Save the list into a file so we can access the list from the parent process
84  file_put_contents(SYNCH_FILE, implode(',', $children));
85 
86  // Disconnect from DB
87  _disconnectFromMatrixDatabase();
88 
89  exit(0);
90  // waiting for child exit signal
91  $status = null;
92  pcntl_waitpid(-1, $status);
93 
94  break;
95  default:
96  $status = null;
97  pcntl_waitpid(-1, $status);
98  break;
99  }//end switch
100 
101 
102 $children = Array();
103 if (file_exists(SYNCH_FILE)) {
104  $children_str = file_get_contents(SYNCH_FILE);
105 } else {
106  echo "Unable to find Synch File, probably because the root user was not able to log in, or the user executing this script does not have permission to write to this folder.\n";
107  exit(0);
108 }//end else
109 
110 $children = explode(',', $children_str);
111 $children = array_unique($children);
112 
113 // Chunk them up so we can process each batch when forking
114 $chunk_children = array_chunk($children, $config['batch_size']);
115 $current_child_list = Array();
116 
117 echo "Updating lookups for " . count($children) . " assets...\n";
118 
119 log_to_file('======================= Start updating lookups '.date('d-m-Y h:i:s').' =======================', LOG_FILE);
120 log_to_file("Updating lookups for " . var_export(count($children),TRUE) . " assets \n", LOG_FILE);
121 
122 while (!empty($chunk_children)) {
123  $current_child_list = array_pop($chunk_children);
124  $current_remaining = count($current_child_list);
125  $pid = pcntl_fork();
126 
127  switch ($pid) {
128  case -1:
129  trigger_error('Process failed to fork', E_USER_ERROR);
130  exit(1);
131  break;
132  case 0:
133 
134  // Connect to DB within the child process
135  _connectToMatrixDatabase();
136  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
137 
138  $conn_id = MatrixDAL::getCurrentDbId();
139 
140  foreach ($current_child_list as $child_assetid) {
141  $child_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($child_assetid);
142 
143  // Update lookups
144  if (!is_object($child_asset)) {
145  log_to_file('Asset #'.$child_assetid.' does not exist, skipping', LOG_FILE);
146  if ($config['verbose']) echo '-- Asset #'.$child_assetid.' does not exist, skipping' . "\n";
147  continue;
148  }
149  $child_asset->updateLookups(FALSE);
150  log_to_file('Updated lookups for #'.$child_assetid, LOG_FILE);
151 
152  $current_remaining--;
153  if ($config['verbose']) echo '-- Remaining: ' . (string) ($current_remaining + (count($chunk_children) * $config['batch_size'])) . "\n";
154 
155  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($child_asset);
156  $child_asset = NULL;
157  unset($child_asset);
158 
159  }//end foreach
160 
161  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
162  // Disconnect from DB
163  _disconnectFromMatrixDatabase();
164 
165  exit(0);
166  break;
167 
168  default:
169  $status = null;
170  pcntl_waitpid(-1, $status);
171  break;
172 
173  }//end switch
174 //}//end foreach
175 }//end while
176 
177 echo "Done\n";
178 
179 log_to_file('======================= Finished updating lookups '.date('d-m-Y h:i:s').' =======================', LOG_FILE);
180 if (file_exists(SYNCH_FILE)) {
181  unlink(SYNCH_FILE);
182 }//end if
183 exit(0);
184 
185 
186 
187 
193 function usage() {
194  echo "\n";
195  echo "Usage: php {$_SERVER['argv'][0]} <system_root> [assetid[,assetid]] [--batch-size <num>] [--verbose]\n";
196  echo "\n";
197  echo " [assetid] Assets to update lookups for (and their children). If this argument is omitted the script will run on the whole system\n";
198  echo " --batch-size <num> Number of assets to process per fork. Default is 1000.\n";
199  echo " --verbose Print number of assets remaining to stdout.\n";
200  echo "\n";
201  echo "Results are logged to data/private/logs/".LOG_FILE_NAME."\n";
202 
203 }//end usage()
204 
205 
212 function process_args(&$config) {
213 
214  $config['system_root'] = (isset($_SERVER['argv'][1])) ? $_SERVER['argv'][1] : '';
215  if (empty($config['system_root'])) {
216  echo "ERROR: You need to supply the path to the System Root as the first argument\n";
217  usage();
218  exit();
219  }
220 
221  if (!is_dir($config['system_root']) || !is_readable($config['system_root'].'/core/include/init.inc')) {
222  echo "ERROR: Path provided doesn't point to a Matrix installation's System Root. Please provide correct path and try again.\n";
223  usage();
224  exit();
225  }
226 
227  $config['assetids'] = (isset($_SERVER['argv'][2])) ? trim($_SERVER['argv'][2]) : '';
228  if (empty($config['assetids']) || strpos($config['assetids'], '--') === 0) {
229  echo "\nWARNING: You are running this update lookup on the whole system.\nThis is fine but it may take a long time\n\nYOU HAVE 5 SECONDS TO CANCEL THIS SCRIPT... ";
230  for ($i = 1; $i <= 5; $i++) {
231  sleep(1);
232  echo $i.' ';
233  }
234  $config['assetids'] = '';
235  }
236 
237  $config['batch_size'] = (int) get_parameterised_arg('--batch-size', 1000);
238  $config['verbose'] = (int) get_boolean_arg('--verbose');
239 
240  echo "\n";
241  echo "Updating lookups from asset(s): {$config['assetids']}.\n";
242  echo "Batch size: {$config['batch_size']}\n";
243  echo "\n";
244 
245 }//end function processArgs()
246 
247 
254 function get_boolean_arg($arg) {
255  $key = array_search($arg, $_SERVER['argv']);
256  if ($key) {
257  return TRUE;
258  } else {
259  return FALSE;
260  }//end if
261 
262 }//end get_boolean_arg()
263 
264 
272 function get_parameterised_arg($arg, $default) {
273  $key = array_search($arg, $_SERVER['argv']);
274  // Look for the parameter to this arg
275  if (($key) && (isset($_SERVER['argv'][$key + 1]))) {
276  return $_SERVER['argv'][$key + 1];
277  } else {
278  return $default;
279  }
280 
281 }//end get_parameterised_arg()
282 
283 
292 function get_line($prompt='')
293 {
294  echo $prompt;
295  // now get their entry and remove the trailing new line
296  return rtrim(fgets(STDIN, 4096));
297 
298 }//end get_line()
299 
300 
307 function _disconnectFromMatrixDatabase()
308 {
309  $conn_id = MatrixDAL::getCurrentDbId();
310  if (isset($conn_id) && !empty($conn_id)) {
312  MatrixDAL::dbClose($conn_id);
313  }//end if
314 
315 }//end _disconnectFromMatrixDatabase()
316 
317 
324 function _connectToMatrixDatabase()
325 {
326  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
327 
328 }//end _connectToMatrixDatabase()
329 
330 
335 function log_to_file($content, $file_name="set_metadata_schema.log")
336 {
337  file_put_contents($file_name, '['.date('d-m-Y h:i:s').'] '.$content."\n", FILE_APPEND);
338 
339 }//end log_to_file();
340 
341 
342 function getRootNodes($action)
343 {
344  $rootnodes = explode(',', $action);
345 
346  // Check if each of these rootnodes exists in the system
347  $rootnodes_exists = $GLOBALS['SQ_SYSTEM']->am->assetExists($rootnodes);
348 
349  $not_exists = array_diff($rootnodes, $rootnodes_exists);
350  if (!empty($not_exists)) {
351  $list_not_exists = implode(', ', $not_exists);
352  echo "These rootnode ids do not exists in the system: $list_not_exists \n";
353  exit();
354  }//end if
355 
356  return $rootnodes;
357 }//end getRootNodes()
358 
366 function getTreeSortedChildren($assetids)
367 {
368  $db = MatrixDAL::getDb();
369 
370  $todo_normal = Array();
371  $todo_shadows = Array();
372 
373  foreach ($assetids as $assetid) {
374  // check if we are updating lookups for a shadow asset, or a bridge
375  $id_parts = explode(':', $assetid);
376  if (isset($id_parts[1])) {
377  $todo_shadows = array_merge($todo_shadows, array_keys($GLOBALS['SQ_SYSTEM']->am->getChildren($assetid)));
378  } else {
379  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
380  if ($asset instanceof Bridge) {
381  if (!method_exists($asset, 'getChildren')) {
382  trigger_localised_error('SYS0204', E_USER_WARNING, $asset->name);
383  } else {
384  $todo_shadows = array_merge($todo_shadows, array_keys($asset->getChildren($assetid)));
385  }
386  }
387 
388  $where = 'l.minorid = :assetid';
389  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause($where, 't');
390  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause($where, 'l');
391  $sql = 'SELECT t.treeid
392  FROM '.SQ_TABLE_RUNNING_PREFIX.'ast_lnk_tree t INNER JOIN '.SQ_TABLE_RUNNING_PREFIX.'ast_lnk l ON t.linkid = l.linkid
393  '.$where;
394  $sql = db_extras_modify_limit_clause($sql, MatrixDAL::getDbType(), 1);
395 
396  try {
397  $query = MatrixDAL::preparePdoQuery($sql);
398  MatrixDAL::bindValueToPdo($query, 'assetid', $assetid);
399  $treeid = MatrixDAL::executePdoOne($query);
400  } catch (Exception $e) {
401  throw new Exception('Unable to get treeid for minorid: '.$assetid.' due to database error: '.$e->getMessage());
402  }
403 
404  $sql = 'SELECT l.minorid, MAX(LENGTH(t.treeid)) as length
405  FROM '.SQ_TABLE_RUNNING_PREFIX.'ast_lnk_tree t
406  INNER JOIN '.SQ_TABLE_RUNNING_PREFIX.'ast_lnk l ON t.linkid = l.linkid
407  ';
408  $where = 't.treeid LIKE :treeid
409  GROUP BY l.minorid ORDER BY length';
410 
411  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause($where, 't');
412  $where = $GLOBALS['SQ_SYSTEM']->constructRollbackWhereClause($where, 'l');
413 
414  try {
415  $query = MatrixDAL::preparePdoQuery($sql.$where);
416  MatrixDAL::bindValueToPdo($query, 'treeid', $treeid.'%');
417  $new_assets = MatrixDAL::executePdoAssoc($query);
418  } catch (Exception $e) {
419  throw new Exception('Unable to get minorids for treeid: '.$treeid[0]['treeid'].' due to database error: '.$e->getMessage());
420  }
421 
422  $todo_normal = array_merge($todo_normal, $new_assets);
423  }//end else
424 
425  }//end foreach
426 
427  // Make sure lower assets are done after higher ones
428  usort($todo_normal, create_function('$a, $b', 'return $a[\'length\'] > $b[\'length\'];'));
429  $todo_assetids = Array();
430  foreach($todo_normal as $asset_info) {
431  $todo_assetids[] = $asset_info['minorid'];
432  }
433 
434  $todo_assetids = array_unique(array_merge($todo_assetids, $todo_shadows));
435 
436  return $todo_assetids;
437 
438 }//end getTreeSortedChildren()
439 
440 ?>