3
3
namespace Rcalicdan \FiberAsync \MySQLi ;
4
4
5
5
use Rcalicdan \FiberAsync \Api \AsyncMySQLi ;
6
- use Rcalicdan \FiberAsync \Config \ConfigLoader ;
6
+ use Rcalicdan \FiberAsync \Config \MysqliConfigLoader ;
7
7
use Rcalicdan \FiberAsync \Promise \Interfaces \PromiseInterface ;
8
- use Rcalicdan \FiberAsync \QueryBuilder \PDOQueryBuilder ;
8
+ use Rcalicdan \FiberAsync \QueryBuilder \MySQLiQueryBuilder ;
9
9
10
10
/**
11
- * DB API - Main entry point for auto-configured async database operations using AsyncMySQLi under the hood
12
- * with asynchonous query builder support.
13
- *
14
- * This API automatically loads configuration from .env and config/database .php
15
- * the first time it is used, providing a zero-setup experience for the developer.
16
- */
11
+ * DB API - Main entry point for auto-configured async database operations using AsyncMySQLi under the hood
12
+ * with asynchronous query builder support.
13
+ *
14
+ * This API automatically loads configuration from .env and config/mysqli/config .php
15
+ * the first time it is used, providing a zero-setup experience for the developer.
16
+ */
17
17
class DB
18
18
{
19
- private static bool $ isInitialized = false ;
20
- private static bool $ hasValidationError = false ;
21
-
22
- /**
23
- * The core of the new design: A private, self-configuring initializer.
24
- * This method is called by every public method to ensure the system is ready.
25
- * Validates only once if successful, but re-validates if there were previous errors.
26
- */
27
- private static function initializeIfNeeded (): void
28
- {
29
- if (self ::$ isInitialized && ! self ::$ hasValidationError ) {
30
- return ;
31
- }
32
-
33
- self ::$ hasValidationError = false ;
34
-
35
- try {
36
- $ configLoader = ConfigLoader::getInstance ();
37
- $ dbConfigAll = $ configLoader ->get ('database ' );
38
-
39
- if (! is_array ($ dbConfigAll )) {
40
- throw new \RuntimeException ("Database configuration not found. Ensure 'config/database.php' exists in your project root. " );
41
- }
42
-
43
- $ defaultConnection = $ dbConfigAll ['default ' ] ?? null ;
44
- if (! is_string ($ defaultConnection )) {
45
- throw new \RuntimeException ('Default connection name must be a string in your database config. ' );
46
- }
47
-
48
- $ connections = $ dbConfigAll ['connections ' ] ?? null ;
49
- if (! is_array ($ connections )) {
50
- throw new \RuntimeException ('Database connections configuration must be an array. ' );
51
- }
52
-
53
- if (! isset ($ connections [$ defaultConnection ]) || ! is_array ($ connections [$ defaultConnection ])) {
54
- throw new \RuntimeException ("Default database connection ' {$ defaultConnection }' not defined in your database config. " );
55
- }
56
-
57
- $ connectionConfig = $ connections [$ defaultConnection ];
58
-
59
- /** @var array<string, mixed> $validatedConfig */
60
- $ validatedConfig = [];
61
- foreach ($ connectionConfig as $ key => $ value ) {
62
- if (! is_string ($ key )) {
63
- throw new \RuntimeException ('Database connection configuration must have string keys only. ' );
64
- }
65
- $ validatedConfig [$ key ] = $ value ;
66
- }
67
-
68
- $ poolSize = 10 ;
69
- if (isset ($ dbConfigAll ['pool_size ' ])) {
70
- if (! is_int ($ dbConfigAll ['pool_size ' ]) || $ dbConfigAll ['pool_size ' ] < 1 ) {
71
- throw new \RuntimeException ('Database pool size must be a positive integer. ' );
72
- }
73
- $ poolSize = $ dbConfigAll ['pool_size ' ];
74
- }
75
-
76
- AsyncMySQLi::init ($ validatedConfig , $ poolSize );
77
- self ::$ isInitialized = true ;
78
-
79
- } catch (\Exception $ e ) {
80
- self ::$ hasValidationError = true ;
81
- self ::$ isInitialized = false ;
82
-
83
- throw $ e ;
84
- }
85
- }
86
-
87
- /**
88
- * Resets the entire database system. Crucial for isolated testing.
89
- */
90
- public static function reset (): void
91
- {
92
- AsyncMySQLi::reset ();
93
- ConfigLoader::reset ();
94
- self ::$ isInitialized = false ;
95
- self ::$ hasValidationError = false ;
96
- }
97
-
98
- /**
99
- * Start a new query builder instance for the given table.
100
- */
101
- public static function table (string $ table ): PDOQueryBuilder
102
- {
103
- self ::initializeIfNeeded ();
104
-
105
- return new PDOQueryBuilder ($ table );
106
- }
107
-
108
- /**
109
- * Execute a raw query.
110
- *
111
- * @param array<string, mixed> $bindings
112
- * @return PromiseInterface<array<int, array<string, mixed>>>
113
- */
114
- public static function raw (string $ sql , array $ bindings = []): PromiseInterface
115
- {
116
- self ::initializeIfNeeded ();
117
-
118
- return AsyncMySQLi::query ($ sql , $ bindings );
119
- }
120
-
121
- /**
122
- * Execute a raw query and return the first result.
123
- *
124
- * @param array<string, mixed> $bindings
125
- * @return PromiseInterface<array<string, mixed>|false>
126
- */
127
- public static function rawFirst (string $ sql , array $ bindings = []): PromiseInterface
128
- {
129
- self ::initializeIfNeeded ();
130
-
131
- return AsyncMySQLi::fetchOne ($ sql , $ bindings );
132
- }
133
-
134
- /**
135
- * Execute a raw query and return a single scalar value.
136
- *
137
- * @param array<string, mixed> $bindings
138
- * @return PromiseInterface<mixed>
139
- */
140
- public static function rawValue (string $ sql , array $ bindings = []): PromiseInterface
141
- {
142
- self ::initializeIfNeeded ();
143
-
144
- return AsyncMySQLi::fetchValue ($ sql , $ bindings );
145
- }
146
-
147
- /**
148
- * Execute a raw statement (INSERT, UPDATE, DELETE).
149
- *
150
- * @param array<string, mixed> $bindings
151
- * @return PromiseInterface<int>
152
- */
153
- public static function rawExecute (string $ sql , array $ bindings = []): PromiseInterface
154
- {
155
- self ::initializeIfNeeded ();
156
-
157
- return AsyncMySQLi::execute ($ sql , $ bindings );
158
- }
159
-
160
- /**
161
- * Run a database transaction.
162
- *
163
- * @return PromiseInterface<mixed>
164
- */
165
- public static function transaction (callable $ callback ): PromiseInterface
166
- {
167
- self ::initializeIfNeeded ();
168
-
169
- return AsyncMySQLi::transaction ($ callback );
170
- }
171
- }
19
+ private static bool $ isInitialized = false ;
20
+ private static bool $ hasValidationError = false ;
21
+
22
+ /**
23
+ * The core of the new design: A private, self-configuring initializer.
24
+ * This method is called by every public method to ensure the system is ready.
25
+ * Validates only once if successful, but re-validates if there were previous errors.
26
+ */
27
+ private static function initializeIfNeeded (): void
28
+ {
29
+ if (self ::$ isInitialized && !self ::$ hasValidationError ) {
30
+ return ;
31
+ }
32
+
33
+ self ::$ hasValidationError = false ;
34
+
35
+ try {
36
+ $ configLoader = MysqliConfigLoader::getInstance ();
37
+ $ dbConfig = $ configLoader ->get ('config ' );
38
+
39
+ if (!is_array ($ dbConfig )) {
40
+ throw new \RuntimeException ("MySQLi configuration not found. Ensure 'config/mysqli/config.php' exists in your project root. " );
41
+ }
42
+
43
+ $ connectionConfig = $ dbConfig ['connection ' ] ?? null ;
44
+ if (!is_array ($ connectionConfig )) {
45
+ throw new \RuntimeException ('MySQLi connection configuration must be an array. ' );
46
+ }
47
+
48
+ // Validate required connection parameters
49
+ $ required = ['host ' , 'database ' , 'username ' ];
50
+ foreach ($ required as $ key ) {
51
+ if (!isset ($ connectionConfig [$ key ])) {
52
+ throw new \RuntimeException ("Missing required MySQLi connection parameter: {$ key }" );
53
+ }
54
+ }
55
+
56
+ /** @var array<string, mixed> $validatedConfig */
57
+ $ validatedConfig = [];
58
+ foreach ($ connectionConfig as $ key => $ value ) {
59
+ if (!is_string ($ key )) {
60
+ throw new \RuntimeException ('MySQLi connection configuration must have string keys only. ' );
61
+ }
62
+ $ validatedConfig [$ key ] = $ value ;
63
+ }
64
+
65
+ $ poolSize = $ dbConfig ['pool_size ' ] ?? 10 ;
66
+ if (!is_int ($ poolSize ) || $ poolSize < 1 ) {
67
+ throw new \RuntimeException ('MySQLi pool size must be a positive integer. ' );
68
+ }
69
+
70
+ AsyncMySQLi::init ($ validatedConfig , $ poolSize );
71
+ self ::$ isInitialized = true ;
72
+
73
+ } catch (\Exception $ e ) {
74
+ self ::$ hasValidationError = true ;
75
+ self ::$ isInitialized = false ;
76
+
77
+ throw $ e ;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Resets the entire database system. Crucial for isolated testing.
83
+ */
84
+ public static function reset (): void
85
+ {
86
+ AsyncMySQLi::reset ();
87
+ MysqliConfigLoader::reset ();
88
+ self ::$ isInitialized = false ;
89
+ self ::$ hasValidationError = false ;
90
+ }
91
+
92
+ /**
93
+ * Start a new query builder instance for the given table.
94
+ */
95
+ public static function table (string $ table ): MySQLiQueryBuilder
96
+ {
97
+ self ::initializeIfNeeded ();
98
+
99
+ return new MySQLiQueryBuilder ($ table );
100
+ }
101
+
102
+ /**
103
+ * Execute a raw query.
104
+ *
105
+ * @param array<string, mixed> $bindings
106
+ * @return PromiseInterface<array<int, array<string, mixed>>>
107
+ */
108
+ public static function raw (string $ sql , array $ bindings = []): PromiseInterface
109
+ {
110
+ self ::initializeIfNeeded ();
111
+
112
+ return AsyncMySQLi::query ($ sql , $ bindings );
113
+ }
114
+
115
+ /**
116
+ * Execute a raw query and return the first result.
117
+ *
118
+ * @param array<string, mixed> $bindings
119
+ * @return PromiseInterface<array<string, mixed>|false>
120
+ */
121
+ public static function rawFirst (string $ sql , array $ bindings = []): PromiseInterface
122
+ {
123
+ self ::initializeIfNeeded ();
124
+
125
+ return AsyncMySQLi::fetchOne ($ sql , $ bindings );
126
+ }
127
+
128
+ /**
129
+ * Execute a raw query and return a single scalar value.
130
+ *
131
+ * @param array<string, mixed> $bindings
132
+ * @return PromiseInterface<mixed>
133
+ */
134
+ public static function rawValue (string $ sql , array $ bindings = []): PromiseInterface
135
+ {
136
+ self ::initializeIfNeeded ();
137
+
138
+ return AsyncMySQLi::fetchValue ($ sql , $ bindings );
139
+ }
140
+
141
+ /**
142
+ * Execute a raw statement (INSERT, UPDATE, DELETE).
143
+ *
144
+ * @param array<string, mixed> $bindings
145
+ * @return PromiseInterface<int>
146
+ */
147
+ public static function rawExecute (string $ sql , array $ bindings = []): PromiseInterface
148
+ {
149
+ self ::initializeIfNeeded ();
150
+
151
+ return AsyncMySQLi::execute ($ sql , $ bindings );
152
+ }
153
+
154
+ /**
155
+ * Run a database transaction.
156
+ *
157
+ * @return PromiseInterface<mixed>
158
+ */
159
+ public static function transaction (callable $ callback ): PromiseInterface
160
+ {
161
+ self ::initializeIfNeeded ();
162
+
163
+ return AsyncMySQLi::transaction ($ callback );
164
+ }
165
+ }
0 commit comments