PreparedPDOStatement::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 4
dl 0
loc 10
ccs 5
cts 5
cp 1
crap 1
rs 10
1
<?php
2
3
namespace mindplay\sql\framework\pdo;
4
5
use mindplay\sql\framework\Logger;
6
use mindplay\sql\framework\PreparedStatement;
7
use mindplay\sql\model\TypeProvider;
8
use PDO;
9
use PDOException;
10
use PDOStatement;
11
use RuntimeException;
12
13
/**
14
 * This class implements a Prepared Statement adapter for PDO Statements.
15
 */
16
class PreparedPDOStatement implements PreparedStatement
17
{
18
    private PDOStatement $handle;
19
    
20
    private PDOExceptionMapper $exception_mapper;
21
    
22
    private TypeProvider $types;
23
    
24
    /**
25
     * @var array<string,int|float|string|bool|null>
26
     */
27
    private array $params = [];
28
    
29
    private bool $executed = false;
30
    
31
    private Logger $logger;
32
33 1
    public function __construct(
34
        PDOStatement $handle,
35
        PDOExceptionMapper $exception_mapper,
36
        TypeProvider $types,
37
        Logger $logger
38
    ) {
39 1
        $this->handle = $handle;
40 1
        $this->exception_mapper = $exception_mapper;
41 1
        $this->types = $types;
42 1
        $this->logger = $logger;
43
    }
44
45
    /**
46
     * @inheritdoc
47
     */
48 1
    public function bind(string $name, int|float|string|bool|null $value): void
49
    {
50 1
        static $PDO_TYPE = [
51 1
            'integer' => PDO::PARAM_INT,
52 1
            'double'  => PDO::PARAM_STR, // bind as string, since there's no float type in PDO
53 1
            'string'  => PDO::PARAM_STR,
54 1
            'boolean' => PDO::PARAM_BOOL,
55 1
            'NULL'    => PDO::PARAM_NULL,
56 1
        ];
57
58 1
        $value_type = gettype($value);
59
60 1
        $scalar_type = "scalar.{$value_type}";
61
62 1
        if ($this->types->hasType($scalar_type)) {
63 1
            $type = $this->types->getType($scalar_type);
64
65 1
            $value = $type->convertToSQL($value);
66
67 1
            $value_type = gettype($value);
68
        }
69
70 1
        if (isset($PDO_TYPE[$value_type])) {
71 1
            $this->handle->bindValue($name, $value, $PDO_TYPE[$value_type]);
72
73 1
            $this->params[$name] = $value;
74
        } else {
75
            throw new RuntimeException("internal error: unexpected value type {$value_type}");
76
        }
77
    }
78
79
    /**
80
     * @inheritdoc
81
     */
82 1
    public function execute(): void
83
    {
84 1
        $microtime_begin = microtime(true);
85
86 1
        $success = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $success is dead and can be removed.
Loading history...
87
88
        try {
89 1
            $success = @$this->handle->execute();
90 1
        } catch (PDOException $e) {
91
            // error will be handled below
92
        }
93
94 1
        if ($success) {
95 1
            $this->executed = true;
96 1
            $microtime_end = microtime(true);
97 1
            $time_msec = ($microtime_end - $microtime_begin) * 1000;
98 1
            $this->logger->logQuery($this->handle->queryString, $this->params, $time_msec);
99
        } else {
100 1
            list($sql_state, $error_code, $error_message) = $this->handle->errorInfo();
101
102 1
            $exception_type = $this->exception_mapper->getExceptionType($sql_state, $error_code, $error_message);
103
104 1
            throw new $exception_type(
105 1
                $this->handle->queryString,
106 1
                $this->params,
107 1
                "{$sql_state}: {$error_message}",
108 1
                $error_code
109 1
            );
110
        }
111
    }
112
113
    /**
114
     * @inheritdoc
115
     * 
116
     * @return array<string,mixed>|null
117
     */
118 1
    public function fetch(): array|null
119
    {
120 1
        if (! $this->executed) {
121 1
            $this->execute();
122
        }
123
124 1
        $result = $this->handle->fetch(PDO::FETCH_ASSOC);
125
126 1
        if ($result === false) {
127 1
            return null;
128
        }
129
130 1
        return $result;
131
    }
132
133
    /**
134
     * @inheritdoc
135
     */
136
    public function getRowsAffected(): int
137
    {
138
        return $this->handle->rowCount();
139
    }
140
}
141