EasySms.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. <?php
  2. /*
  3. * This file is part of the overtrue/easy-sms.
  4. *
  5. * (c) overtrue <i@overtrue.me>
  6. *
  7. * This source file is subject to the MIT license that is bundled
  8. * with this source code in the file LICENSE.
  9. */
  10. namespace Overtrue\EasySms;
  11. use Closure;
  12. use Overtrue\EasySms\Contracts\GatewayInterface;
  13. use Overtrue\EasySms\Contracts\MessageInterface;
  14. use Overtrue\EasySms\Contracts\PhoneNumberInterface;
  15. use Overtrue\EasySms\Contracts\StrategyInterface;
  16. use Overtrue\EasySms\Exceptions\InvalidArgumentException;
  17. use Overtrue\EasySms\Gateways\Gateway;
  18. use Overtrue\EasySms\Strategies\OrderStrategy;
  19. use Overtrue\EasySms\Support\Config;
  20. /**
  21. * Class EasySms.
  22. */
  23. class EasySms
  24. {
  25. /**
  26. * @var \Overtrue\EasySms\Support\Config
  27. */
  28. protected $config;
  29. /**
  30. * @var string
  31. */
  32. protected $defaultGateway;
  33. /**
  34. * @var array
  35. */
  36. protected $customCreators = [];
  37. /**
  38. * @var array
  39. */
  40. protected $gateways = [];
  41. /**
  42. * @var \Overtrue\EasySms\Messenger
  43. */
  44. protected $messenger;
  45. /**
  46. * @var array
  47. */
  48. protected $strategies = [];
  49. /**
  50. * Constructor.
  51. *
  52. * @param array $config
  53. */
  54. public function __construct(array $config)
  55. {
  56. $this->config = new Config($config);
  57. }
  58. /**
  59. * Send a message.
  60. *
  61. * @param string|array $to
  62. * @param \Overtrue\EasySms\Contracts\MessageInterface|array $message
  63. * @param array $gateways
  64. *
  65. * @return array
  66. *
  67. * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException
  68. * @throws \Overtrue\EasySms\Exceptions\NoGatewayAvailableException
  69. */
  70. public function send($to, $message, array $gateways = [])
  71. {
  72. $to = $this->formatPhoneNumber($to);
  73. $message = $this->formatMessage($message);
  74. $gateways = empty($gateways) ? $message->getGateways() : $gateways;
  75. if (empty($gateways)) {
  76. $gateways = $this->config->get('default.gateways', []);
  77. }
  78. return $this->getMessenger()->send($to, $message, $this->formatGateways($gateways));
  79. }
  80. /**
  81. * Create a gateway.
  82. *
  83. * @param string|null $name
  84. *
  85. * @return \Overtrue\EasySms\Contracts\GatewayInterface
  86. *
  87. * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException
  88. */
  89. public function gateway($name)
  90. {
  91. if (!isset($this->gateways[$name])) {
  92. $this->gateways[$name] = $this->createGateway($name);
  93. }
  94. return $this->gateways[$name];
  95. }
  96. /**
  97. * Get a strategy instance.
  98. *
  99. * @param string|null $strategy
  100. *
  101. * @return \Overtrue\EasySms\Contracts\StrategyInterface
  102. *
  103. * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException
  104. */
  105. public function strategy($strategy = null)
  106. {
  107. if (\is_null($strategy)) {
  108. $strategy = $this->config->get('default.strategy', OrderStrategy::class);
  109. }
  110. if (!\class_exists($strategy)) {
  111. $strategy = __NAMESPACE__.'\Strategies\\'.\ucfirst($strategy);
  112. }
  113. if (!\class_exists($strategy)) {
  114. throw new InvalidArgumentException("Unsupported strategy \"{$strategy}\"");
  115. }
  116. if (empty($this->strategies[$strategy]) || !($this->strategies[$strategy] instanceof StrategyInterface)) {
  117. $this->strategies[$strategy] = new $strategy($this);
  118. }
  119. return $this->strategies[$strategy];
  120. }
  121. /**
  122. * Register a custom driver creator Closure.
  123. *
  124. * @param string $name
  125. * @param \Closure $callback
  126. *
  127. * @return $this
  128. */
  129. public function extend($name, Closure $callback)
  130. {
  131. $this->customCreators[$name] = $callback;
  132. return $this;
  133. }
  134. /**
  135. * @return \Overtrue\EasySms\Support\Config
  136. */
  137. public function getConfig()
  138. {
  139. return $this->config;
  140. }
  141. /**
  142. * @return \Overtrue\EasySms\Messenger
  143. */
  144. public function getMessenger()
  145. {
  146. return $this->messenger ?: $this->messenger = new Messenger($this);
  147. }
  148. /**
  149. * Create a new driver instance.
  150. *
  151. * @param string $name
  152. *
  153. * @throws \InvalidArgumentException
  154. *
  155. * @return GatewayInterface
  156. *
  157. * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException
  158. */
  159. protected function createGateway($name)
  160. {
  161. $config = $this->config->get("gateways.{$name}", []);
  162. if (!isset($config['timeout'])) {
  163. $config['timeout'] = $this->config->get('timeout', Gateway::DEFAULT_TIMEOUT);
  164. }
  165. $config['options'] = $this->config->get('options', []);
  166. if (isset($this->customCreators[$name])) {
  167. $gateway = $this->callCustomCreator($name, $config);
  168. } else {
  169. $className = $this->formatGatewayClassName($name);
  170. $gateway = $this->makeGateway($className, $config);
  171. }
  172. if (!($gateway instanceof GatewayInterface)) {
  173. throw new InvalidArgumentException(\sprintf('Gateway "%s" must implement interface %s.', $name, GatewayInterface::class));
  174. }
  175. return $gateway;
  176. }
  177. /**
  178. * Make gateway instance.
  179. *
  180. * @param string $gateway
  181. * @param array $config
  182. *
  183. * @return \Overtrue\EasySms\Contracts\GatewayInterface
  184. *
  185. * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException
  186. */
  187. protected function makeGateway($gateway, $config)
  188. {
  189. if (!\class_exists($gateway) || !\in_array(GatewayInterface::class, \class_implements($gateway))) {
  190. throw new InvalidArgumentException(\sprintf('Class "%s" is a invalid easy-sms gateway.', $gateway));
  191. }
  192. return new $gateway($config);
  193. }
  194. /**
  195. * Format gateway name.
  196. *
  197. * @param string $name
  198. *
  199. * @return string
  200. */
  201. protected function formatGatewayClassName($name)
  202. {
  203. if (\class_exists($name) && \in_array(GatewayInterface::class, \class_implements($name))) {
  204. return $name;
  205. }
  206. $name = \ucfirst(\str_replace(['-', '_', ''], '', $name));
  207. return __NAMESPACE__."\\Gateways\\{$name}Gateway";
  208. }
  209. /**
  210. * Call a custom gateway creator.
  211. *
  212. * @param string $gateway
  213. * @param array $config
  214. *
  215. * @return mixed
  216. */
  217. protected function callCustomCreator($gateway, $config)
  218. {
  219. return \call_user_func($this->customCreators[$gateway], $config);
  220. }
  221. /**
  222. * @param string|\Overtrue\EasySms\Contracts\PhoneNumberInterface $number
  223. *
  224. * @return \Overtrue\EasySms\Contracts\PhoneNumberInterface|string
  225. */
  226. protected function formatPhoneNumber($number)
  227. {
  228. if ($number instanceof PhoneNumberInterface) {
  229. return $number;
  230. }
  231. return new PhoneNumber(\trim($number));
  232. }
  233. /**
  234. * @param array|string|\Overtrue\EasySms\Contracts\MessageInterface $message
  235. *
  236. * @return \Overtrue\EasySms\Contracts\MessageInterface
  237. */
  238. protected function formatMessage($message)
  239. {
  240. if (!($message instanceof MessageInterface)) {
  241. if (!\is_array($message)) {
  242. $message = [
  243. 'content' => $message,
  244. 'template' => $message,
  245. ];
  246. }
  247. $message = new Message($message);
  248. }
  249. return $message;
  250. }
  251. /**
  252. * @param array $gateways
  253. *
  254. * @return array
  255. *
  256. * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException
  257. */
  258. protected function formatGateways(array $gateways)
  259. {
  260. $formatted = [];
  261. foreach ($gateways as $gateway => $setting) {
  262. if (\is_int($gateway) && \is_string($setting)) {
  263. $gateway = $setting;
  264. $setting = [];
  265. }
  266. $formatted[$gateway] = $setting;
  267. $globalSettings = $this->config->get("gateways.{$gateway}", []);
  268. if (\is_string($gateway) && !empty($globalSettings) && \is_array($setting)) {
  269. $formatted[$gateway] = new Config(\array_merge($globalSettings, $setting));
  270. }
  271. }
  272. $result = [];
  273. foreach ($this->strategy()->apply($formatted) as $name) {
  274. $result[$name] = $formatted[$name];
  275. }
  276. return $result;
  277. }
  278. }