xref: /plugin/struct/meta/Assignments.php (revision ed60c3b35483367298781f7b408d38ff6458040d)
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