xref: /plugin/struct/meta/Assignments.php (revision 0e9e058f643da238dab19c6509e3c380758631b3)
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);
59*0e9e058fSAndreas Gohr        $pagerows = $this->sqlite->res2arr($res);
60ed60c3b3SAndreas Gohr        $this->sqlite->res_close($res);
61ed60c3b3SAndreas Gohr
62ed60c3b3SAndreas Gohr        // reevalute the pages and assign when needed
63*0e9e058fSAndreas Gohr        foreach($pagerows as $row) {
64*0e9e058fSAndreas Gohr            $tables = $this->getPageAssignments($row['pid'], true);
65ed60c3b3SAndreas Gohr            if(in_array($table, $tables)) {
66*0e9e058fSAndreas Gohr                $this->assignPageSchema($row['pid'], $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);
91*0e9e058fSAndreas Gohr        $pagerows = $this->sqlite->res2arr($res);
92ed60c3b3SAndreas Gohr        $this->sqlite->res_close($res);
93ed60c3b3SAndreas Gohr
94ed60c3b3SAndreas Gohr        // reevalute the pages and unassign when needed
95*0e9e058fSAndreas Gohr        foreach($pagerows as $row) {
96be94e9d9SAndreas Gohr            $tables = $this->getPageAssignments($row['pid'], true);
97ed60c3b3SAndreas Gohr            if(!in_array($table, $tables)) {
98be94e9d9SAndreas Gohr                $this->deassignPageSchema($row['pid'], $table);
99ed60c3b3SAndreas Gohr            }
100ed60c3b3SAndreas Gohr        }
101ed60c3b3SAndreas Gohr
102ed60c3b3SAndreas Gohr        return $ok;
103ed60c3b3SAndreas Gohr    }
104ed60c3b3SAndreas Gohr
105ed60c3b3SAndreas Gohr    /**
106*0e9e058fSAndreas Gohr     * Clear all patterns - deassigns all pages
107*0e9e058fSAndreas Gohr     *
108*0e9e058fSAndreas Gohr     * This is mostly useful for testing and not used in the interface currently
109*0e9e058fSAndreas Gohr     *
110*0e9e058fSAndreas Gohr     * @return bool
111*0e9e058fSAndreas Gohr     */
112*0e9e058fSAndreas Gohr    public function clear() {
113*0e9e058fSAndreas Gohr        $sql = 'DELETE FROM schema_assignments_patterns';
114*0e9e058fSAndreas Gohr        $ok = (bool) $this->sqlite->query($sql);
115*0e9e058fSAndreas Gohr
116*0e9e058fSAndreas Gohr        $sql = 'UPDATE schema_assignments SET assigned = 0';
117*0e9e058fSAndreas Gohr        $ok = $ok && (bool) $this->sqlite->query($sql);
118*0e9e058fSAndreas Gohr
119*0e9e058fSAndreas Gohr        // reload patterns
120*0e9e058fSAndreas Gohr        $this->loadPatterns();
121*0e9e058fSAndreas Gohr
122*0e9e058fSAndreas Gohr        return $ok;
123*0e9e058fSAndreas Gohr    }
124*0e9e058fSAndreas Gohr
125*0e9e058fSAndreas Gohr    /**
126ed60c3b3SAndreas Gohr     * Add page to assignments
127ed60c3b3SAndreas Gohr     *
128ed60c3b3SAndreas Gohr     * @param string $page
129ed60c3b3SAndreas Gohr     * @param string $table
130ed60c3b3SAndreas Gohr     * @return bool
131ed60c3b3SAndreas Gohr     */
132*0e9e058fSAndreas Gohr    protected function assignPageSchema($page, $table) {
133ed60c3b3SAndreas Gohr        $sql = 'REPLACE INTO schema_assignments (pid, tbl, assigned) VALUES (?, ?, 1)';
134ed60c3b3SAndreas Gohr        return (bool) $this->sqlite->query($sql, array($page, $table));
135ed60c3b3SAndreas Gohr    }
136ed60c3b3SAndreas Gohr
137ed60c3b3SAndreas Gohr    /**
138ed60c3b3SAndreas Gohr     * Remove page from assignments
139ed60c3b3SAndreas Gohr     *
140ed60c3b3SAndreas Gohr     * @param string $page
141ed60c3b3SAndreas Gohr     * @param string $table
142ed60c3b3SAndreas Gohr     * @return bool
143ed60c3b3SAndreas Gohr     */
144*0e9e058fSAndreas Gohr    protected function deassignPageSchema($page, $table) {
145ed60c3b3SAndreas Gohr        $sql = 'REPLACE INTO schema_assignments (pid, tbl, assigned) VALUES (?, ?, 0)';
146ed60c3b3SAndreas Gohr        return (bool) $this->sqlite->query($sql, array($page, $table));
1471a8d1235SAndreas Gohr    }
1481a8d1235SAndreas Gohr
1491a8d1235SAndreas Gohr    /**
15049d38573SAndreas Gohr     * Get the whole pattern table
1511a8d1235SAndreas Gohr     *
1521a8d1235SAndreas Gohr     * @return array
1531a8d1235SAndreas Gohr     */
15433d7be6aSAndreas Gohr    public function getAllPatterns() {
15549d38573SAndreas Gohr        return $this->patterns;
1561a8d1235SAndreas Gohr    }
1571a8d1235SAndreas Gohr
1581a8d1235SAndreas Gohr    /**
159fb31ca9fSAndreas Gohr     * Returns a list of table names assigned to the given page
160fb31ca9fSAndreas Gohr     *
161fb31ca9fSAndreas Gohr     * @param string $page
1629ff81b7fSAndreas Gohr     * @param bool $checkpatterns Should the current patterns be re-evaluated?
1639ff81b7fSAndreas Gohr     * @return \string[] tables assigned
164fb31ca9fSAndreas Gohr     */
1659ff81b7fSAndreas Gohr    public function getPageAssignments($page, $checkpatterns=true) {
166fb31ca9fSAndreas Gohr        $tables = array();
167fb31ca9fSAndreas Gohr        $page = cleanID($page);
168fb31ca9fSAndreas Gohr
1699ff81b7fSAndreas Gohr        if($checkpatterns) {
1709ff81b7fSAndreas Gohr            // evaluate patterns
1719ff81b7fSAndreas Gohr            $pns = ':' . getNS($page) . ':';
17249d38573SAndreas Gohr            foreach($this->patterns as $row) {
173ed60c3b3SAndreas Gohr                if($this->matchPagePattern($row['pattern'], $page, $pns)) {
174ed60c3b3SAndreas Gohr                    $tables[] = $row['tbl'];
175fb31ca9fSAndreas Gohr                }
176fb31ca9fSAndreas Gohr            }
1779ff81b7fSAndreas Gohr        } else {
1789ff81b7fSAndreas Gohr            // just select
1799ff81b7fSAndreas Gohr            $sql = 'SELECT tbl FROM schema_assignments WHERE pid = ? AND assigned = 1';
1809ff81b7fSAndreas Gohr            $res = $this->sqlite->query($sql, array($page));
1819ff81b7fSAndreas Gohr            $list = $this->sqlite->res2arr($res);
1829ff81b7fSAndreas Gohr            $this->sqlite->res_close($res);
1839ff81b7fSAndreas Gohr            foreach($list as $row) {
1849ff81b7fSAndreas Gohr                $tables[] = $row['tbl'];
1859ff81b7fSAndreas Gohr            }
1869ff81b7fSAndreas Gohr        }
187fb31ca9fSAndreas Gohr
188fb31ca9fSAndreas Gohr        return array_unique($tables);
189fb31ca9fSAndreas Gohr    }
19056672c36SAndreas Gohr
19156672c36SAndreas Gohr    /**
192ed60c3b3SAndreas Gohr     * Check if the given pattern matches the given page
193ed60c3b3SAndreas Gohr     *
194ed60c3b3SAndreas Gohr     * @param string $pattern the pattern to check against
195ed60c3b3SAndreas Gohr     * @param string $page the cleaned pageid to check
196ed60c3b3SAndreas Gohr     * @param string|null $pns optimization, the colon wrapped namespace of the page, set null for automatic
197ed60c3b3SAndreas Gohr     * @return bool
198ed60c3b3SAndreas Gohr     */
199ed60c3b3SAndreas Gohr    protected function matchPagePattern($pattern, $page, $pns = null) {
200*0e9e058fSAndreas Gohr        if(trim($pattern,':') == '**') return true; // match all
201*0e9e058fSAndreas Gohr
202ed60c3b3SAndreas Gohr        if(is_null($pns)) {
203ed60c3b3SAndreas Gohr            $pns = ':' . getNS($page) . ':';
204ed60c3b3SAndreas Gohr        }
205ed60c3b3SAndreas Gohr
206ed60c3b3SAndreas Gohr        $ans = ':' . cleanID($pattern) . ':';
207ed60c3b3SAndreas Gohr
208ed60c3b3SAndreas Gohr        if(substr($pattern, -2) == '**') {
209ed60c3b3SAndreas Gohr            // upper namespaces match
210ed60c3b3SAndreas Gohr            if(strpos($pns, $ans) === 0) {
211ed60c3b3SAndreas Gohr                return true;
212ed60c3b3SAndreas Gohr            }
213ed60c3b3SAndreas Gohr        } else if(substr($pattern, -1) == '*') {
214ed60c3b3SAndreas Gohr            // namespaces match exact
215ed60c3b3SAndreas Gohr            if($ans == $pns) {
216ed60c3b3SAndreas Gohr                return true;
217ed60c3b3SAndreas Gohr            }
218ed60c3b3SAndreas Gohr        } else {
219ed60c3b3SAndreas Gohr            // exact match
220ed60c3b3SAndreas Gohr            if(cleanID($pattern) == $page) {
221ed60c3b3SAndreas Gohr                return true;
222ed60c3b3SAndreas Gohr            }
223ed60c3b3SAndreas Gohr        }
224ed60c3b3SAndreas Gohr
225ed60c3b3SAndreas Gohr        return false;
226ed60c3b3SAndreas Gohr    }
227ed60c3b3SAndreas Gohr
228ed60c3b3SAndreas Gohr    /**
22956672c36SAndreas Gohr     * Returns all tables of schemas that existed and stored data for the page back then
23056672c36SAndreas Gohr     *
231*0e9e058fSAndreas Gohr     * @deprecated because we're always only interested in the current state of affairs, even when restoring.
23256672c36SAndreas Gohr     *
23356672c36SAndreas Gohr     * @param string $page
23456672c36SAndreas Gohr     * @param string $ts
23556672c36SAndreas Gohr     * @return array
23656672c36SAndreas Gohr     */
23756672c36SAndreas Gohr    public function getHistoricAssignments($page, $ts) {
23856672c36SAndreas Gohr        $sql = "SELECT DISTINCT tbl FROM schemas WHERE ts <= ? ORDER BY ts DESC";
23956672c36SAndreas Gohr        $res = $this->sqlite->query($sql, $ts);
24056672c36SAndreas Gohr        $tables = $this->sqlite->res2arr($res);
24156672c36SAndreas Gohr        $this->sqlite->res_close($res);
24256672c36SAndreas Gohr
24356672c36SAndreas Gohr        $assigned = array();
24456672c36SAndreas Gohr        foreach($tables as $row) {
24556672c36SAndreas Gohr            $table = $row['tbl'];
246ed60c3b3SAndreas Gohr            /** @noinspection SqlResolve */
24756672c36SAndreas Gohr            $sql = "SELECT pid FROM data_$table WHERE pid = ? AND rev <= ? LIMIT 1";
24856672c36SAndreas Gohr            $res = $this->sqlite->query($sql, $page, $ts);
24956672c36SAndreas Gohr            $found = $this->sqlite->res2arr($res);
25056672c36SAndreas Gohr            $this->sqlite->res_close($res);
25156672c36SAndreas Gohr
25256672c36SAndreas Gohr            if($found) $assigned[] = $table;
25356672c36SAndreas Gohr        }
25456672c36SAndreas Gohr
25556672c36SAndreas Gohr        return $assigned;
25656672c36SAndreas Gohr    }
257fb31ca9fSAndreas Gohr}
258