1fb31ca9fSAndreas Gohr<?php 2fb31ca9fSAndreas Gohr 3fb31ca9fSAndreas Gohrnamespace plugin\struct\meta; 4fb31ca9fSAndreas Gohr 51a8d1235SAndreas Gohr/** 61a8d1235SAndreas Gohr * Class Assignments 71a8d1235SAndreas Gohr * 81a8d1235SAndreas Gohr * Manages the assignment of schemas (table names) to pages and namespaces 91a8d1235SAndreas Gohr * 101a8d1235SAndreas Gohr * @package plugin\struct\meta 111a8d1235SAndreas Gohr */ 12fb31ca9fSAndreas Gohrclass Assignments { 13fb31ca9fSAndreas Gohr 14fb31ca9fSAndreas Gohr /** @var \helper_plugin_sqlite|null */ 15fb31ca9fSAndreas Gohr protected $sqlite; 16fb31ca9fSAndreas Gohr 1733d7be6aSAndreas Gohr /** @var array All the assignments patterns */ 1849d38573SAndreas Gohr protected $patterns; 19fb31ca9fSAndreas Gohr 20fb31ca9fSAndreas Gohr /** 21fb31ca9fSAndreas Gohr * Assignments constructor. 22fb31ca9fSAndreas Gohr */ 23fb31ca9fSAndreas Gohr public function __construct() { 24fb31ca9fSAndreas Gohr /** @var \helper_plugin_struct_db $helper */ 25fb31ca9fSAndreas Gohr $helper = plugin_load('helper', 'struct_db'); 26fb31ca9fSAndreas Gohr $this->sqlite = $helper->getDB(); 27fb31ca9fSAndreas Gohr 2833d7be6aSAndreas Gohr if($this->sqlite) $this->loadPatterns(); 29fb31ca9fSAndreas Gohr } 30fb31ca9fSAndreas Gohr 31fb31ca9fSAndreas Gohr /** 3249d38573SAndreas Gohr * Load existing assignment patterns 33fb31ca9fSAndreas Gohr */ 3433d7be6aSAndreas Gohr protected function loadPatterns() { 3549d38573SAndreas Gohr $sql = 'SELECT * FROM schema_assignments_patterns ORDER BY pattern'; 36fb31ca9fSAndreas Gohr $res = $this->sqlite->query($sql); 3749d38573SAndreas Gohr $this->patterns = $this->sqlite->res2arr($res); 38fb31ca9fSAndreas Gohr $this->sqlite->res_close($res); 39fb31ca9fSAndreas Gohr } 40fb31ca9fSAndreas Gohr 41fb31ca9fSAndreas Gohr /** 4249d38573SAndreas Gohr * Add a new assignment pattern to the pattern table 431a8d1235SAndreas Gohr * 4449d38573SAndreas Gohr * @param string $pattern 451a8d1235SAndreas Gohr * @param string $table 461a8d1235SAndreas Gohr * @return bool 471a8d1235SAndreas Gohr */ 4833d7be6aSAndreas Gohr public function addPattern($pattern, $table) { 49*ed60c3b3SAndreas Gohr // add the pattern 5049d38573SAndreas Gohr $sql = 'REPLACE INTO schema_assignments_patterns (pattern, tbl) VALUES (?,?)'; 51*ed60c3b3SAndreas Gohr $ok = (bool) $this->sqlite->query($sql, array($pattern, $table)); 52*ed60c3b3SAndreas Gohr 53*ed60c3b3SAndreas Gohr // reload patterns 54*ed60c3b3SAndreas Gohr $this->loadPatterns(); 55*ed60c3b3SAndreas Gohr 56*ed60c3b3SAndreas Gohr // fetch all pages where the schema isn't assigned, yet 57*ed60c3b3SAndreas Gohr $sql = 'SELECT pid FROM schema_assignments WHERE tbl != ? OR assigned != 1'; 58*ed60c3b3SAndreas Gohr $res = $this->sqlite->query($sql, $table); 59*ed60c3b3SAndreas Gohr $pages = $this->sqlite->res2arr($res); 60*ed60c3b3SAndreas Gohr $this->sqlite->res_close($res); 61*ed60c3b3SAndreas Gohr 62*ed60c3b3SAndreas Gohr // reevalute the pages and assign when needed 63*ed60c3b3SAndreas Gohr foreach($pages as $page) { 64*ed60c3b3SAndreas Gohr $tables = $this->getPageAssignments($page); 65*ed60c3b3SAndreas Gohr if(in_array($table, $tables)) { 66*ed60c3b3SAndreas Gohr $this->assignPageSchema($page, $table); 67*ed60c3b3SAndreas Gohr } 68*ed60c3b3SAndreas Gohr } 69*ed60c3b3SAndreas Gohr 70*ed60c3b3SAndreas Gohr return $ok; 711a8d1235SAndreas Gohr } 721a8d1235SAndreas Gohr 731a8d1235SAndreas Gohr /** 7449d38573SAndreas Gohr * Remove an existing assignment pattern from the pattern table 751a8d1235SAndreas Gohr * 7649d38573SAndreas Gohr * @param string $pattern 771a8d1235SAndreas Gohr * @param string $table 781a8d1235SAndreas Gohr * @return bool 791a8d1235SAndreas Gohr */ 8033d7be6aSAndreas Gohr public function removePattern($pattern, $table) { 81*ed60c3b3SAndreas Gohr // remove the pattern 8249d38573SAndreas Gohr $sql = 'DELETE FROM schema_assignments_patterns WHERE pattern = ? AND tbl = ?'; 83*ed60c3b3SAndreas Gohr $ok = (bool) $this->sqlite->query($sql, array($pattern, $table)); 84*ed60c3b3SAndreas Gohr 85*ed60c3b3SAndreas Gohr // reload patterns 86*ed60c3b3SAndreas Gohr $this->loadPatterns(); 87*ed60c3b3SAndreas Gohr 88*ed60c3b3SAndreas Gohr // fetch possibly affected pages 89*ed60c3b3SAndreas Gohr $sql = 'SELECT pid FROM schema_assignments WHERE tbl = ?'; 90*ed60c3b3SAndreas Gohr $res = $this->sqlite->query($sql, $table); 91*ed60c3b3SAndreas Gohr $pages = $this->sqlite->res2arr($res); 92*ed60c3b3SAndreas Gohr $this->sqlite->res_close($res); 93*ed60c3b3SAndreas Gohr 94*ed60c3b3SAndreas Gohr // reevalute the pages and unassign when needed 95*ed60c3b3SAndreas Gohr foreach($pages as $page) { 96*ed60c3b3SAndreas Gohr $tables = $this->getPageAssignments($page); 97*ed60c3b3SAndreas Gohr if(!in_array($table, $tables)) { 98*ed60c3b3SAndreas Gohr $this->deassignPageSchema($page, $table); 99*ed60c3b3SAndreas Gohr } 100*ed60c3b3SAndreas Gohr } 101*ed60c3b3SAndreas Gohr 102*ed60c3b3SAndreas Gohr return $ok; 103*ed60c3b3SAndreas Gohr } 104*ed60c3b3SAndreas Gohr 105*ed60c3b3SAndreas Gohr /** 106*ed60c3b3SAndreas Gohr * Add page to assignments 107*ed60c3b3SAndreas Gohr * 108*ed60c3b3SAndreas Gohr * @param string $page 109*ed60c3b3SAndreas Gohr * @param string $table 110*ed60c3b3SAndreas Gohr * @return bool 111*ed60c3b3SAndreas Gohr */ 112*ed60c3b3SAndreas Gohr public function assignPageSchema($page, $table) { 113*ed60c3b3SAndreas Gohr $sql = 'REPLACE INTO schema_assignments (pid, tbl, assigned) VALUES (?, ?, 1)'; 114*ed60c3b3SAndreas Gohr return (bool) $this->sqlite->query($sql, array($page, $table)); 115*ed60c3b3SAndreas Gohr } 116*ed60c3b3SAndreas Gohr 117*ed60c3b3SAndreas Gohr /** 118*ed60c3b3SAndreas Gohr * Remove page from assignments 119*ed60c3b3SAndreas Gohr * 120*ed60c3b3SAndreas Gohr * @param string $page 121*ed60c3b3SAndreas Gohr * @param string $table 122*ed60c3b3SAndreas Gohr * @return bool 123*ed60c3b3SAndreas Gohr */ 124*ed60c3b3SAndreas Gohr public function deassignPageSchema($page, $table) { 125*ed60c3b3SAndreas Gohr $sql = 'REPLACE INTO schema_assignments (pid, tbl, assigned) VALUES (?, ?, 0)'; 126*ed60c3b3SAndreas Gohr return (bool) $this->sqlite->query($sql, array($page, $table)); 1271a8d1235SAndreas Gohr } 1281a8d1235SAndreas Gohr 1291a8d1235SAndreas Gohr /** 13049d38573SAndreas Gohr * Get the whole pattern table 1311a8d1235SAndreas Gohr * 1321a8d1235SAndreas Gohr * @return array 1331a8d1235SAndreas Gohr */ 13433d7be6aSAndreas Gohr public function getAllPatterns() { 13549d38573SAndreas Gohr return $this->patterns; 1361a8d1235SAndreas Gohr } 1371a8d1235SAndreas Gohr 1381a8d1235SAndreas Gohr /** 139fb31ca9fSAndreas Gohr * Returns a list of table names assigned to the given page 140fb31ca9fSAndreas Gohr * 141fb31ca9fSAndreas Gohr * @param string $page 142fb31ca9fSAndreas Gohr * @return string[] tables assigned 143fb31ca9fSAndreas Gohr */ 144fb31ca9fSAndreas Gohr public function getPageAssignments($page) { 145fb31ca9fSAndreas Gohr $tables = array(); 146fb31ca9fSAndreas Gohr 147fb31ca9fSAndreas Gohr $page = cleanID($page); 148fb31ca9fSAndreas Gohr $pns = ':' . getNS($page) . ':'; 149fb31ca9fSAndreas Gohr 15049d38573SAndreas Gohr foreach($this->patterns as $row) { 151*ed60c3b3SAndreas Gohr if($this->matchPagePattern($row['pattern'], $page, $pns)) { 152*ed60c3b3SAndreas Gohr $tables[] = $row['tbl']; 153fb31ca9fSAndreas Gohr } 154fb31ca9fSAndreas Gohr } 155fb31ca9fSAndreas Gohr 156fb31ca9fSAndreas Gohr return array_unique($tables); 157fb31ca9fSAndreas Gohr } 15856672c36SAndreas Gohr 15956672c36SAndreas Gohr /** 160*ed60c3b3SAndreas Gohr * Check if the given pattern matches the given page 161*ed60c3b3SAndreas Gohr * 162*ed60c3b3SAndreas Gohr * @param string $pattern the pattern to check against 163*ed60c3b3SAndreas Gohr * @param string $page the cleaned pageid to check 164*ed60c3b3SAndreas Gohr * @param string|null $pns optimization, the colon wrapped namespace of the page, set null for automatic 165*ed60c3b3SAndreas Gohr * @return bool 166*ed60c3b3SAndreas Gohr */ 167*ed60c3b3SAndreas Gohr protected function matchPagePattern($pattern, $page, $pns = null) { 168*ed60c3b3SAndreas Gohr if(is_null($pns)) { 169*ed60c3b3SAndreas Gohr $pns = ':' . getNS($page) . ':'; 170*ed60c3b3SAndreas Gohr } 171*ed60c3b3SAndreas Gohr 172*ed60c3b3SAndreas Gohr $ans = ':' . cleanID($pattern) . ':'; 173*ed60c3b3SAndreas Gohr 174*ed60c3b3SAndreas Gohr if(substr($pattern, -2) == '**') { 175*ed60c3b3SAndreas Gohr // upper namespaces match 176*ed60c3b3SAndreas Gohr if(strpos($pns, $ans) === 0) { 177*ed60c3b3SAndreas Gohr return true; 178*ed60c3b3SAndreas Gohr } 179*ed60c3b3SAndreas Gohr } else if(substr($pattern, -1) == '*') { 180*ed60c3b3SAndreas Gohr // namespaces match exact 181*ed60c3b3SAndreas Gohr if($ans == $pns) { 182*ed60c3b3SAndreas Gohr return true; 183*ed60c3b3SAndreas Gohr } 184*ed60c3b3SAndreas Gohr } else { 185*ed60c3b3SAndreas Gohr // exact match 186*ed60c3b3SAndreas Gohr if(cleanID($pattern) == $page) { 187*ed60c3b3SAndreas Gohr return true; 188*ed60c3b3SAndreas Gohr } 189*ed60c3b3SAndreas Gohr } 190*ed60c3b3SAndreas Gohr 191*ed60c3b3SAndreas Gohr return false; 192*ed60c3b3SAndreas Gohr } 193*ed60c3b3SAndreas Gohr 194*ed60c3b3SAndreas Gohr /** 19556672c36SAndreas Gohr * Returns all tables of schemas that existed and stored data for the page back then 19656672c36SAndreas Gohr * 19756672c36SAndreas Gohr * @todo this is not used currently and can probably be removed again, because we're 19856672c36SAndreas Gohr * always only interested in the current state of affairs, even when restoring. 19956672c36SAndreas Gohr * 20056672c36SAndreas Gohr * @param string $page 20156672c36SAndreas Gohr * @param string $ts 20256672c36SAndreas Gohr * @return array 20356672c36SAndreas Gohr */ 20456672c36SAndreas Gohr public function getHistoricAssignments($page, $ts) { 20556672c36SAndreas Gohr $sql = "SELECT DISTINCT tbl FROM schemas WHERE ts <= ? ORDER BY ts DESC"; 20656672c36SAndreas Gohr $res = $this->sqlite->query($sql, $ts); 20756672c36SAndreas Gohr $tables = $this->sqlite->res2arr($res); 20856672c36SAndreas Gohr $this->sqlite->res_close($res); 20956672c36SAndreas Gohr 21056672c36SAndreas Gohr $assigned = array(); 21156672c36SAndreas Gohr foreach($tables as $row) { 21256672c36SAndreas Gohr $table = $row['tbl']; 213*ed60c3b3SAndreas Gohr /** @noinspection SqlResolve */ 21456672c36SAndreas Gohr $sql = "SELECT pid FROM data_$table WHERE pid = ? AND rev <= ? LIMIT 1"; 21556672c36SAndreas Gohr $res = $this->sqlite->query($sql, $page, $ts); 21656672c36SAndreas Gohr $found = $this->sqlite->res2arr($res); 21756672c36SAndreas Gohr $this->sqlite->res_close($res); 21856672c36SAndreas Gohr 21956672c36SAndreas Gohr if($found) $assigned[] = $table; 22056672c36SAndreas Gohr } 22156672c36SAndreas Gohr 22256672c36SAndreas Gohr return $assigned; 22356672c36SAndreas Gohr } 224fb31ca9fSAndreas Gohr} 225