1: <?php
2: namespace Opencart\System\Library\DB;
3: /**
4: * Class MySQLi
5: *
6: * @package Opencart\System\Library\DB
7: */
8: class MySQLi {
9: /**
10: * @var ?\mysqli
11: */
12: private ?\mysqli $connection;
13:
14: /**
15: * Constructor
16: *
17: * @param string $hostname
18: * @param string $username
19: * @param string $password
20: * @param string $database
21: * @param int $port
22: * @param string $ssl_key
23: * @param string $ssl_cert
24: * @param string $ssl_ca
25: */
26: public function __construct(string $hostname, string $username, string $password, string $database, int $port = 0, string $ssl_key = '', string $ssl_cert = '', string $ssl_ca = '') {
27: if (!$port) {
28: $port = 3306;
29: }
30:
31: // MSQL SSL connection
32: $temp_ssl_key_file = '';
33:
34: if ($ssl_key) {
35: $temp_ssl_key_file = tempnam(sys_get_temp_dir(), 'mysqli_key_');
36:
37: $handle = fopen($temp_ssl_key_file, 'w');
38:
39: fwrite($handle, $ssl_key);
40:
41: fclose($handle);
42: }
43:
44: $temp_ssl_cert_file = '';
45:
46: if ($ssl_cert) {
47: $temp_ssl_cert_file = tempnam(sys_get_temp_dir(), 'mysqli_cert_');
48:
49: $handle = fopen($temp_ssl_cert_file, 'w');
50:
51: fwrite($handle, $ssl_cert);
52:
53: fclose($handle);
54: }
55:
56: $temp_ssl_ca_file = '';
57:
58: if ($ssl_ca) {
59: $temp_ssl_ca_file = tempnam(sys_get_temp_dir(), 'mysqli_ca_');
60:
61: $handle = fopen($temp_ssl_ca_file, 'w');
62:
63: fwrite($handle, '-----BEGIN CERTIFICATE-----' . PHP_EOL . $ssl_ca . PHP_EOL . '-----END CERTIFICATE-----');
64:
65: fclose($handle);
66: }
67:
68: try {
69: $this->connection = mysqli_init() ?: null;
70:
71: if ($temp_ssl_key_file || $temp_ssl_cert_file || $temp_ssl_ca_file) {
72: $this->connection->ssl_set($temp_ssl_key_file, $temp_ssl_cert_file, $temp_ssl_ca_file, null, null);
73: $this->connection->real_connect($hostname, $username, $password, $database, $port, null, MYSQLI_CLIENT_SSL);
74: } else {
75: $this->connection->real_connect($hostname, $username, $password, $database, $port, null);
76: }
77:
78: $this->connection->set_charset('utf8mb4');
79:
80: $this->query("SET SESSION sql_mode = 'NO_ZERO_IN_DATE,NO_ENGINE_SUBSTITUTION'");
81: $this->query("SET FOREIGN_KEY_CHECKS = 0");
82:
83: // Sync PHP and DB time zones
84: $this->query("SET `time_zone` = '" . $this->escape(date('P')) . "'");
85: } catch (\mysqli_sql_exception $e) {
86: throw new \Exception('Error: Could not make a database link using ' . $username . '@' . $hostname . '!<br/>Message: ' . $e->getMessage());
87: }
88: }
89:
90: /**
91: * Query
92: *
93: * @param string $sql
94: *
95: * @return mixed
96: */
97: public function query(string $sql) {
98: try {
99: $query = $this->connection->query($sql);
100:
101: if ($query instanceof \mysqli_result) {
102: $data = [];
103:
104: while ($row = $query->fetch_assoc()) {
105: $data[] = $row;
106: }
107:
108: $result = new \stdClass();
109: $result->num_rows = $query->num_rows;
110: $result->row = $data[0] ?? [];
111: $result->rows = $data;
112:
113: $query->close();
114:
115: unset($data);
116:
117: return $result;
118: } else {
119: return true;
120: }
121: } catch (\mysqli_sql_exception $e) {
122: throw new \Exception('Error: ' . $this->connection->error . '<br/>Error No: ' . $this->connection->errno . '<br/>' . $sql);
123: }
124: }
125:
126: /**
127: * Escape
128: *
129: * @param string $value
130: *
131: * @return string
132: */
133: public function escape(string $value): string {
134: return $this->connection->real_escape_string($value);
135: }
136:
137: /**
138: * countAffected
139: *
140: * @return int
141: */
142: public function countAffected(): int {
143: return $this->connection->affected_rows;
144: }
145:
146: /**
147: * getLastId
148: *
149: * @return int
150: */
151: public function getLastId(): int {
152: return $this->connection->insert_id;
153: }
154:
155: /**
156: * isConnected
157: *
158: * @return bool
159: */
160: public function isConnected(): bool {
161: return $this->connection !== null;
162: }
163:
164: /**
165: * Destructor
166: *
167: * Closes the DB connection when this object is destroyed.
168: */
169: public function __destruct() {
170: if ($this->connection) {
171: $this->connection->close();
172:
173: $this->connection = null;
174: }
175: }
176: }
177: