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

Connection::getConnection()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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