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