ConnectionFactory.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. <?php
  2. namespace Illuminate\Database\Connectors;
  3. use PDOException;
  4. use Illuminate\Support\Arr;
  5. use InvalidArgumentException;
  6. use Illuminate\Database\Connection;
  7. use Illuminate\Database\MySqlConnection;
  8. use Illuminate\Database\SQLiteConnection;
  9. use Illuminate\Database\PostgresConnection;
  10. use Illuminate\Database\SqlServerConnection;
  11. use Illuminate\Contracts\Container\Container;
  12. class ConnectionFactory
  13. {
  14. /**
  15. * The IoC container instance.
  16. *
  17. * @var \Illuminate\Contracts\Container\Container
  18. */
  19. protected $container;
  20. /**
  21. * Create a new connection factory instance.
  22. *
  23. * @param \Illuminate\Contracts\Container\Container $container
  24. * @return void
  25. */
  26. public function __construct(Container $container)
  27. {
  28. $this->container = $container;
  29. }
  30. /**
  31. * Establish a PDO connection based on the configuration.
  32. *
  33. * @param array $config
  34. * @param string|null $name
  35. * @return \Illuminate\Database\Connection
  36. */
  37. public function make(array $config, $name = null)
  38. {
  39. $config = $this->parseConfig($config, $name);
  40. if (isset($config['read'])) {
  41. return $this->createReadWriteConnection($config);
  42. }
  43. return $this->createSingleConnection($config);
  44. }
  45. /**
  46. * Parse and prepare the database configuration.
  47. *
  48. * @param array $config
  49. * @param string $name
  50. * @return array
  51. */
  52. protected function parseConfig(array $config, $name)
  53. {
  54. return Arr::add(Arr::add($config, 'prefix', ''), 'name', $name);
  55. }
  56. /**
  57. * Create a single database connection instance.
  58. *
  59. * @param array $config
  60. * @return \Illuminate\Database\Connection
  61. */
  62. protected function createSingleConnection(array $config)
  63. {
  64. $pdo = $this->createPdoResolver($config);
  65. return $this->createConnection(
  66. $config['driver'], $pdo, $config['database'], $config['prefix'], $config
  67. );
  68. }
  69. /**
  70. * Create a single database connection instance.
  71. *
  72. * @param array $config
  73. * @return \Illuminate\Database\Connection
  74. */
  75. protected function createReadWriteConnection(array $config)
  76. {
  77. $connection = $this->createSingleConnection($this->getWriteConfig($config));
  78. return $connection->setReadPdo($this->createReadPdo($config));
  79. }
  80. /**
  81. * Create a new PDO instance for reading.
  82. *
  83. * @param array $config
  84. * @return \Closure
  85. */
  86. protected function createReadPdo(array $config)
  87. {
  88. return $this->createPdoResolver($this->getReadConfig($config));
  89. }
  90. /**
  91. * Get the read configuration for a read / write connection.
  92. *
  93. * @param array $config
  94. * @return array
  95. */
  96. protected function getReadConfig(array $config)
  97. {
  98. return $this->mergeReadWriteConfig(
  99. $config, $this->getReadWriteConfig($config, 'read')
  100. );
  101. }
  102. /**
  103. * Get the read configuration for a read / write connection.
  104. *
  105. * @param array $config
  106. * @return array
  107. */
  108. protected function getWriteConfig(array $config)
  109. {
  110. return $this->mergeReadWriteConfig(
  111. $config, $this->getReadWriteConfig($config, 'write')
  112. );
  113. }
  114. /**
  115. * Get a read / write level configuration.
  116. *
  117. * @param array $config
  118. * @param string $type
  119. * @return array
  120. */
  121. protected function getReadWriteConfig(array $config, $type)
  122. {
  123. return isset($config[$type][0])
  124. ? Arr::random($config[$type])
  125. : $config[$type];
  126. }
  127. /**
  128. * Merge a configuration for a read / write connection.
  129. *
  130. * @param array $config
  131. * @param array $merge
  132. * @return array
  133. */
  134. protected function mergeReadWriteConfig(array $config, array $merge)
  135. {
  136. return Arr::except(array_merge($config, $merge), ['read', 'write']);
  137. }
  138. /**
  139. * Create a new Closure that resolves to a PDO instance.
  140. *
  141. * @param array $config
  142. * @return \Closure
  143. */
  144. protected function createPdoResolver(array $config)
  145. {
  146. return array_key_exists('host', $config)
  147. ? $this->createPdoResolverWithHosts($config)
  148. : $this->createPdoResolverWithoutHosts($config);
  149. }
  150. /**
  151. * Create a new Closure that resolves to a PDO instance with a specific host or an array of hosts.
  152. *
  153. * @param array $config
  154. * @return \Closure
  155. */
  156. protected function createPdoResolverWithHosts(array $config)
  157. {
  158. return function () use ($config) {
  159. foreach (Arr::shuffle($hosts = $this->parseHosts($config)) as $key => $host) {
  160. $config['host'] = $host;
  161. try {
  162. return $this->createConnector($config)->connect($config);
  163. } catch (PDOException $e) {
  164. continue;
  165. }
  166. }
  167. throw $e;
  168. };
  169. }
  170. /**
  171. * Parse the hosts configuration item into an array.
  172. *
  173. * @param array $config
  174. * @return array
  175. */
  176. protected function parseHosts(array $config)
  177. {
  178. $hosts = Arr::wrap($config['host']);
  179. if (empty($hosts)) {
  180. throw new InvalidArgumentException('Database hosts array is empty.');
  181. }
  182. return $hosts;
  183. }
  184. /**
  185. * Create a new Closure that resolves to a PDO instance where there is no configured host.
  186. *
  187. * @param array $config
  188. * @return \Closure
  189. */
  190. protected function createPdoResolverWithoutHosts(array $config)
  191. {
  192. return function () use ($config) {
  193. return $this->createConnector($config)->connect($config);
  194. };
  195. }
  196. /**
  197. * Create a connector instance based on the configuration.
  198. *
  199. * @param array $config
  200. * @return \Illuminate\Database\Connectors\ConnectorInterface
  201. *
  202. * @throws \InvalidArgumentException
  203. */
  204. public function createConnector(array $config)
  205. {
  206. if (! isset($config['driver'])) {
  207. throw new InvalidArgumentException('A driver must be specified.');
  208. }
  209. if ($this->container->bound($key = "db.connector.{$config['driver']}")) {
  210. return $this->container->make($key);
  211. }
  212. switch ($config['driver']) {
  213. case 'mysql':
  214. return new MySqlConnector;
  215. case 'pgsql':
  216. return new PostgresConnector;
  217. case 'sqlite':
  218. return new SQLiteConnector;
  219. case 'sqlsrv':
  220. return new SqlServerConnector;
  221. }
  222. throw new InvalidArgumentException("Unsupported driver [{$config['driver']}]");
  223. }
  224. /**
  225. * Create a new connection instance.
  226. *
  227. * @param string $driver
  228. * @param \PDO|\Closure $connection
  229. * @param string $database
  230. * @param string $prefix
  231. * @param array $config
  232. * @return \Illuminate\Database\Connection
  233. *
  234. * @throws \InvalidArgumentException
  235. */
  236. protected function createConnection($driver, $connection, $database, $prefix = '', array $config = [])
  237. {
  238. if ($resolver = Connection::getResolver($driver)) {
  239. return $resolver($connection, $database, $prefix, $config);
  240. }
  241. switch ($driver) {
  242. case 'mysql':
  243. return new MySqlConnection($connection, $database, $prefix, $config);
  244. case 'pgsql':
  245. return new PostgresConnection($connection, $database, $prefix, $config);
  246. case 'sqlite':
  247. return new SQLiteConnection($connection, $database, $prefix, $config);
  248. case 'sqlsrv':
  249. return new SqlServerConnection($connection, $database, $prefix, $config);
  250. }
  251. throw new InvalidArgumentException("Unsupported driver [{$driver}]");
  252. }
  253. }