aboutsummaryrefslogtreecommitdiffhomepage
path: root/services/app/src/Database/Connection.php
blob: 01f46b277b31f10d17527fb22a0175b521b5995d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<?php

declare(strict_types=1);

namespace Nsfisis\Albatross\Database;

use Exception;
use LogicException;
use Nsfisis\Albatross\Sql\QueryBuilder;
use PDO;
use PDOException;

final class Connection
{
    private readonly PDO $conn;

    public function __construct(
        string $driver,
        string $host,
        int $port,
        string $name,
        string $user,
        string $password,
        int $max_tries = 10,
        int $sleep_sec = 3,
    ) {
        if ($driver !== 'pgsql') {
            throw new LogicException('Only pgsql is supported');
        }
        $this->conn = self::tryConnect(
            "$driver:host=$host;port=$port;dbname=$name;user=$user;password=$password",
            $max_tries,
            $sleep_sec,
        );
    }

    public static function tryConnect(
        string $dsn,
        int $max_tries,
        int $sleep_sec,
    ): PDO {
        $tries = 0;
        while (true) {
            try {
                return self::connect($dsn);
            } catch (PDOException $e) {
                if ($max_tries <= $tries) {
                    throw $e;
                }
                sleep($sleep_sec);
            }
            $tries++;
        }
    }

    public function query(): QueryBuilder
    {
        return new QueryBuilder($this->conn);
    }

    /**
     * @template T
     * @param callable(): T $fn
     * @return T
     */
    public function transaction(callable $fn): mixed
    {
        $this->conn->beginTransaction();
        try {
            $result = $fn();
            $this->conn->commit();
            return $result;
        } catch (Exception $e) {
            $this->conn->rollBack();
            throw $e;
        }
    }

    /**
     * @throws PDOException
     */
    private static function connect(string $dsn): PDO
    {
        return new PDO(
            dsn: $dsn,
            options: [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_PERSISTENT => true,
            ],
        );
    }
}