Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.67% covered (success)
96.67%
58 / 60
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
DAO
96.67% covered (success)
96.67%
58 / 60
77.78% covered (warning)
77.78%
7 / 9
23
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 __destruct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 _
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
 runFile
n/a
0 / 0
n/a
0 / 0
2
 getDBSchemaVersion
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getMeta
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 setMeta
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
2
 getTestFullState
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 beginTransaction
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 rollBack
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/** @noinspection PhpUnhandledExceptionInspection */
3declare(strict_types=1);
4
5class DAO {
6  const tables = [ // because we use different types of DB is difficult to get table list by command
7    'users',
8    'workspaces',
9    'login_sessions',
10    'login_session_groups',
11    'person_sessions',
12    'tests',
13    'units',
14    'admin_sessions',
15    'test_commands',
16    'test_logs',
17    'test_reviews',
18    'logins',
19    'unit_logs',
20    'unit_reviews',
21    'workspace_users',
22    'meta',
23    'unit_data',
24    'files',
25    'unit_defs_attachments',
26    'file_relations'
27  ];
28
29  protected ?PDO $pdoDBhandle = null;
30  protected int $timeUserIsAllowedInMinutes = 600;
31  protected ?string $passwordSalt = 't'; // TODO remove and use SystemConfig
32  protected bool $insecurePasswords = false; // TODO remove and use SystemConfig
33  protected ?int $lastAffectedRows = null;
34
35  public function __construct() {
36    $this->pdoDBhandle = DB::getConnection();
37
38    $this->pdoDBhandle->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
39
40    $this->passwordSalt = SystemConfig::$password_salt;
41    $this->insecurePasswords = SystemConfig::$debug_useInsecurePasswords;
42  }
43
44  public function __destruct() {
45    if ($this->pdoDBhandle !== null) {
46      unset($this->pdoDBhandle);
47      $this->pdoDBhandle = null;
48    }
49  }
50
51  public function _(string $sql, array $replacements = [], $multiRow = false): ?array {
52    $this->lastAffectedRows = null;
53
54    $sqlStatement = $this->pdoDBhandle->prepare($sql);
55
56    try {
57      $sqlStatement->execute($replacements);
58    } catch (Exception $exception) {
59      $caller = debug_backtrace(!DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['function'];
60      throw new Exception($exception->getMessage() . " ($caller)", 0, $exception);
61    }
62
63    $this->lastAffectedRows = $sqlStatement->rowCount();
64
65    if (!$sqlStatement->columnCount()) {
66      return null;
67    }
68
69    if ($multiRow) {
70      $result = $sqlStatement->fetchAll(PDO::FETCH_ASSOC);
71      return is_bool($result) ? [] : $result;
72    }
73
74    $result = $sqlStatement->fetch(PDO::FETCH_ASSOC);
75    return is_bool($result) ? null : $result;
76  }
77
78  /**
79   * @codeCoverageIgnore
80   */
81  public function runFile(string $path): void {
82    if (!file_exists($path)) {
83      throw new HttpError("File does not exist: `$path`");
84    }
85
86    $this->pdoDBhandle->exec(file_get_contents($path));
87  }
88
89  public function getDBSchemaVersion(): string {
90    try {
91      $result = $this->_("select `value` from meta where metaKey = 'dbSchemaVersion'");
92      return $result['value'] ?? '0.0.0-no-entry';
93
94    } catch (Exception) {
95      return '0.0.0-no-table';
96    }
97  }
98
99  public function getMeta(array $categories): array {
100    $categoriesString = implode(',', array_map([$this->pdoDBhandle, "quote"], $categories));
101    $result = $this->_("SELECT * FROM meta where category in ($categoriesString)", [], true);
102    $returner = [];
103    foreach ($categories as $category) {
104      $returner[$category] = [];
105    }
106    foreach ($result as $row) {
107      $returner[$row['category']][$row['metaKey']] = $row['value'];
108    }
109    return $returner;
110  }
111
112  public function setMeta(string $category, string $key, ?string $value): void {
113    $currentValue = $this->_("select `value` from meta where metaKey = :key", [':key' => $key]);
114
115    if (!$currentValue) {
116      $this->_(
117        "insert into meta (category, metaKey, value) values (:category, :key, :value)",
118        [
119          ':key' => $key,
120          ':category' => $category,
121          ':value' => $value
122        ]
123      );
124    } else {
125      $this->_(
126        "update meta set value=:value, category=:category where metaKey = :key",
127        [
128          ':key' => $key,
129          ':category' => $category,
130          ':value' => $value
131        ]
132      );
133    }
134  }
135
136  public function getTestFullState(array $testSessionData): array {
137    $testState = JSON::decode($testSessionData['testState'] ?? '', true);
138
139    if ($testSessionData['locked']) {
140      $testState['status'] = 'locked';
141    } else if (!$testSessionData['running']) {
142      $testState['status'] = 'pending';
143    } else {
144      $testState['status'] = 'running';
145    }
146
147    return $testState;
148  }
149
150  public function beginTransaction(): void {
151    $this->pdoDBhandle->beginTransaction();
152  }
153
154  public function rollBack(): void {
155    $this->pdoDBhandle->rollBack();
156  }
157}