PdoStorage::__invoke()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 4
dl 0
loc 44
ccs 26
cts 26
cp 1
crap 4
rs 9.216
c 0
b 0
f 0
1
<?php
2
namespace Germania\PermanentAuth;
3
4
use Germania\PermanentAuth\Exceptions\DuplicateSelectorException;
5
use Germania\PermanentAuth\Exceptions\StorageException;
6
use Psr\Log\LoggerInterface;
7
use Psr\Log\NullLogger;
8
9
/**
10
 * This Callable stores a persistent login for a given User ID with selector, token hash and expiration date.
11
 *
12
 * If a selector exists, a `DuplicateSelectorException` will be thrown.
13
 * If the insert statement execution fails, a `StorageException` will be thrown.
14
 */
15
class PdoStorage
16
{
17
18
    /**
19
     * @var string
20
     */
21
    public $table     = "auth_logins";
22
23
    /**
24
     * @var PDOStatement
25
     */
26
    public $avoid_stmt;
27
28
    /**
29
     * @var PDOStatement
30
     */
31
    public $insert_stmt;
32
33
    /**
34
     * @var LoggerInterface
35
     */
36
    public $logger;
37
38
39
40
    /**
41
     * @param \PDO             $pdo    PDO instance
42
     * @param LoggerInterface  $logger Optional: PSR-3 Logger
43
     * @param string           $table  Optional: Custom table name, default: `auth_logins`
44
     */
45 30
    public function __construct( \PDO $pdo, LoggerInterface $logger = null, $table = null )
46
    {
47
48 30
        $this->table  = $table ?: $this->table;
49 30
        $this->logger = $logger ?: new NullLogger;
50
51
52
        // ------------------------------------------
53
        // 1. Prepare avoid duplicates statement
54
        // ------------------------------------------
55
        $avoid_duplicate_sql = "SELECT selector
56 30
        FROM  {$this->table}
57
        WHERE selector = :selector
58 6
        LIMIT 1";
59
60 30
        $this->avoid_stmt = $pdo->prepare($avoid_duplicate_sql);
0 ignored issues
show
Documentation Bug introduced by
It seems like $pdo->prepare($avoid_duplicate_sql) of type object<PDOStatement> is incompatible with the declared type object<Germania\PermanentAuth\PDOStatement> of property $avoid_stmt.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
61
62
63
        // ------------------------------------------
64
        // 2. Prepare Insert statements
65
        // ------------------------------------------
66 30
        $insert_sql = "INSERT INTO {$this->table} (
67
          user_id,
68
          selector,
69
          token_hash,
70
          valid_until
71
        ) VALUES (
72
          :user_id,
73
          :selector,
74
          :token_hash,
75
          :valid_until
76 6
        )";
77
78 30
        $this->insert_stmt = $pdo->prepare( $insert_sql );
0 ignored issues
show
Documentation Bug introduced by
It seems like $pdo->prepare($insert_sql) of type object<PDOStatement> is incompatible with the declared type object<Germania\PermanentAuth\PDOStatement> of property $insert_stmt.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
79
80
81 30
    }
82
83
    /**
84
     * @param  int       $user_id     User ID
85
     * @param  string    $selector    Cookie selector
86
     * @param  string    $token_hash  Token hash
87
     * @param  \DateTime $valid_until Valid until DateTime
88
     *
89
     * @return boolean
90
     *
91
     * @throws StorageException if Insert Statement executions returns FALSE.
92
     */
93 20
    public function __invoke($user_id, $selector, $token_hash, \DateTime $valid_until)
94
    {
95
96
        // ------------------------------------------
97
        // 1. Find out if selector already exists
98
        // ------------------------------------------
99 20
        $way_clear = $this->avoid_stmt->execute([':selector' => $selector]);
100 20
        if (!$way_clear) {
101 5
            throw new StorageException("Could not check if selector is existing.");
102
        }
103 15
        if ($this->avoid_stmt->fetchColumn()) {
104 5
            throw new DuplicateSelectorException;
105
        }
106
107
        // ------------------------------------------
108
        // 2. Insert
109
        // ------------------------------------------
110
111 10
        $result = $this->insert_stmt->execute([
112 10
            ':user_id'          => $user_id,
113 10
            ':selector'         => $selector,
114 10
            ':token_hash'       => $token_hash,
115 10
            ':valid_until'      => $valid_until->format('Y-m-d H:i:s')
116 2
        ]);
117
118
119 10
        if ($result) :
120 5
            $this->logger->info("Stored in database", [
121 5
                'user_id'  => $user_id,
122 4
                'selector' => $selector
123 1
            ]);
124 1
        else:
125 5
            $error_info = $this->insert_stmt->errorInfo();
126 5
            $this->logger->error('Could not store persistent login', [
127 5
                'user_id'    => $user_id,
128 5
                'selector'   => $selector,
129 4
                'stmt_error' => $error_info
130 1
            ]);
131
132 5
            throw new StorageException("Could not store persistent login on server: " . implode("/", $error_info));
133
        endif;
134
135 5
        return true;
136
    }
137
138
139
}
140