xref: /template/strap/ComboStrap/Event.php (revision c3437056399326d621a01da73b649707fbb0ae69)
1*c3437056SNickeau<?php
2*c3437056SNickeau
3*c3437056SNickeau
4*c3437056SNickeaunamespace ComboStrap;
5*c3437056SNickeau
6*c3437056SNickeau/**
7*c3437056SNickeau * Class Event
8*c3437056SNickeau * @package ComboStrap
9*c3437056SNickeau * Asynchronous pub/sub system
10*c3437056SNickeau *
11*c3437056SNickeau * Dokuwiki allows event but they are synchronous
12*c3437056SNickeau * because php does not live in multiple thread
13*c3437056SNickeau *
14*c3437056SNickeau * With the help of Sqlite, we make them asynchronous
15*c3437056SNickeau */
16*c3437056SNickeauclass Event
17*c3437056SNickeau{
18*c3437056SNickeau
19*c3437056SNickeau    const EVENT_TABLE_NAME = "EVENTS_QUEUE";
20*c3437056SNickeau
21*c3437056SNickeau    const CANONICAL = "support";
22*c3437056SNickeau    const EVENT_NAME_ATTRIBUTE = "name";
23*c3437056SNickeau    const EVENT_DATA_ATTRIBUTE = "data";
24*c3437056SNickeau
25*c3437056SNickeau    /**
26*c3437056SNickeau     * process all replication request, created with {@link Event::createEvent()}
27*c3437056SNickeau     *
28*c3437056SNickeau     * by default, there is 5 pages in a default dokuwiki installation in the wiki namespace)
29*c3437056SNickeau     */
30*c3437056SNickeau    public static function dispatchEvent($maxEvent = 10)
31*c3437056SNickeau    {
32*c3437056SNickeau
33*c3437056SNickeau        $sqlite = Sqlite::createOrGetBackendSqlite();
34*c3437056SNickeau        if ($sqlite === null) {
35*c3437056SNickeau            LogUtility::msg("Sqlite is mandatory for asynchronous event");
36*c3437056SNickeau            return;
37*c3437056SNickeau        }
38*c3437056SNickeau
39*c3437056SNickeau        /**
40*c3437056SNickeau         * In case of a start or if there is a recursive bug
41*c3437056SNickeau         * We don't want to take all the resources
42*c3437056SNickeau         */
43*c3437056SNickeau        $maxBackgroundEventLow = 10;
44*c3437056SNickeau
45*c3437056SNickeau        $version = $sqlite->getVersion();
46*c3437056SNickeau        $rows = [];
47*c3437056SNickeau        if ($version > "3.35.0") {
48*c3437056SNickeau
49*c3437056SNickeau            // returning clause is available since 3.35 on delete
50*c3437056SNickeau            // https://www.sqlite.org/lang_returning.html
51*c3437056SNickeau
52*c3437056SNickeau            $eventTableName = self::EVENT_TABLE_NAME;
53*c3437056SNickeau            $statement = "delete from {$eventTableName} returning *";
54*c3437056SNickeau            // https://www.sqlite.org/lang_delete.html#optional_limit_and_order_by_clauses
55*c3437056SNickeau            if ($sqlite->hasOption("SQLITE_ENABLE_UPDATE_DELETE_LIMIT")) {
56*c3437056SNickeau                $statement .= "order by timestamp limit {$maxBackgroundEventLow}";
57*c3437056SNickeau            }
58*c3437056SNickeau            $request = $sqlite->createRequest()
59*c3437056SNickeau                ->setStatement($statement);
60*c3437056SNickeau            try {
61*c3437056SNickeau                $rows = $request->execute()
62*c3437056SNickeau                    ->getRows();
63*c3437056SNickeau                if (sizeof($rows) === 0) {
64*c3437056SNickeau                    return;
65*c3437056SNickeau                }
66*c3437056SNickeau            } catch (ExceptionCombo $e) {
67*c3437056SNickeau                LogUtility::msg($e->getMessage(), LogUtility::LVL_MSG_ERROR, $e->getCanonical());
68*c3437056SNickeau            } finally {
69*c3437056SNickeau                $request->close();
70*c3437056SNickeau            }
71*c3437056SNickeau
72*c3437056SNickeau        }
73*c3437056SNickeau
74*c3437056SNickeau        /**
75*c3437056SNickeau         * Error in the block before or not the good version
76*c3437056SNickeau         * We try to get the records with a select/delete
77*c3437056SNickeau         */
78*c3437056SNickeau        if (sizeof($rows) === 0) {
79*c3437056SNickeau
80*c3437056SNickeau
81*c3437056SNickeau            // technically the lock system of dokuwiki does not allow two process to run on
82*c3437056SNickeau            // the indexer, we trust it
83*c3437056SNickeau            $attributes = [self::EVENT_NAME_ATTRIBUTE, self::EVENT_DATA_ATTRIBUTE, DatabasePageRow::ROWID];
84*c3437056SNickeau            $select = Sqlite::createSelectFromTableAndColumns(self::EVENT_TABLE_NAME, $attributes);
85*c3437056SNickeau            $request = $sqlite->createRequest()
86*c3437056SNickeau                ->setQuery($select);
87*c3437056SNickeau
88*c3437056SNickeau            $rowsSelected = [];
89*c3437056SNickeau            try {
90*c3437056SNickeau                $rowsSelected = $request->execute()
91*c3437056SNickeau                    ->getRows();
92*c3437056SNickeau                if (sizeof($rowsSelected) === 0) {
93*c3437056SNickeau                    return;
94*c3437056SNickeau                }
95*c3437056SNickeau            } catch (ExceptionCombo $e) {
96*c3437056SNickeau                LogUtility::msg("Error while retrieving the event {$e->getMessage()}", LogUtility::LVL_MSG_ERROR, $e->getCanonical());
97*c3437056SNickeau                return;
98*c3437056SNickeau            } finally {
99*c3437056SNickeau                $request->close();
100*c3437056SNickeau            }
101*c3437056SNickeau
102*c3437056SNickeau            $eventTableName = self::EVENT_TABLE_NAME;
103*c3437056SNickeau            $rows = [];
104*c3437056SNickeau            foreach ($rowsSelected as $row) {
105*c3437056SNickeau                $request = $sqlite->createRequest()
106*c3437056SNickeau                    ->setQueryParametrized("delete from $eventTableName where rowid = ? ", [$row[DatabasePageRow::ROWID]]);
107*c3437056SNickeau                try {
108*c3437056SNickeau                    $changeCount = $request->execute()
109*c3437056SNickeau                        ->getChangeCount();
110*c3437056SNickeau                    if ($changeCount !== 1) {
111*c3437056SNickeau                        LogUtility::msg("The delete of the event was not successful or it was deleted by another process", LogUtility::LVL_MSG_ERROR);
112*c3437056SNickeau                    } else {
113*c3437056SNickeau                        $rows[] = $row;
114*c3437056SNickeau                    }
115*c3437056SNickeau                } catch (ExceptionCombo $e) {
116*c3437056SNickeau                    LogUtility::msg("Error while deleting the event. Message {$e->getMessage()}", LogUtility::LVL_MSG_ERROR, $e->getCanonical());
117*c3437056SNickeau                    return;
118*c3437056SNickeau                } finally {
119*c3437056SNickeau                    $request->close();
120*c3437056SNickeau                }
121*c3437056SNickeau            }
122*c3437056SNickeau
123*c3437056SNickeau
124*c3437056SNickeau        }
125*c3437056SNickeau
126*c3437056SNickeau
127*c3437056SNickeau        $eventCounter = 0;
128*c3437056SNickeau        foreach ($rows as $row) {
129*c3437056SNickeau            $eventCounter++;
130*c3437056SNickeau            $eventName = $row[self::EVENT_NAME_ATTRIBUTE];
131*c3437056SNickeau            $eventData = [];
132*c3437056SNickeau            $eventDataJson = $row[self::EVENT_DATA_ATTRIBUTE];
133*c3437056SNickeau            if ($eventDataJson !== null) {
134*c3437056SNickeau                try {
135*c3437056SNickeau                    $eventData = Json::createFromString($eventDataJson)->toArray();
136*c3437056SNickeau                } catch (ExceptionCombo $e) {
137*c3437056SNickeau                    LogUtility::msg("The stored data for the event $eventName was not in the json format");
138*c3437056SNickeau                    continue;
139*c3437056SNickeau                }
140*c3437056SNickeau            }
141*c3437056SNickeau            \dokuwiki\Extension\Event::createAndTrigger($eventName, $eventData);
142*c3437056SNickeau
143*c3437056SNickeau            if ($eventCounter >= $maxEvent) {
144*c3437056SNickeau                break;
145*c3437056SNickeau            }
146*c3437056SNickeau
147*c3437056SNickeau        }
148*c3437056SNickeau
149*c3437056SNickeau    }
150*c3437056SNickeau
151*c3437056SNickeau    /**
152*c3437056SNickeau     * Ask a replication in the background
153*c3437056SNickeau     * @param string $name - a string with the reason
154*c3437056SNickeau     * @param array $data
155*c3437056SNickeau     */
156*c3437056SNickeau    public static
157*c3437056SNickeau    function createEvent(string $name, array $data)
158*c3437056SNickeau    {
159*c3437056SNickeau
160*c3437056SNickeau        $sqlite = Sqlite::createOrGetBackendSqlite();
161*c3437056SNickeau        if ($sqlite === null) {
162*c3437056SNickeau            LogUtility::msg("Unable to create the event $name. Sqlite is not available");
163*c3437056SNickeau            return;
164*c3437056SNickeau        }
165*c3437056SNickeau
166*c3437056SNickeau
167*c3437056SNickeau        /**
168*c3437056SNickeau         * If not present
169*c3437056SNickeau         */
170*c3437056SNickeau        $entry = array(
171*c3437056SNickeau            "name" => $name,
172*c3437056SNickeau            "timestamp" => Iso8601Date::createFromNow()->toString()
173*c3437056SNickeau        );
174*c3437056SNickeau
175*c3437056SNickeau        if ($data !== null) {
176*c3437056SNickeau            $entry["data"] = Json::createFromArray($data)->toPrettyJsonString();
177*c3437056SNickeau        }
178*c3437056SNickeau
179*c3437056SNickeau        /**
180*c3437056SNickeau         * Execute
181*c3437056SNickeau         */
182*c3437056SNickeau        $request = $sqlite->createRequest()
183*c3437056SNickeau            ->setTableRow(self::EVENT_TABLE_NAME, $entry);
184*c3437056SNickeau        try {
185*c3437056SNickeau            $request->execute();
186*c3437056SNickeau        } catch (ExceptionCombo $e) {
187*c3437056SNickeau            LogUtility::msg("Unable to create the event $name. Error:" . $e->getMessage(), LogUtility::LVL_MSG_ERROR, $e->getCanonical());
188*c3437056SNickeau        } finally {
189*c3437056SNickeau            $request->close();
190*c3437056SNickeau        }
191*c3437056SNickeau
192*c3437056SNickeau
193*c3437056SNickeau    }
194*c3437056SNickeau
195*c3437056SNickeau    /**
196*c3437056SNickeau     * @param $pageId
197*c3437056SNickeau     *
198*c3437056SNickeau     * This is equivalent to {@link TaskRunner}
199*c3437056SNickeau     *
200*c3437056SNickeau     * lib/exe/taskrunner.php?id='.rawurlencode($ID)
201*c3437056SNickeau     * $taskRunner = new \dokuwiki\TaskRunner();
202*c3437056SNickeau     * $taskRunner->run();
203*c3437056SNickeau     *
204*c3437056SNickeau     */
205*c3437056SNickeau    public static function startTaskRunnerForPage($pageId)
206*c3437056SNickeau    {
207*c3437056SNickeau        $tmp = []; // No event data
208*c3437056SNickeau        $tmp['page'] = $pageId;
209*c3437056SNickeau        $evt = new \dokuwiki\Extension\Event('INDEXER_TASKS_RUN', $tmp);
210*c3437056SNickeau        $evt->advise_after();
211*c3437056SNickeau    }
212*c3437056SNickeau
213*c3437056SNickeau    public static function getQueueSize()
214*c3437056SNickeau    {
215*c3437056SNickeau        $sqlite = Sqlite::createOrGetBackendSqlite();
216*c3437056SNickeau        if ($sqlite === null) {
217*c3437056SNickeau            LogUtility::msg("Unable to count the number of event in the queue. Sqlite is not available");
218*c3437056SNickeau            return;
219*c3437056SNickeau        }
220*c3437056SNickeau
221*c3437056SNickeau
222*c3437056SNickeau        /**
223*c3437056SNickeau         * Execute
224*c3437056SNickeau         */
225*c3437056SNickeau        $request = $sqlite->createRequest()
226*c3437056SNickeau            ->setQuery("select count(1) from " . self::EVENT_TABLE_NAME);
227*c3437056SNickeau        $count = 0;
228*c3437056SNickeau        try {
229*c3437056SNickeau            $count = $request->execute()
230*c3437056SNickeau                ->getFirstCellValueAsInt();
231*c3437056SNickeau        } catch (ExceptionCombo $e) {
232*c3437056SNickeau            LogUtility::msg("Unable to count the number of event in the queue. Error:" . $e->getMessage(), LogUtility::LVL_MSG_ERROR, $e->getCanonical());
233*c3437056SNickeau        } finally {
234*c3437056SNickeau            $request->close();
235*c3437056SNickeau        }
236*c3437056SNickeau        return $count;
237*c3437056SNickeau    }
238*c3437056SNickeau
239*c3437056SNickeau
240*c3437056SNickeau}
241