Passed
Pull Request — master (#197)
by
unknown
01:38
created

Connection::getConnection()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 9
rs 10
c 1
b 0
f 0
1
<?php
2
namespace Foolz\SphinxQL\Drivers\Mysqli;
3
4
use Foolz\SphinxQL\Drivers\ConnectionBase;
5
use Foolz\SphinxQL\Drivers\MultiResultSet;
6
use Foolz\SphinxQL\Drivers\ResultSet;
7
use Foolz\SphinxQL\Exception\ConnectionException;
8
use Foolz\SphinxQL\Exception\DatabaseException;
9
use Foolz\SphinxQL\Exception\SphinxQLException;
10
use mysqli;
11
use PDO;
12
use RuntimeException;
13
14
/**
15
 * SphinxQL connection class utilizing the MySQLi extension.
16
 * It also contains escaping and quoting functions.
17
 */
18
class Connection extends ConnectionBase
19
{
20
    /**
21
     * Internal Encoding
22
     *
23
     * @var string
24
     */
25
    protected $internal_encoding;
26
27
    /**
28
     * Returns the internal encoding.
29
     *
30
     * @return string current multibyte internal encoding
31
     */
32
    public function getInternalEncoding()
33
    {
34
        return $this->internal_encoding;
35
    }
36
37
    /**
38
     * @return mysqli
39
     * @throws ConnectionException
40
     */
41
    public function getConnection(): mysqli
42
    {
43
        $connection = parent::getConnection();
44
45
        if ($connection instanceof PDO) {
46
            throw new RuntimeException('Connection type mismatch');
47
        }
48
49
        return $connection;
50
    }
51
52
    /**
53
     * @inheritdoc
54
     */
55
    public function connect(): bool
56
    {
57
        $data = $this->getParams();
58
        $conn = mysqli_init();
59
60
        if (!empty($data['options'])) {
61
            foreach ($data['options'] as $option => $value) {
62
                $conn->options($option, $value);
63
            }
64
        }
65
66
        set_error_handler(static function () {
67
        });
68
        try {
69
            if (!$conn->real_connect($data['host'], null, null, null, (int) $data['port'], $data['socket'])) {
70
                throw new ConnectionException('Connection Error: ['.$conn->connect_errno.']'.$conn->connect_error);
71
            }
72
        } finally {
73
            restore_error_handler();
74
        }
75
76
        $conn->set_charset('utf8');
77
        $this->connection = $conn;
78
        $this->mbPush();
79
80
        return true;
81
    }
82
83
    /**
84
     * Pings the Sphinx server.
85
     *
86
     * @return bool True if connected, false otherwise
87
     * @throws ConnectionException
88
     */
89
    public function ping()
90
    {
91
        $this->ensureConnection();
92
93
        return $this->getConnection()->ping();
94
    }
95
96
    /**
97
     * @inheritdoc
98
     * @return ConnectionBase
99
     * @throws ConnectionException
100
     */
101
    public function close()
102
    {
103
        $this->mbPop();
104
        $this->getConnection()->close();
105
106
        return parent::close();
107
    }
108
109
    /**
110
     * @inheritdoc
111
     */
112
    public function query($query)
113
    {
114
        $this->ensureConnection();
115
116
        set_error_handler(static function () {
117
        });
118
        try {
119
            /**
120
             * ManticoreSearch/Sphinx silence warnings thrown by php mysqli/mysqlnd
121
             *
122
             * unknown command (code=9) - status() command not implemented by Sphinx/ManticoreSearch
123
             * ERROR mysqli::prepare(): (08S01/1047): unknown command (code=22) - prepare() not implemented by Sphinx/Manticore
124
             */
125
            $resource = @$this->getConnection()->query($query);
126
        } finally {
127
            restore_error_handler();
128
        }
129
130
        if ($this->getConnection()->error) {
131
            throw new DatabaseException('['.$this->getConnection()->errno.'] '.
132
                $this->getConnection()->error.' [ '.$query.']');
133
        }
134
135
        return new ResultSet(new ResultSetAdapter($this, $resource));
136
    }
137
138
    /**
139
     * @inheritdoc
140
     */
141
    public function multiQuery(array $queue)
142
    {
143
        $count = count($queue);
144
145
        if ($count === 0) {
146
            throw new SphinxQLException('The Queue is empty.');
147
        }
148
149
        $this->ensureConnection();
150
151
        $this->getConnection()->multi_query(implode(';', $queue));
152
153
        if ($this->getConnection()->error) {
154
            throw new DatabaseException('['.$this->getConnection()->errno.'] '.
155
                $this->getConnection()->error.' [ '.implode(';', $queue).']');
156
        }
157
158
        return new MultiResultSet(new MultiResultSetAdapter($this));
159
    }
160
161
    /**
162
     * Escapes the input with \MySQLi::real_escape_string.
163
     * Based on FuelPHP's escaping function.
164
     * @inheritdoc
165
     */
166
    public function escape($value)
167
    {
168
        $this->ensureConnection();
169
170
        if (($value = $this->getConnection()->real_escape_string((string) $value)) === false) {
171
            // @codeCoverageIgnoreStart
172
            throw new DatabaseException($this->getConnection()->error, $this->getConnection()->errno);
173
            // @codeCoverageIgnoreEnd
174
        }
175
176
        return "'".$value."'";
177
    }
178
179
    /**
180
     * Enter UTF-8 multi-byte workaround mode.
181
     */
182
    public function mbPush()
183
    {
184
        $internalEncoding = mb_internal_encoding();
185
        if (is_string($internalEncoding)) {
186
            $this->internal_encoding = $internalEncoding;
187
        }
188
        mb_internal_encoding('UTF-8');
189
190
        return $this;
191
    }
192
193
    /**
194
     * Exit UTF-8 multi-byte workaround mode.
195
     */
196
    public function mbPop()
197
    {
198
        // TODO: add test case for #155
199
        if ($this->getInternalEncoding()) {
200
            mb_internal_encoding($this->getInternalEncoding());
201
            $this->internal_encoding = null;
202
        }
203
204
        return $this;
205
    }
206
}
207