@@ -31,37 +31,157 @@ abstract class AbstractConnection
3131 protected int $ fetch = PDO ::FETCH_OBJ ;
3232
3333 /**
34- * The PDO instance
34+ * The write (primary) PDO instance
3535 *
36- * @var PDO
36+ * @var ? PDO
3737 */
38- protected PDO $ pdo ;
38+ protected ? PDO $ write_pdo = null ;
3939
4040 /**
41- * Create an instance of the PDO
41+ * The read (replica) PDO instance
42+ *
43+ * @var ?PDO
44+ */
45+ protected ?PDO $ read_pdo = null ;
46+
47+ /**
48+ * The configuration used to build the write connection
49+ *
50+ * @var array
51+ */
52+ protected array $ write_config = [];
53+
54+ /**
55+ * The configuration used to build the read connection,
56+ * or null when the connection is not split (reads use write).
57+ *
58+ * @var ?array
59+ */
60+ protected ?array $ read_config = null ;
61+
62+ /**
63+ * AbstractConnection constructor.
64+ *
65+ * Splits the connection configuration into a write (primary)
66+ * configuration and an optional read (replica) configuration.
67+ *
68+ * @param array $config
69+ */
70+ public function __construct (array $ config )
71+ {
72+ $ this ->config = $ config ;
73+
74+ $ this ->write_config = $ config ;
75+ unset($ this ->write_config ['read ' ]);
76+
77+ if (isset ($ config ['read ' ]) && is_array ($ config ['read ' ])) {
78+ $ this ->read_config = array_merge ($ this ->write_config , $ config ['read ' ]);
79+ } else {
80+ $ this ->read_config = null ;
81+ }
82+
83+ // Validate eagerly so misconfiguration fails fast, while the
84+ // connection itself is still established lazily on first use.
85+ $ this ->validateConfig ($ this ->write_config );
86+
87+ if ($ this ->read_config !== null ) {
88+ $ this ->validateConfig ($ this ->read_config );
89+ }
90+ }
91+
92+ /**
93+ * Validate the connection configuration.
94+ *
95+ * @param array $config
96+ * @return void
97+ */
98+ abstract protected function validateConfig (array $ config ): void ;
99+
100+ /**
101+ * Build a PDO instance from the given configuration.
102+ *
103+ * @param array $config
104+ * @return PDO
105+ */
106+ abstract protected function makePdo (array $ config ): PDO ;
107+
108+ /**
109+ * Build (eagerly) the write connection.
110+ *
111+ * Kept for backward compatibility with callers that expect to
112+ * (re)establish the connection explicitly.
42113 *
43114 * @return void
44115 */
45- abstract public function connection (): void ;
116+ public function connection (): void
117+ {
118+ $ this ->write_pdo = $ this ->makePdo ($ this ->write_config );
119+ }
46120
47121 /**
48- * Retrieves the connection
122+ * Retrieves the connection (the write/primary connection)
49123 *
50124 * @return PDO
51125 */
52126 public function getConnection (): PDO
53127 {
54- return $ this ->pdo ;
128+ return $ this ->getWriteConnection ();
129+ }
130+
131+ /**
132+ * Retrieves the write (primary) connection, building it lazily
133+ *
134+ * @return PDO
135+ */
136+ public function getWriteConnection (): PDO
137+ {
138+ if ($ this ->write_pdo === null ) {
139+ $ this ->write_pdo = $ this ->makePdo ($ this ->write_config );
140+ }
141+
142+ return $ this ->write_pdo ;
55143 }
56144
57145 /**
58- * Set the connection
146+ * Retrieves the read (replica) connection, building it lazily.
147+ *
148+ * Falls back to the write connection when the connection is not split.
149+ *
150+ * @return PDO
151+ */
152+ public function getReadConnection (): PDO
153+ {
154+ if ($ this ->read_config === null ) {
155+ return $ this ->getWriteConnection ();
156+ }
157+
158+ if ($ this ->read_pdo === null ) {
159+ $ this ->read_pdo = $ this ->makePdo ($ this ->read_config );
160+ }
161+
162+ return $ this ->read_pdo ;
163+ }
164+
165+ /**
166+ * Whether the write connection has already been established.
167+ *
168+ * Used to inspect transaction state without forcing a connection open.
169+ *
170+ * @return bool
171+ */
172+ public function hasWriteConnection (): bool
173+ {
174+ return $ this ->write_pdo instanceof PDO ;
175+ }
176+
177+ /**
178+ * Set the connection (the write/primary connection)
59179 *
60180 * @param PDO $pdo
61181 */
62182 public function setConnection (PDO $ pdo ): void
63183 {
64- $ this ->pdo = $ pdo ;
184+ $ this ->write_pdo = $ pdo ;
65185 }
66186
67187 /**
@@ -84,10 +204,14 @@ public function setFetchMode(int $fetch): void
84204 {
85205 $ this ->fetch = $ fetch ;
86206
87- $ this ->pdo ->setAttribute (
88- PDO ::ATTR_DEFAULT_FETCH_MODE ,
89- $ fetch
90- );
207+ foreach ([$ this ->write_pdo , $ this ->read_pdo ] as $ pdo ) {
208+ if ($ pdo instanceof PDO ) {
209+ $ pdo ->setAttribute (
210+ PDO ::ATTR_DEFAULT_FETCH_MODE ,
211+ $ fetch
212+ );
213+ }
214+ }
91215 }
92216
93217 /**
@@ -137,7 +261,7 @@ public function getCollation(): string
137261 */
138262 public function getPdoDriver (): string
139263 {
140- return $ this ->pdo ->getAttribute (PDO ::ATTR_DRIVER_NAME );
264+ return $ this ->getConnection () ->getAttribute (PDO ::ATTR_DRIVER_NAME );
141265 }
142266
143267 /**
0 commit comments