1: <?php
2: namespace Opencart\System\Library\DB;
3: /**
4: * Class PDO
5: *
6: * @package Opencart\System\Library\DB
7: */
8: class PDO {
9: /**
10: * @var \PDO|null
11: */
12: private ?\PDO $connection;
13: /**
14: * @var array<string, string>
15: */
16: private array $data = [];
17: /**
18: * @var int
19: */
20: private int $affected;
21:
22: /**
23: * Constructor
24: *
25: * @param string $hostname
26: * @param string $username
27: * @param string $password
28: * @param string $database
29: * @param string $port
30: */
31: public function __construct(string $hostname, string $username, string $password, string $database, string $port = '') {
32: if (!$port) {
33: $port = '3306';
34: }
35:
36: try {
37: $pdo = new \PDO('mysql:host=' . $hostname . ';port=' . $port . ';dbname=' . $database . ';charset=utf8mb4', $username, $password, [\PDO::ATTR_PERSISTENT => false, \PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4 COLLATE utf8mb4_general_ci']);
38: } catch (\PDOException $e) {
39: throw new \Exception('Error: Could not make a database link using ' . $username . '@' . $hostname . '!');
40: }
41:
42: $this->connection = $pdo;
43:
44: $this->query("SET SESSION sql_mode = 'NO_ZERO_IN_DATE,NO_ENGINE_SUBSTITUTION'");
45: $this->query("SET FOREIGN_KEY_CHECKS = 0");
46:
47: // Sync PHP and DB time zones
48: $this->query("SET `time_zone` = '" . $this->escape(date('P')) . "'");
49: }
50:
51: /**
52: * Query
53: *
54: * @param string $sql
55: *
56: * @return \stdClass|true
57: */
58: public function query(string $sql) {
59: $sql = preg_replace('/(?:\'\:)([a-z0-9]*.)(?:\')/', ':$1', $sql);
60:
61: $statement = $this->connection->prepare($sql);
62:
63: try {
64: if ($statement && $statement->execute($this->data)) {
65: $this->data = [];
66:
67: if ($statement->columnCount()) {
68: $data = $statement->fetchAll(\PDO::FETCH_ASSOC);
69: $statement->closeCursor();
70:
71: $result = new \stdClass();
72: $result->row = $data[0] ?? [];
73: $result->rows = $data;
74: $result->num_rows = count($data);
75: $this->affected = 0;
76:
77: return $result;
78: } else {
79: $this->affected = $statement->rowCount();
80: $statement->closeCursor();
81:
82: return true;
83: }
84: } else {
85: return true;
86: }
87: } catch (\PDOException $e) {
88: throw new \Exception('Error: ' . $e->getMessage() . ' <br/>Error Code : ' . $e->getCode() . ' <br/>' . $sql);
89: }
90: }
91:
92: /**
93: * Escape
94: *
95: * @param string $value
96: *
97: * @return string
98: */
99: public function escape(string $value): string {
100: $key = ':' . count($this->data);
101:
102: $this->data[$key] = $value;
103:
104: return $key;
105: }
106:
107: /**
108: * countAffected
109: *
110: * @return int
111: */
112: public function countAffected(): int {
113: return $this->affected;
114: }
115:
116: /**
117: * getLastId
118: *
119: * @return ?int
120: */
121: public function getLastId(): ?int {
122: $id = $this->connection->lastInsertId();
123:
124: return $id ? (int)$id : null;
125: }
126:
127: /**
128: * isConnected
129: *
130: * @return bool
131: */
132: public function isConnected(): bool {
133: return $this->connection !== null;
134: }
135:
136: /**
137: * Destructor
138: *
139: * Closes the DB connection when this object is destroyed.
140: */
141: public function __destruct() {
142: $this->connection = null;
143: }
144: }
145: