summaryrefslogtreecommitdiff
path: root/vendor/doctrine/dbal/src/ExpandArrayParameters.php
blob: 253861cc2aaa685772d1b19dd8b6e141dbf5a1fb (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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<?php

declare(strict_types=1);

namespace Doctrine\DBAL;

use Doctrine\DBAL\ArrayParameters\Exception\MissingNamedParameter;
use Doctrine\DBAL\ArrayParameters\Exception\MissingPositionalParameter;
use Doctrine\DBAL\SQL\Parser\Visitor;
use Doctrine\DBAL\Types\Type;

use function array_fill;
use function array_key_exists;
use function count;
use function implode;
use function substr;

/** @psalm-import-type WrapperParameterTypeArray from Connection */
final class ExpandArrayParameters implements Visitor
{
    private int $originalParameterIndex = 0;

    /** @var list<string> */
    private array $convertedSQL = [];

    /** @var list<mixed> */
    private array $convertedParameters = [];

    /** @var array<int<0, max>,string|ParameterType|Type> */
    private array $convertedTypes = [];

    /**
     * @param array<int, mixed>|array<string, mixed> $parameters
     * @psalm-param WrapperParameterTypeArray $types
     */
    public function __construct(
        private readonly array $parameters,
        private readonly array $types,
    ) {
    }

    public function acceptPositionalParameter(string $sql): void
    {
        $index = $this->originalParameterIndex;

        if (! array_key_exists($index, $this->parameters)) {
            throw MissingPositionalParameter::new($index);
        }

        $this->acceptParameter($index, $this->parameters[$index]);

        $this->originalParameterIndex++;
    }

    public function acceptNamedParameter(string $sql): void
    {
        $name = substr($sql, 1);

        if (! array_key_exists($name, $this->parameters)) {
            throw MissingNamedParameter::new($name);
        }

        $this->acceptParameter($name, $this->parameters[$name]);
    }

    public function acceptOther(string $sql): void
    {
        $this->convertedSQL[] = $sql;
    }

    public function getSQL(): string
    {
        return implode('', $this->convertedSQL);
    }

    /** @return list<mixed> */
    public function getParameters(): array
    {
        return $this->convertedParameters;
    }

    private function acceptParameter(int|string $key, mixed $value): void
    {
        if (! isset($this->types[$key])) {
            $this->convertedSQL[]        = '?';
            $this->convertedParameters[] = $value;

            return;
        }

        $type = $this->types[$key];

        if (! $type instanceof ArrayParameterType) {
            $this->appendTypedParameter([$value], $type);

            return;
        }

        if (count($value) === 0) {
            $this->convertedSQL[] = 'NULL';

            return;
        }

        $this->appendTypedParameter($value, ArrayParameterType::toElementParameterType($type));
    }

    /** @return array<int<0, max>,string|ParameterType|Type> */
    public function getTypes(): array
    {
        return $this->convertedTypes;
    }

    /** @param list<mixed> $values */
    private function appendTypedParameter(array $values, string|ParameterType|Type $type): void
    {
        $this->convertedSQL[] = implode(', ', array_fill(0, count($values), '?'));

        $index = count($this->convertedParameters);

        foreach ($values as $value) {
            $this->convertedParameters[]  = $value;
            $this->convertedTypes[$index] = $type;

            $index++;
        }
    }
}