ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilAtomQueryBase.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
27 abstract class ilAtomQueryBase
28 {
29  protected const ITERATIONS = 10;
33  protected static array $available_isolations_levels = array(
38  );
42  protected static array $possible_anomalies = array(
47  );
51  protected static array $anomalies_map = array(
57  ),
61  ),
64  ),
66  );
71  protected array $tables = array();
75  protected $query;
76  protected \ilDBInterface $ilDBInstance;
77 
82  public function __construct(ilDBInterface $ilDBInstance, int $isolation_level = ilAtomQuery::ISOLATION_SERIALIZABLE)
83  {
84  static::checkIsolationLevel($isolation_level);
85  $this->ilDBInstance = $ilDBInstance;
86  $this->isolation_level = $isolation_level;
87  }
88 
89  //
90  //
91  //
95  public function getRisks(): array
96  {
97  return static::getPossibleAnomalies($this->getIsolationLevel());
98  }
99 
106  public function addTableLock(string $table_name): ilTableLockInterface
107  {
108  $ilTableLock = new ilTableLock($table_name, $this->ilDBInstance);
109  $ilTableLock->setLockLevel($this->getDeterminedLockLevel());
110  $this->tables[] = $ilTableLock;
111 
112  return $ilTableLock;
113  }
114 
115  protected function getDeterminedLockLevel(): int
116  {
118  }
119 
135  public function addQueryCallable(callable $query): void
136  {
137  if ($this->query) {
139  }
140  if (!$this->checkCallable($query)) {
142  }
143  $this->query = $query;
144  }
145 
149  public function replaceQueryCallable(callable $query): void
150  {
151  if (!$this->checkCallable($query)) {
153  }
154  $this->query = $query;
155  }
156 
161  abstract public function run(): void;
162 
163  public function getIsolationLevel(): int
164  {
165  return $this->isolation_level;
166  }
167 
171  public static function isThereRiskThat(int $isolation_level, int $anomaly): bool
172  {
173  static::checkIsolationLevel($isolation_level);
174  static::checkAnomaly($anomaly);
175 
176  return in_array($anomaly, static::getPossibleAnomalies($isolation_level));
177  }
178 
182  public static function getPossibleAnomalies(int $isolation_level): array
183  {
184  static::checkIsolationLevel($isolation_level);
185 
186  return self::$anomalies_map[$isolation_level];
187  }
188 
192  public static function checkIsolationLevel(int $isolation_level): void
193  {
194  // The following Isolations are currently not supported
195  if (in_array($isolation_level, array(
199  ))) {
200  throw new ilAtomQueryException('Level: ' . $isolation_level, ilAtomQueryException::DB_ATOM_ISO_WRONG_LEVEL);
201  }
202  // Check if a available Isolation level is selected
203  if (!in_array($isolation_level, self::$available_isolations_levels)) {
204  throw new ilAtomQueryException('Level: ' . $isolation_level, ilAtomQueryException::DB_ATOM_ISO_WRONG_LEVEL);
205  }
206  }
207 
211  public static function checkAnomaly(int $anomaly): void
212  {
213  if (!in_array($anomaly, self::$possible_anomalies)) {
215  }
216  }
217 
221  protected function checkQueries(): void
222  {
223  if (!($this->query instanceof \Traversable) && (is_array($this->query) && 0 === count($this->query))) {
225  }
226 
227  foreach ($this->query as $query) {
228  if (!$this->checkCallable($query)) {
230  }
231  }
232  }
233 
234  public function checkCallable(callable $query): bool
235  {
236  if (!is_callable($query)) {
237  return false; // Won't be triggered sidn type-hinting already checks this
238  }
239  if (is_array($query)) {
240  return false;
241  }
242  if (is_string($query)) {
243  return false;
244  }
245 
246  $is_a_closure = ($query instanceof Closure);
247  if (!$is_a_closure) {
248  $ref = new ReflectionClass($query);
249  foreach ($ref->getMethods() as $method) {
250  if ($method->getName() === '__invoke') {
251  return true;
252  }
253  }
254 
255  return false;
256  }
257  if ($is_a_closure) {
258  $ref = new ReflectionFunction($query);
259  $parameters = $ref->getParameters();
260  if (count($parameters) !== 1) {
261  return false;
262  }
263  $reflectionClass = $parameters[0]->getType();
264  return $reflectionClass && $reflectionClass->getName() === ilDBInterface::class;
265  }
266 
267  return true;
268  }
269 
270  protected function hasWriteLocks(): bool
271  {
272  $has_write_locks = false;
273  foreach ($this->tables as $table) {
274  if ($table->getLockLevel() === ilAtomQuery::LOCK_WRITE) {
275  $has_write_locks = true;
276  }
277  }
278 
279  return $has_write_locks;
280  }
281 
285  protected function runQueries(): void
286  {
288  $query($this->ilDBInstance);
289  }
290 
294  protected function checkBeforeRun(): void
295  {
296  $this->checkQueries();
297 
300  }
301 
302  if (count($this->tables) === 0) {
304  }
305  }
306 }
static array $available_isolations_levels
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static checkIsolationLevel(int $isolation_level)
addTableLock(string $table_name)
Add table-names which are influenced by your queries, MyISAm has to lock those tables.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
ilDBInterface $ilDBInstance
static checkAnomaly(int $anomaly)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
run()
Fire your Queries.
addQueryCallable(callable $query)
All action on the database during this isolation has to be passed as Callable to ilAtomQuery.
static isThereRiskThat(int $isolation_level, int $anomaly)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
__construct(ilDBInterface $ilDBInstance, int $isolation_level=ilAtomQuery::ISOLATION_SERIALIZABLE)
ilAtomQuery constructor.
static array $possible_anomalies
checkCallable(callable $query)
static getPossibleAnomalies(int $isolation_level)
static array $anomalies_map
replaceQueryCallable(callable $query)