Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.48% covered (success)
90.48%
38 / 42
60.00% covered (warning)
60.00%
3 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
PreparedStatement
90.48% covered (success)
90.48%
38 / 42
60.00% covered (warning)
60.00%
3 / 5
16.22
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 query
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
2.02
 exec
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 bindParams
88.00% covered (warning)
88.00%
22 / 25
0.00% covered (danger)
0.00%
0 / 1
9.14
 sendBlob
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
1<?php declare(strict_types=1);
2/*
3 * This file is part of Aplus Framework Database Library.
4 *
5 * (c) Natan Felles <natanfelles@gmail.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10namespace Framework\Database;
11
12use InvalidArgumentException;
13use RuntimeException;
14
15/**
16 * Class PreparedStatement.
17 *
18 * @package database
19 */
20class PreparedStatement
21{
22    protected \mysqli_stmt $statement;
23    protected bool $sendingBlob = false;
24
25    public function __construct(\mysqli_stmt $statement)
26    {
27        $this->statement = $statement;
28    }
29
30    /**
31     * Executes the prepared statement, returning a result set as a Result object.
32     *
33     * @param bool|float|int|string|null ...$params Parameters sent to the prepared statement
34     *
35     * @throws RuntimeException if it cannot obtain a result set from the prepared statement
36     *
37     * @return Result
38     */
39    public function query(bool | float | int | string | null ...$params) : Result
40    {
41        $this->bindParams($params);
42        $this->statement->execute();
43        $result = $this->statement->get_result();
44        if ($result === false) {
45            throw new RuntimeException('Failed while trying to obtain a result set from the prepared statement');
46        }
47        return new Result($result, true);
48    }
49
50    /**
51     * Executes the prepared statement and return the number of affected rows.
52     *
53     * @param bool|float|int|string|null ...$params Parameters sent to the prepared statement
54     *
55     * @return int|string
56     */
57    public function exec(bool | float | int | string | null ...$params) : int|string
58    {
59        $this->bindParams($params);
60        $this->statement->execute();
61        if ($this->statement->field_count) {
62            $this->statement->free_result();
63        }
64        return $this->statement->affected_rows;
65    }
66
67    /**
68     * @param array|mixed[] $params Values types: bool, float, int, string or null
69     */
70    protected function bindParams(array $params) : void
71    {
72        $this->sendingBlob = false;
73        if (empty($params)) {
74            return;
75        }
76        $types = '';
77        foreach ($params as &$param) {
78            $type = \gettype($param);
79            switch ($type) {
80                case 'boolean':
81                    $types .= 'i';
82                    $param = (int) $param;
83                    break;
84                case 'double':
85                    $types .= 'd';
86                    break;
87                case 'integer':
88                    $types .= 'i';
89                    break;
90                case 'NULL':
91                case 'string':
92                    $types .= 's';
93                    break;
94                default:
95                    throw new InvalidArgumentException(
96                        "Invalid param data type: {$type}"
97                    );
98            }
99        }
100        unset($param);
101        $this->statement->bind_param($types, ...$params);
102    }
103
104    public function sendBlob(string $chunk) : bool
105    {
106        if ( ! $this->sendingBlob) {
107            $this->sendingBlob = true;
108            $null = null;
109            $this->statement->bind_param('b', $null);
110        }
111        return $this->statement->send_long_data(0, $chunk);
112    }
113}