PdoClient   A
last analyzed

Complexity

Total Complexity 22

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 95.16%

Importance

Changes 0
Metric Value
dl 0
loc 119
c 0
b 0
f 0
wmc 22
lcom 1
cbo 4
ccs 59
cts 62
cp 0.9516
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A executeQuery() 0 4 1
A executeCommand() 0 17 4
A execute() 0 16 2
A bindParameterList() 0 13 4
A bindParameter() 0 15 3
C resolvePdoType() 0 26 7
1
<?php
2
3
namespace Hgraca\MicroOrm\DataSource\Pdo;
4
5
use Exception;
6
use Hgraca\Helper\ArrayHelper;
7
use Hgraca\MicroOrm\DataSource\ClientInterface;
8
use Hgraca\MicroOrm\DataSource\Exception\BindingException;
9
use Hgraca\MicroOrm\DataSource\Exception\ExecutionException;
10
use Hgraca\MicroOrm\DataSource\Exception\TypeResolutionException;
11
use PDO;
12
use PDOStatement;
13
14
final class PdoClient implements ClientInterface
15
{
16
    /** @var PDO */
17
    private $pdo;
18
19 6
    public function __construct(PDO $pdo)
20
    {
21 6
        $this->pdo = $pdo;
22 6
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
23 6
    }
24
25 2
    public function executeQuery(string $sql): array
26
    {
27 2
        return $this->execute($sql)->fetchAll(PDO::FETCH_ASSOC);
28
    }
29
30 4
    public function executeCommand(string $sql, array $bindingsList = [])
31
    {
32 4
        if (!ArrayHelper::isTwoDimensional($bindingsList)) {
33 4
            $bindingsList = [$bindingsList];
34
        }
35
36
        try {
37 4
            $this->pdo->beginTransaction();
38 4
            foreach ($bindingsList as $bindings) {
39 4
                $preparedStatement = $this->execute($sql, $bindings, $preparedStatement ?? null);
40
            }
41 1
            $this->pdo->commit();
42 3
        } catch (Exception $e) {
43 3
            $this->pdo->rollBack();
44 3
            throw $e;
45
        }
46 1
    }
47
48
    /**
49
     * @throws ExecutionException
50
     */
51 6
    private function execute(string $sql, array $bindings = [], PDOStatement $preparedStatement = null): PDOStatement
52
    {
53 6
        $preparedStatement = $preparedStatement ?? $this->pdo->prepare($sql);
54
55 6
        $this->bindParameterList($preparedStatement, $bindings);
56
57 4
        if (!$preparedStatement->execute()) {
58 2
            throw new ExecutionException(
59 2
                "Could not execute query: '$sql'"
60 2
                . ' Error code: ' . $preparedStatement->errorCode()
61 2
                . ' Error Info: ' . json_encode($preparedStatement->errorInfo())
62
            );
63
        }
64
65 2
        return $preparedStatement;
66
    }
67
68 5
    private function bindParameterList(PDOStatement $stmt, array $parameterList)
69
    {
70 5
        foreach ($parameterList as $name => $value) {
71 3
            if (is_array($value)) {
72
                /** @var array $value */
73
                foreach ($value as $filterName => $filterValue) {
74
                    $this->bindParameter($stmt, $filterName, $filterValue);
75
                }
76
            } else {
77 3
                $this->bindParameter($stmt, $name, $value);
78
            }
79
        }
80 3
    }
81
82
    /**
83
     * @throws BindingException
84
     */
85 3
    private function bindParameter(PDOStatement $stmt, string $name, $value)
86
    {
87 3
        $pdoType = $this->resolvePdoType($value);
88 2
        $bound = $stmt->bindValue(
89 2
            ':' . $name,
90 2
            $pdoType === PDO::PARAM_STR ? strval($value) : $value,
91
            $pdoType
92
        );
93
94 2
        if (false === $bound) {
95 1
            throw new BindingException(
96 1
                'Could not bind value: ' . json_encode(['name' => $name, 'value' => $value, 'type' => $pdoType])
97
            );
98
        }
99 1
    }
100
101
    /**
102
     * @param mixed $value
103
     *
104
     * @throws TypeResolutionException
105
     */
106 3
    private function resolvePdoType($value): int
107
    {
108 3
        $type = gettype($value);
109
        switch ($type) {
110 3
            case 'boolean':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
111 2
                $pdoType = PDO::PARAM_BOOL;
112 2
                break;
113 2
            case 'string':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
114 2
            case 'double': // float
115 1
                $pdoType = PDO::PARAM_STR;
116 1
                break;
117 2
            case 'integer':
118 1
                $pdoType = PDO::PARAM_INT;
119 1
                break;
120 2
            case 'NULL':
121 1
                $pdoType = PDO::PARAM_NULL;
122 1
                break;
123 1
            case 'object':
124 1
                $class = get_class($value);
125 1
                throw new TypeResolutionException("Invalid type '$class' for query filter.");
126
            default:
127
                throw new TypeResolutionException("Invalid type '$type' for query filter.");
128
        }
129
130 2
        return $pdoType;
131
    }
132
}
133