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) { 49ed60c3b3SAndreas Gohr // add the pattern 5049d38573SAndreas Gohr $sql = 'REPLACE INTO schema_assignments_patterns (pattern, tbl) VALUES (?,?)'; 51ed60c3b3SAndreas Gohr $ok = (bool) $this->sqlite->query($sql, array($pattern, $table)); 52ed60c3b3SAndreas Gohr 53ed60c3b3SAndreas Gohr // reload patterns 54ed60c3b3SAndreas Gohr $this->loadPatterns(); 55ed60c3b3SAndreas Gohr 56ed60c3b3SAndreas Gohr // fetch all pages where the schema isn't assigned, yet 57ed60c3b3SAndreas Gohr $sql = 'SELECT pid FROM schema_assignments WHERE tbl != ? OR assigned != 1'; 58ed60c3b3SAndreas Gohr $res = $this->sqlite->query($sql, $table); 59ed60c3b3SAndreas Gohr $pages = $this->sqlite->res2arr($res); 60ed60c3b3SAndreas Gohr $this->sqlite->res_close($res); 61ed60c3b3SAndreas Gohr 62ed60c3b3SAndreas Gohr // reevalute the pages and assign when needed 63ed60c3b3SAndreas Gohr foreach($pages as $page) { 649ff81b7fSAndreas Gohr $tables = $this->getPageAssignments($page, true); 65ed60c3b3SAndreas Gohr if(in_array($table, $tables)) { 66ed60c3b3SAndreas Gohr $this->assignPageSchema($page, $table); 67ed60c3b3SAndreas Gohr } 68ed60c3b3SAndreas Gohr } 69ed60c3b3SAndreas Gohr 70ed60c3b3SAndreas 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) { 81ed60c3b3SAndreas Gohr // remove the pattern 8249d38573SAndreas Gohr $sql = 'DELETE FROM schema_assignments_patterns WHERE pattern = ? AND tbl = ?'; 83ed60c3b3SAndreas Gohr $ok = (bool) $this->sqlite->query($sql, array($pattern, $table)); 84ed60c3b3SAndreas Gohr 85ed60c3b3SAndreas Gohr // reload patterns 86ed60c3b3SAndreas Gohr $this->loadPatterns(); 87ed60c3b3SAndreas Gohr 88ed60c3b3SAndreas Gohr // fetch possibly affected pages 89ed60c3b3SAndreas Gohr $sql = 'SELECT pid FROM schema_assignments WHERE tbl = ?'; 90ed60c3b3SAndreas Gohr $res = $this->sqlite->query($sql, $table); 91ed60c3b3SAndreas Gohr $pages = $this->sqlite->res2arr($res); 92ed60c3b3SAndreas Gohr $this->sqlite->res_close($res); 93ed60c3b3SAndreas Gohr 94ed60c3b3SAndreas Gohr // reevalute the pages and unassign when needed 95*be94e9d9SAndreas Gohr foreach($pages as $row) { 96*be94e9d9SAndreas Gohr $tables = $this->getPageAssignments($row['pid'], true); 97ed60c3b3SAndreas Gohr if(!in_array($table, $tables)) { 98*be94e9d9SAndreas Gohr $this->deassignPageSchema($row['pid'], $table); 99ed60c3b3SAndreas Gohr } 100ed60c3b3SAndreas Gohr } 101ed60c3b3SAndreas Gohr 102ed60c3b3SAndreas Gohr return $ok; 103ed60c3b3SAndreas Gohr } 104ed60c3b3SAndreas Gohr 105ed60c3b3SAndreas Gohr /** 106ed60c3b3SAndreas Gohr * Add page to assignments 107ed60c3b3SAndreas Gohr * 108ed60c3b3SAndreas Gohr * @param string $page 109ed60c3b3SAndreas Gohr * @param string $table 110ed60c3b3SAndreas Gohr * @return bool 111ed60c3b3SAndreas Gohr */ 112ed60c3b3SAndreas Gohr public function assignPageSchema($page, $table) { 113ed60c3b3SAndreas Gohr $sql = 'REPLACE INTO schema_assignments (pid, tbl, assigned) VALUES (?, ?, 1)'; 114ed60c3b3SAndreas Gohr return (bool) $this->sqlite->query($sql, array($page, $table)); 115ed60c3b3SAndreas Gohr } 116ed60c3b3SAndreas Gohr 117ed60c3b3SAndreas Gohr /** 118ed60c3b3SAndreas Gohr * Remove page from assignments 119ed60c3b3SAndreas Gohr * 120ed60c3b3SAndreas Gohr * @param string $page 121ed60c3b3SAndreas Gohr * @param string $table 122ed60c3b3SAndreas Gohr * @return bool 123ed60c3b3SAndreas Gohr */ 124ed60c3b3SAndreas Gohr public function deassignPageSchema($page, $table) { 125ed60c3b3SAndreas Gohr $sql = 'REPLACE INTO schema_assignments (pid, tbl, assigned) VALUES (?, ?, 0)'; 126ed60c3b3SAndreas 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 1429ff81b7fSAndreas Gohr * @param bool $checkpatterns Should the current patterns be re-evaluated? 1439ff81b7fSAndreas Gohr * @return \string[] tables assigned 144fb31ca9fSAndreas Gohr */ 1459ff81b7fSAndreas Gohr public function getPageAssignments($page, $checkpatterns=true) { 146fb31ca9fSAndreas Gohr $tables = array(); 147fb31ca9fSAndreas Gohr $page = cleanID($page); 148fb31ca9fSAndreas Gohr 1499ff81b7fSAndreas Gohr if($checkpatterns) { 1509ff81b7fSAndreas Gohr // evaluate patterns 1519ff81b7fSAndreas Gohr $pns = ':' . getNS($page) . ':'; 15249d38573SAndreas Gohr foreach($this->patterns as $row) { 153ed60c3b3SAndreas Gohr if($this->matchPagePattern($row['pattern'], $page, $pns)) { 154ed60c3b3SAndreas Gohr $tables[] = $row['tbl']; 155fb31ca9fSAndreas Gohr } 156fb31ca9fSAndreas Gohr } 1579ff81b7fSAndreas Gohr } else { 1589ff81b7fSAndreas Gohr // just select 1599ff81b7fSAndreas Gohr $sql = 'SELECT tbl FROM schema_assignments WHERE pid = ? AND assigned = 1'; 1609ff81b7fSAndreas Gohr $res = $this->sqlite->query($sql, array($page)); 1619ff81b7fSAndreas Gohr $list = $this->sqlite->res2arr($res); 1629ff81b7fSAndreas Gohr $this->sqlite->res_close($res); 1639ff81b7fSAndreas Gohr foreach($list as $row) { 1649ff81b7fSAndreas Gohr $tables[] = $row['tbl']; 1659ff81b7fSAndreas Gohr } 1669ff81b7fSAndreas Gohr } 167fb31ca9fSAndreas Gohr 168fb31ca9fSAndreas Gohr return array_unique($tables); 169fb31ca9fSAndreas Gohr } 17056672c36SAndreas Gohr 17156672c36SAndreas Gohr /** 172ed60c3b3SAndreas Gohr * Check if the given pattern matches the given page 173ed60c3b3SAndreas Gohr * 174ed60c3b3SAndreas Gohr * @param string $pattern the pattern to check against 175ed60c3b3SAndreas Gohr * @param string $page the cleaned pageid to check 176ed60c3b3SAndreas Gohr * @param string|null $pns optimization, the colon wrapped namespace of the page, set null for automatic 177ed60c3b3SAndreas Gohr * @return bool 178ed60c3b3SAndreas Gohr */ 179ed60c3b3SAndreas Gohr protected function matchPagePattern($pattern, $page, $pns = null) { 180ed60c3b3SAndreas Gohr if(is_null($pns)) { 181ed60c3b3SAndreas Gohr $pns = ':' . getNS($page) . ':'; 182ed60c3b3SAndreas Gohr } 183ed60c3b3SAndreas Gohr 184ed60c3b3SAndreas Gohr $ans = ':' . cleanID($pattern) . ':'; 185ed60c3b3SAndreas Gohr 186ed60c3b3SAndreas Gohr if(substr($pattern, -2) == '**') { 187ed60c3b3SAndreas Gohr // upper namespaces match 188ed60c3b3SAndreas Gohr if(strpos($pns, $ans) === 0) { 189ed60c3b3SAndreas Gohr return true; 190ed60c3b3SAndreas Gohr } 191ed60c3b3SAndreas Gohr } else if(substr($pattern, -1) == '*') { 192ed60c3b3SAndreas Gohr // namespaces match exact 193ed60c3b3SAndreas Gohr if($ans == $pns) { 194ed60c3b3SAndreas Gohr return true; 195ed60c3b3SAndreas Gohr } 196ed60c3b3SAndreas Gohr } else { 197ed60c3b3SAndreas Gohr // exact match 198ed60c3b3SAndreas Gohr if(cleanID($pattern) == $page) { 199ed60c3b3SAndreas Gohr return true; 200ed60c3b3SAndreas Gohr } 201ed60c3b3SAndreas Gohr } 202ed60c3b3SAndreas Gohr 203ed60c3b3SAndreas Gohr return false; 204ed60c3b3SAndreas Gohr } 205ed60c3b3SAndreas Gohr 206ed60c3b3SAndreas Gohr /** 20756672c36SAndreas Gohr * Returns all tables of schemas that existed and stored data for the page back then 20856672c36SAndreas Gohr * 20956672c36SAndreas Gohr * @todo this is not used currently and can probably be removed again, because we're 21056672c36SAndreas Gohr * always only interested in the current state of affairs, even when restoring. 21156672c36SAndreas Gohr * 21256672c36SAndreas Gohr * @param string $page 21356672c36SAndreas Gohr * @param string $ts 21456672c36SAndreas Gohr * @return array 21556672c36SAndreas Gohr */ 21656672c36SAndreas Gohr public function getHistoricAssignments($page, $ts) { 21756672c36SAndreas Gohr $sql = "SELECT DISTINCT tbl FROM schemas WHERE ts <= ? ORDER BY ts DESC"; 21856672c36SAndreas Gohr $res = $this->sqlite->query($sql, $ts); 21956672c36SAndreas Gohr $tables = $this->sqlite->res2arr($res); 22056672c36SAndreas Gohr $this->sqlite->res_close($res); 22156672c36SAndreas Gohr 22256672c36SAndreas Gohr $assigned = array(); 22356672c36SAndreas Gohr foreach($tables as $row) { 22456672c36SAndreas Gohr $table = $row['tbl']; 225ed60c3b3SAndreas Gohr /** @noinspection SqlResolve */ 22656672c36SAndreas Gohr $sql = "SELECT pid FROM data_$table WHERE pid = ? AND rev <= ? LIMIT 1"; 22756672c36SAndreas Gohr $res = $this->sqlite->query($sql, $page, $ts); 22856672c36SAndreas Gohr $found = $this->sqlite->res2arr($res); 22956672c36SAndreas Gohr $this->sqlite->res_close($res); 23056672c36SAndreas Gohr 23156672c36SAndreas Gohr if($found) $assigned[] = $table; 23256672c36SAndreas Gohr } 23356672c36SAndreas Gohr 23456672c36SAndreas Gohr return $assigned; 23556672c36SAndreas Gohr } 236fb31ca9fSAndreas Gohr} 237