Repository: overtrue/easy-sms (language: php)

Issue:
随机算法存在问题
目前的随机算法是在对比值的时候使用mt_rand生成随机数来比较，这会导致概率分布的不均匀:
https://github.com/overtrue/easy-sms/blob/c3c5b3bc942beac18aa7cc1e435cefa5ecbe5975/src/Strategies/RandomStrategy.php#L24-L30

演示地址: https://3v4l.org/Jkh3l

附源代码:
```php
<?php
$counts = [0, 0, 0]; // 统计 A, B, C 出现在首位的次数

function rnd1(array $gateways)
    {
        uasort($gateways, function () {
            return mt_rand() - mt_rand();
        });

        return array_keys($gateways);
    }

for ($i = 0; $i < 10000; $i++) {
    $sorted = rnd1(['A' => 0, 'B' => 0, 'C' => 0]);

    // print_r($sorted);    
    $first = array_search($sorted[0], ['A', 'B', 'C']);
    $counts[$first]++;
}

echo "A appears first: " . $counts[0] / 100 . "%\n";
echo "B appears first: " . $counts[1] / 100 . "%\n";
echo "C appears first: " . $counts[2] / 100 . "%\n";
?>
```
> A appears first: 36.99%
> B appears first: 25.23%
> C appears first: 37.78%

输出结果会发现B出现在第一位的概率远远低于其他情况，基本上只有20%多一点

一个可行的方案应该是将其替换为 `shuffle` 这样可以保证足够随机和均匀



Discussion / hints:

Fix patch:
diff --git a/src/Contracts/MessageInterface.php b/src/Contracts/MessageInterface.php
index 23b34cb..ca0a7fb 100644
--- a/src/Contracts/MessageInterface.php
+++ b/src/Contracts/MessageInterface.php
@@ -22,36 +22,26 @@ interface MessageInterface
 
     /**
      * Return the message type.
-     *
-     * @return string
      */
-    public function getMessageType();
+    public function getMessageType(): string;
 
     /**
      * Return message content.
-     *
-     * @return string
      */
-    public function getContent(?GatewayInterface $gateway = null);
+    public function getContent(?GatewayInterface $gateway = null): ?string;
 
     /**
      * Return the template id of message.
-     *
-     * @return string
      */
-    public function getTemplate(?GatewayInterface $gateway = null);
+    public function getTemplate(?GatewayInterface $gateway = null): ?string;
 
     /**
      * Return the template data of message.
-     *
-     * @return array
      */
-    public function getData(?GatewayInterface $gateway = null);
+    public function getData(?GatewayInterface $gateway = null): array;
 
     /**
      * Return message supported gateways.
-     *
-     * @return array
      */
-    public function getGateways();
+    public function getGateways(): array;
 }
diff --git a/src/Contracts/PhoneNumberInterface.php b/src/Contracts/PhoneNumberInterface.php
index 7d64648..9bce12d 100644
--- a/src/Contracts/PhoneNumberInterface.php
+++ b/src/Contracts/PhoneNumberInterface.php
@@ -20,31 +20,23 @@ interface PhoneNumberInterface extends \JsonSerializable
 {
     /**
      * 86.
-     *
-     * @return int
      */
-    public function getIDDCode();
+    public function getIDDCode(): ?int;
 
     /**
      * 18888888888.
-     *
-     * @return int
      */
-    public function getNumber();
+    public function getNumber(): int|string;
 
     /**
      * +8618888888888.
-     *
-     * @return string
      */
-    public function getUniversalNumber();
+    public function getUniversalNumber(): string;
 
     /**
      * 008618888888888.
-     *
-     * @return string
      */
-    public function getZeroPrefixedNumber();
+    public function getZeroPrefixedNumber(): string;
 
     /**
      * @return string
diff --git a/src/EasySms.php b/src/EasySms.php
index d773164..d927834 100644
--- a/src/EasySms.php
+++ b/src/EasySms.php
@@ -17,6 +17,7 @@
 use Overtrue\EasySms\Contracts\PhoneNumberInterface;
 use Overtrue\EasySms\Contracts\StrategyInterface;
 use Overtrue\EasySms\Exceptions\InvalidArgumentException;
+use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
 use Overtrue\EasySms\Gateways\Gateway;
 use Overtrue\EasySms\Strategies\OrderStrategy;
 use Overtrue\EasySms\Support\Config;
@@ -26,35 +27,17 @@
  */
 class EasySms
 {
-    /**
-     * @var Config
-     */
-    protected $config;
+    protected Config $config;
 
-    /**
-     * @var string
-     */
-    protected $defaultGateway;
+    protected string $defaultGateway;
 
-    /**
-     * @var array
-     */
-    protected $customCreators = [];
+    protected array $customCreators = [];
 
-    /**
-     * @var array
-     */
-    protected $gateways = [];
+    protected array $gateways = [];
 
-    /**
-     * @var Messenger
-     */
-    protected $messenger;
+    protected Messenger $messenger;
 
-    /**
-     * @var array
-     */
-    protected $strategies = [];
+    protected array $strategies = [];
 
     /**
      * Constructor.
@@ -67,15 +50,10 @@ public function __construct(array $config)
     /**
      * Send a message.
      *
-     * @param string|array           $to
-     * @param MessageInterface|array $message
-     *
-     * @return array
-     *
+     * @throws NoGatewayAvailableException
      * @throws InvalidArgumentException
-     * @throws Exceptions\NoGatewayAvailableException
      */
-    public function send($to, $message, array $gateways = [])
+    public function send(array|string $to, MessageInterface|array $message, array $gateways = []): array
     {
         $to = $this->formatPhoneNumber($to);
         $message = $this->formatMessage($message);
@@ -91,13 +69,9 @@ public function send($to, $message, array $gateways = [])
     /**
      * Create a gateway.
      *
-     * @param string|null $name
-     *
-     * @return GatewayInterface
-     *
      * @throws InvalidArgumentException
      */
-    public function gateway($name)
+    public function gateway(?string $name): GatewayInterface
     {
         if (!isset($this->gateways[$name])) {
             $this->gateways[$name] = $this->createGateway($name);
@@ -109,20 +83,16 @@ public function gateway($name)
     /**
      * Get a strategy instance.
      *
-     * @param string|null $strategy
-     *
-     * @return StrategyInterface
-     *
      * @throws InvalidArgumentException
      */
-    public function strategy($strategy = null)
+    public function strategy(?string $strategy = null): StrategyInterface
     {
         if (\is_null($strategy)) {
             $strategy = $this->config->get('default.strategy', OrderStrategy::class);
         }
 
         if (!\class_exists($strategy)) {
-            $strategy = __NAMESPACE__.'\Strategies\\'.\ucfirst($strategy);
+            $strategy = __NAMESPACE__ . '\Strategies\\' . \ucfirst($strategy);
         }
 
         if (!\class_exists($strategy)) {
@@ -139,44 +109,32 @@ public function strategy($strategy = null)
     /**
      * Register a custom driver creator Closure.
      *
-     * @param string $name
-     *
      * @return $this
      */
-    public function extend($name, \Closure $callback)
+    public function extend(string $name, \Closure $callback): static
     {
         $this->customCreators[$name] = $callback;
 
         return $this;
     }
 
-    /**
-     * @return Config
-     */
-    public function getConfig()
+    public function getConfig(): Config
     {
         return $this->config;
     }
 
-    /**
-     * @return Messenger
-     */
-    public function getMessenger()
+    public function getMessenger(): Messenger
     {
-        return $this->messenger ?: $this->messenger = new Messenger($this);
+        return $this->messenger ??= new Messenger($this);
     }
 
     /**
      * Create a new driver instance.
      *
-     * @param string $name
-     *
-     * @return GatewayInterface
-     *
      * @throws \InvalidArgumentException
      * @throws InvalidArgumentException
      */
-    protected function createGateway($name)
+    protected function createGateway(string $name): GatewayInterface
     {
         $config = $this->config->get("gateways.{$name}", []);
 
@@ -203,14 +161,9 @@ protected function createGateway($name)
     /**
      * Make gateway instance.
      *
-     * @param string $gateway
-     * @param array  $config
-     *
-     * @return GatewayInterface
-     *
      * @throws InvalidArgumentException
      */
-    protected function makeGateway($gateway, $config)
+    protected function makeGateway(string $gateway, array $config): GatewayInterface
     {
         if (!\class_exists($gateway) || !\in_array(GatewayInterface::class, \class_implements($gateway))) {
             throw new InvalidArgumentException(\sprintf('Class "%s" is a invalid easy-sms gateway.', $gateway));
@@ -221,12 +174,8 @@ protected function makeGateway($gateway, $config)
 
     /**
      * Format gateway name.
-     *
-     * @param string $name
-     *
-     * @return string
      */
-    protected function formatGatewayClassName($name)
+    protected function formatGatewayClassName(string $name): string
     {
         if (\class_exists($name) && \in_array(GatewayInterface::class, \class_implements($name))) {
             return $name;
@@ -234,26 +183,18 @@ protected function formatGatewayClassName($name)
 
         $name = \ucfirst(\str_replace(['-', '_', ''], '', $name));
 
-        return __NAMESPACE__."\\Gateways\\{$name}Gateway";
+        return __NAMESPACE__ . "\\Gateways\\{$name}Gateway";
     }
 
     /**
      * Call a custom gateway creator.
-     *
-     * @param string $gateway
-     * @param array  $config
      */
-    protected function callCustomCreator($gateway, $config)
+    protected function callCustomCreator(string $gateway, array $config): mixed
     {
         return \call_user_func($this->customCreators[$gateway], $config);
     }
 
-    /**
-     * @param string|PhoneNumberInterface $number
-     *
-     * @return PhoneNumberInterface|string
-     */
-    protected function formatPhoneNumber($number)
+    protected function formatPhoneNumber(PhoneNumberInterface|string $number): PhoneNumberInterface
     {
         if ($number instanceof PhoneNumberInterface) {
             return $number;
@@ -262,17 +203,12 @@ protected function formatPhoneNumber($number)
         return new PhoneNumber(\trim($number));
     }
 
-    /**
-     * @param array|string|MessageInterface $message
-     *
-     * @return MessageInterface
-     */
-    protected function formatMessage($message)
+    protected function formatMessage(MessageInterface|array|string $message): MessageInterface
     {
         if (!($message instanceof MessageInterface)) {
             if (!\is_array($message)) {
                 $message = [
-                    'content' => $message,
+                    'content'  => $message,
                     'template' => $message,
                 ];
             }
@@ -284,11 +220,9 @@ protected function formatMessage($message)
     }
 
     /**
-     * @return array
-     *
      * @throws InvalidArgumentException
      */
-    protected function formatGateways(array $gateways)
+    protected function formatGateways(array $gateways): array
     {
         $formatted = [];
 
@@ -314,4 +248,4 @@ protected function formatGateways(array $gateways)
 
         return $result;
     }
-}
+}
\ No newline at end of file
diff --git a/src/Gateways/CtyunGateway.php b/src/Gateways/CtyunGateway.php
index 4dbddeb..7272c09 100644
--- a/src/Gateways/CtyunGateway.php
+++ b/src/Gateways/CtyunGateway.php
@@ -5,12 +5,11 @@
 use Overtrue\EasySms\Contracts\MessageInterface;
 use Overtrue\EasySms\Contracts\PhoneNumberInterface;
 use Overtrue\EasySms\Exceptions\GatewayErrorException;
-use Overtrue\EasySms\Gateways\Gateway;
 use Overtrue\EasySms\Support\Config;
 use Overtrue\EasySms\Traits\HasHttpRequest;
 
 /**
- * Class CtyunGateway
+ * Class CtyunGateway.
  *
  * @see https://www.ctyun.cn/document/10020426/10021544
  */
@@ -32,17 +31,17 @@ class CtyunGateway extends Gateway
     public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
     {
         $data = $message->getData($this);
-        $endpoint = self::ENDPOINT_HOST . '/sms/api/v1';
+        $endpoint = self::ENDPOINT_HOST.'/sms/api/v1';
+
         return $this->execute($endpoint, [
-            'phoneNumber' => (string)$to,
+            'phoneNumber' => (string) $to,
             'templateCode' => $this->config->get('template_code'),
-            'templateParam' => '{"code":"' . $data['code'] . '"}',
+            'templateParam' => '{"code":"'.$data['code'].'"}',
             'signName' => $this->config->get('sign_name'),
-            'action' => 'SendSms'
+            'action' => 'SendSms',
         ]);
     }
 
-
     /**
      * @return array
      *
@@ -50,35 +49,35 @@ public function send(PhoneNumberInterface $to, MessageInterface $message, Config
      */
     protected function execute(string $url, array $data)
     {
-        $uuid = date('ymdHis', time()) . substr(microtime(), 2, 6) . sprintf('%03d', rand(0, 999));
-        $time = date('Ymd', time()) . 'T' . date('His') . 'Z';
+        $uuid = date('ymdHis', time()).substr(microtime(), 2, 6).sprintf('%03d', rand(0, 999));
+        $time = date('Ymd', time()).'T'.date('His').'Z';
         $timeDate = substr($time, 0, 8);
 
-        $body = bin2hex(hash("sha256", json_encode($data), true));
+        $body = bin2hex(hash('sha256', json_encode($data), true));
         $query = '';
-        $strSignature = "ctyun-eop-request-id:" . $uuid . "\n" . "eop-date:" . $time . "\n" . "\n" . $query . "\n" . $body;
+        $strSignature = 'ctyun-eop-request-id:'.$uuid."\n".'eop-date:'.$time."\n\n".$query."\n".$body;
 
         $kTime = $this->sha256($time, $this->config->get('secret_key'));
         $kAk = $this->sha256($this->config->get('access_key'), $kTime);
 
         $kDate = $this->sha256($timeDate, $kAk);
 
-        $signature = base64_encode($this->sha256(($strSignature), $kDate));
+        $signature = base64_encode($this->sha256($strSignature, $kDate));
         $headers['Content-Type'] = 'application/json';
         $headers['ctyun-eop-request-id'] = $uuid;
-        $headers['Eop-Authorization'] = $this->config->get('access_key') . ' Headers=ctyun-eop-request-id;' . 'eop-date Signature=' . $signature;
+        $headers['Eop-Authorization'] = $this->config->get('access_key').' Headers=ctyun-eop-request-id;eop-date Signature='.$signature;
         $headers['eop-date'] = $time;
 
         $result = $this->postJson($url, $data, $headers);
-        if ($result['code'] !== self::SUCCESS_CODE) {
+        if (self::SUCCESS_CODE !== $result['code']) {
             throw new GatewayErrorException($result['message'], $result['code'], $result);
         }
+
         return $result;
     }
 
     public function sha256($str, $pass): string
     {
-        return (hash_hmac("sha256", ($str), ($pass), true));
+        return hash_hmac('sha256', $str, $pass, true);
     }
-
 }
diff --git a/src/Gateways/ErrorlogGateway.php b/src/Gateways/ErrorlogGateway.php
index bf005fb..f3bfe05 100644
--- a/src/Gateways/ErrorlogGateway.php
+++ b/src/Gateways/ErrorlogGateway.php
@@ -23,7 +23,7 @@ class ErrorlogGateway extends Gateway
     /**
      * @return array
      */
-    public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
+    public function send(PhoneNumberInterface|array $to, MessageInterface $message, Config $config)
     {
         if (is_array($to)) {
             $to = implode(',', $to);
diff --git a/src/Gateways/Ue35Gateway.php b/src/Gateways/Ue35Gateway.php
index e35beb0..ff4b569 100644
--- a/src/Gateways/Ue35Gateway.php
+++ b/src/Gateways/Ue35Gateway.php
@@ -54,7 +54,7 @@ public function send(PhoneNumberInterface $to, MessageInterface $message, Config
             'user-agent' => 'PHP EasySms Client',
         ];
 
-        $result = $this->request('get', self::getEndpointUri() . '?' . http_build_query($params), ['headers' => $headers]);
+        $result = $this->request('get', self::getEndpointUri().'?'.http_build_query($params), ['headers' => $headers]);
         if (is_string($result)) {
             $result = json_decode(json_encode(simplexml_load_string($result)), true);
         }
@@ -68,6 +68,6 @@ public function send(PhoneNumberInterface $to, MessageInterface $message, Config
 
     public static function getEndpointUri()
     {
-        return 'https://' . static::ENDPOINT_HOST . static::ENDPOINT_URI;
+        return 'https://'.static::ENDPOINT_HOST.static::ENDPOINT_URI;
     }
 }
diff --git a/src/Message.php b/src/Message.php
index 56ba105..7fe9071 100644
--- a/src/Message.php
+++ b/src/Message.php
@@ -19,37 +19,20 @@
  */
 class Message implements MessageInterface
 {
-    /**
-     * @var array
-     */
-    protected $gateways = [];
+    protected array $gateways = [];
 
-    /**
-     * @var string
-     */
-    protected $type;
+    protected string $type;
 
-    /**
-     * @var string
-     */
-    protected $content;
+    protected \Closure|string|null $content = null;
 
-    /**
-     * @var string
-     */
-    protected $template;
+    protected \Closure|string|null $template = null;
 
-    /**
-     * @var array
-     */
-    protected $data = [];
+    protected array|\Closure $data = [];
 
     /**
      * Message constructor.
-     *
-     * @param string $type
      */
-    public function __construct(array $attributes = [], $type = MessageInterface::TEXT_MESSAGE)
+    public function __construct(array $attributes = [], string $type = MessageInterface::TEXT_MESSAGE)
     {
         $this->type = $type;
 
@@ -62,30 +45,24 @@ public function __construct(array $attributes = [], $type = MessageInterface::TE
 
     /**
      * Return the message type.
-     *
-     * @return string
      */
-    public function getMessageType()
+    public function getMessageType(): string
     {
         return $this->type;
     }
 
     /**
      * Return message content.
-     *
-     * @return string
      */
-    public function getContent(?GatewayInterface $gateway = null)
+    public function getContent(?GatewayInterface $gateway = null): ?string
     {
         return is_callable($this->content) ? call_user_func($this->content, $gateway) : $this->content;
     }
 
     /**
      * Return the template id of message.
-     *
-     * @return string
      */
-    public function getTemplate(?GatewayInterface $gateway = null)
+    public function getTemplate(?GatewayInterface $gateway = null): ?string
     {
         return is_callable($this->template) ? call_user_func($this->template, $gateway) : $this->template;
     }
@@ -93,7 +70,7 @@ public function getTemplate(?GatewayInterface $gateway = null)
     /**
      * @return $this
      */
-    public function setType($type)
+    public function setType($type): static
     {
         $this->type = $type;
 
@@ -103,7 +80,7 @@ public function setType($type)
     /**
      * @return $this
      */
-    public function setContent($content)
+    public function setContent($content): static
     {
         $this->content = $content;
 
@@ -113,37 +90,29 @@ public function setContent($content)
     /**
      * @return $this
      */
-    public function setTemplate($template)
+    public function setTemplate($template): static
     {
         $this->template = $template;
 
         return $this;
     }
 
-    /**
-     * @return array
-     */
-    public function getData(?GatewayInterface $gateway = null)
+    public function getData(?GatewayInterface $gateway = null): array
     {
         return is_callable($this->data) ? call_user_func($this->data, $gateway) : $this->data;
     }
 
     /**
-     * @param array|callable $data
-     *
      * @return $this
      */
-    public function setData($data)
+    public function setData(callable|array $data): static
     {
         $this->data = $data;
 
         return $this;
     }
 
-    /**
-     * @return array
-     */
-    public function getGateways()
+    public function getGateways(): array
     {
         return $this->gateways;
     }
@@ -151,7 +120,7 @@ public function getGateways()
     /**
      * @return $this
      */
-    public function setGateways(array $gateways)
+    public function setGateways(array $gateways): static
     {
         $this->gateways = $gateways;
 
diff --git a/src/Messenger.php b/src/Messenger.php
index abea0e2..9c19b61 100644
--- a/src/Messenger.php
+++ b/src/Messenger.php
@@ -13,6 +13,7 @@
 
 use Overtrue\EasySms\Contracts\MessageInterface;
 use Overtrue\EasySms\Contracts\PhoneNumberInterface;
+use Overtrue\EasySms\Exceptions\InvalidArgumentException;
 use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;
 
 /**
@@ -24,10 +25,7 @@ class Messenger
 
     public const STATUS_FAILURE = 'failure';
 
-    /**
-     * @var EasySms
-     */
-    protected $easySms;
+    protected EasySms $easySms;
 
     /**
      * Messenger constructor.
@@ -40,11 +38,10 @@ public function __construct(EasySms $easySms)
     /**
      * Send a message.
      *
-     * @return array
-     *
+     * @throws InvalidArgumentException
      * @throws NoGatewayAvailableException
      */
-    public function send(PhoneNumberInterface $to, MessageInterface $message, array $gateways = [])
+    public function send(PhoneNumberInterface $to, MessageInterface $message, array $gateways = []): array
     {
         $results = [];
         $isSuccessful = false;
@@ -60,14 +57,7 @@ public function send(PhoneNumberInterface $to, MessageInterface $message, array
                 $isSuccessful = true;
 
                 break;
-            } catch (\Exception $e) {
-                $results[$gateway] = [
-                    'gateway' => $gateway,
-                    'status' => self::STATUS_FAILURE,
-                    'template' => $message->getTemplate($this->easySms->gateway($gateway)),
-                    'exception' => $e,
-                ];
-            } catch (\Throwable $e) {
+            } catch (\Exception|\Throwable $e) {
                 $results[$gateway] = [
                     'gateway' => $gateway,
                     'status' => self::STATUS_FAILURE,
diff --git a/src/PhoneNumber.php b/src/PhoneNumber.php
index 4c4dcee..c938c61 100644
--- a/src/PhoneNumber.php
+++ b/src/PhoneNumber.php
@@ -18,23 +18,14 @@
  */
 class PhoneNumber implements Contracts\PhoneNumberInterface
 {
-    /**
-     * @var int
-     */
-    protected $number;
+    protected int|string $number;
 
-    /**
-     * @var int
-     */
-    protected $IDDCode;
+    protected ?int $IDDCode;
 
     /**
      * PhoneNumberInterface constructor.
-     *
-     * @param int    $numberWithoutIDDCode
-     * @param string $IDDCode
      */
-    public function __construct($numberWithoutIDDCode, $IDDCode = null)
+    public function __construct(int|string $numberWithoutIDDCode, ?string $IDDCode = null)
     {
         $this->number = $numberWithoutIDDCode;
         $this->IDDCode = $IDDCode ? intval(ltrim($IDDCode, '+0')) : null;
@@ -42,50 +33,37 @@ public function __construct($numberWithoutIDDCode, $IDDCode = null)
 
     /**
      * 86.
-     *
-     * @return int
      */
-    public function getIDDCode()
+    public function getIDDCode(): ?int
     {
         return $this->IDDCode;
     }
 
     /**
      * 18888888888.
-     *
-     * @return int
      */
-    public function getNumber()
+    public function getNumber(): int|string
     {
         return $this->number;
     }
 
     /**
      * +8618888888888.
-     *
-     * @return string
      */
-    public function getUniversalNumber()
+    public function getUniversalNumber(): string
     {
         return $this->getPrefixedIDDCode('+').$this->number;
     }
 
     /**
      * 008618888888888.
-     *
-     * @return string
      */
-    public function getZeroPrefixedNumber()
+    public function getZeroPrefixedNumber(): string
     {
         return $this->getPrefixedIDDCode('00').$this->number;
     }
 
-    /**
-     * @param string $prefix
-     *
-     * @return string|null
-     */
-    public function getPrefixedIDDCode($prefix)
+    public function getPrefixedIDDCode(string $prefix): ?string
     {
         return $this->IDDCode ? $prefix.$this->IDDCode : null;
     }
@@ -116,10 +94,8 @@ public function jsonSerialize()
 
     /**
      * Check if the phone number belongs to chinese mainland.
-     *
-     * @return bool
      */
-    public function inChineseMainland()
+    public function inChineseMainland(): bool
     {
         return empty($this->IDDCode) || 86 === $this->IDDCode;
     }
diff --git a/src/Strategies/OrderStrategy.php b/src/Strategies/OrderStrategy.php
index e5ef583..f2d1e67 100644
--- a/src/Strategies/OrderStrategy.php
+++ b/src/Strategies/OrderStrategy.php
@@ -20,10 +20,8 @@ class OrderStrategy implements StrategyInterface
 {
     /**
      * Apply the strategy and return result.
-     *
-     * @return array
      */
-    public function apply(array $gateways)
+    public function apply(array $gateways): array
     {
         return array_keys($gateways);
     }
diff --git a/src/Strategies/RandomStrategy.php b/src/Strategies/RandomStrategy.php
index 373398f..6c7c2e6 100644
--- a/src/Strategies/RandomStrategy.php
+++ b/src/Strategies/RandomStrategy.php
@@ -18,15 +18,23 @@
  */
 class RandomStrategy implements StrategyInterface
 {
-    /**
-     * @return array
-     */
-    public function apply(array $gateways)
+    public function apply(array $gateways): array
     {
-        uasort($gateways, function () {
-            return mt_rand() - mt_rand();
-        });
+        try {
+            $keys = \array_keys($gateways);
+            $n = \count($keys);
 
-        return array_keys($gateways);
+            for ($i = $n - 1; $i > 0; --$i) {
+                $j = \random_int(0, $i);
+                [$keys[$i], $keys[$j]] = [$keys[$j], $keys[$i]];
+            }
+
+            return $keys;
+        } catch (\Throwable $exception) {
+            $keys = \array_keys($gateways);
+            shuffle($keys);
+
+            return $keys;
+        }
     }
 }
diff --git a/src/Support/Config.php b/src/Support/Config.php
index 770726b..54d0c84 100644
--- a/src/Support/Config.php
+++ b/src/Support/Config.php
@@ -18,10 +18,7 @@
  */
 class Config implements \ArrayAccess
 {
-    /**
-     * @var array
-     */
-    protected $config;
+    protected array $config;
 
     /**
      * Config constructor.
@@ -34,9 +31,9 @@ public function __construct(array $config = [])
     /**
      * Get an item from an array using "dot" notation.
      *
-     * @param string $key
+     * @return array|mixed|null
      */
-    public function get($key, $default = null)
+    public function get(string $key, mixed $default = null): mixed
     {
         $config = $this->config;
 
@@ -44,7 +41,7 @@ public function get($key, $default = null)
             return $config[$key];
         }
 
-        if (false === strpos($key, '.')) {
+        if (!str_contains($key, '.')) {
             return $default;
         }
 
@@ -75,7 +72,7 @@ public function get($key, $default = null)
      * @since 5.0.0
      */
     #[\ReturnTypeWillChange]
-    public function offsetExists($offset)
+    public function offsetExists(mixed $offset): bool
     {
         return array_key_exists($offset, $this->config);
     }
@@ -94,7 +91,7 @@ public function offsetExists($offset)
      * @since 5.0.0
      */
     #[\ReturnTypeWillChange]
-    public function offsetGet($offset)
+    public function offsetGet(mixed $offset): mixed
     {
         return $this->get($offset);
     }
@@ -114,7 +111,7 @@ public function offsetGet($offset)
      * @since 5.0.0
      */
     #[\ReturnTypeWillChange]
-    public function offsetSet($offset, $value)
+    public function offsetSet(mixed $offset, mixed $value): void
     {
         if (isset($this->config[$offset])) {
             $this->config[$offset] = $value;
@@ -133,7 +130,7 @@ public function offsetSet($offset, $value)
      * @since 5.0.0
      */
     #[\ReturnTypeWillChange]
-    public function offsetUnset($offset)
+    public function offsetUnset(mixed $offset): void
     {
         if (isset($this->config[$offset])) {
             unset($this->config[$offset]);


Test patch:
diff --git a/tests/EasySmsTest.php b/tests/EasySmsTest.php
index 870fbaf..e85ea9d 100644
--- a/tests/EasySmsTest.php
+++ b/tests/EasySmsTest.php
@@ -61,26 +61,30 @@ public function testSend()
             return $number instanceof PhoneNumber && !empty($number->getNumber());
         }), \Mockery::on(function ($message) {
             return $message instanceof MessageInterface && !empty($message->getContent());
-        }), [])->andReturn('send-result');
+        }), [])->andReturn([]);
 
         $easySms = \Mockery::mock(EasySms::class.'[getMessenger]', [['default' => DummyGatewayForTest::class]]);
         $easySms->shouldReceive('getMessenger')->andReturn($messenger);
 
         // simple
-        $this->assertSame('send-result', $easySms->send('18888888888', ['content' => 'hello']));
+        $this->assertIsArray($easySms->send('18888888888', ['content' => 'hello']));
 
         // message object
         $message = new Message(['content' => 'hello']);
-        $this->assertSame('send-result', $easySms->send('18888888888', $message, []));
+        $this->assertIsArray($easySms->send('18888888888', $message, []));
 
         // phone number object
         $number = new PhoneNumber('18888888888', 35);
         $message = new Message(['content' => 'hello']);
         $messenger = \Mockery::mock(Messenger::class);
-        $messenger->expects()->send($number, $message, [])->andReturn('mock-result');
+        $messenger->expects()->send(
+            \Mockery::type(PhoneNumber::class),
+            \Mockery::type(Message::class),
+            [],
+        )->andReturn([]);
         $easySms = \Mockery::mock(EasySms::class.'[getMessenger]', [['default' => DummyGatewayForTest::class]]);
         $easySms->shouldReceive('getMessenger')->andReturn($messenger);
-        $this->assertSame('mock-result', $easySms->send($number, $message));
+        $this->assertIsArray($easySms->send($number, $message));
     }
 
     public function testFormatMessage()
diff --git a/tests/Gateways/CtyunGatewayTest.php b/tests/Gateways/CtyunGatewayTest.php
index c5da3f7..44803e2 100644
--- a/tests/Gateways/CtyunGatewayTest.php
+++ b/tests/Gateways/CtyunGatewayTest.php
@@ -1,6 +1,5 @@
 <?php
 
-
 namespace Overtrue\EasySms\Tests\Gateways;
 
 use Overtrue\EasySms\Exceptions\GatewayErrorException;
@@ -33,7 +32,7 @@ public function testSend()
             'content' => 'mock-content',
             'template' => 'mock-tpl-id', // 模板ID
             'data' => [
-                "code" => 123456,
+                'code' => 123456,
             ],
         ]);
 
diff --git a/tests/Gateways/ErrorlogGatewayTest.php b/tests/Gateways/ErrorlogGatewayTest.php
index 94c4384..52a9fd5 100644
--- a/tests/Gateways/ErrorlogGatewayTest.php
+++ b/tests/Gateways/ErrorlogGatewayTest.php
@@ -40,14 +40,14 @@ public function testSend()
             'data' => ['foo' => 'bar'],
         ]);
 
-        $gateway->send(new PhoneNumber(new PhoneNumber(18188888888)), $message, new Config());
+        $gateway->send(new PhoneNumber(18188888888), $message, new Config());
 
         $this->assertTrue(file_exists($this->logFile));
         $this->assertNotFalse(
             strpos(
                 file_get_contents($this->logFile),
-                'to: 18188888888 | message: "This is a test message."  | template: "" | data: {"foo":"bar"}'
-            )
+                'to: 18188888888 | message: "This is a test message."  | template: "" | data: {"foo":"bar"}',
+            ),
         );
     }
 }