Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 73 additions & 40 deletions src/Discord/Discord.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
use Ratchet\Client\Connector;
use Ratchet\Client\WebSocket;
use Ratchet\RFC6455\Messaging\Message;
use React\Dns\Config\Config as DnsConfig;
use React\EventLoop\Loop;
use React\EventLoop\LoopInterface;
use React\EventLoop\TimerInterface;
Expand Down Expand Up @@ -95,6 +96,29 @@
* @property PrivateChannelRepository $private_channels
* @property SoundRepository $sounds
* @property UserRepository $users
*
* @property array $options
* @property string $options['token']
* @property LoggerInterface $options['logger']
* @property LoopInterface $options['loop']
* @property array|bool $options['loadAllMembers']
* @property array $options['disabledEvents']
* @property bool $options['storeMessages']
* @property array|bool $options['retrieveBans']
* @property int|null $options['large_threshold']
* @property array|null $options['shard']
* @property int|null $options['shard_id']
* @property int|null $options['num_shards']
* @property int|null $options['shardId']
* @property int|null $options['shardCount']
* @property array|null $options['presence']
* @property int $options['intents']
* @property array $options['socket_options']
* @property DnsConfig|string $options['dnsConfig']
* @property string[] $options['cache']
Comment thread
valzargaming marked this conversation as resolved.
Outdated
* @property string $options['collection']
* @property bool $options['useTransportCompression']
Comment on lines +105 to +125
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PHPDoc uses @property tags with array offsets (e.g. $options['token']). This syntax isn’t understood by most PHPDoc parsers (including phpDocumentor), so it’s likely to be ignored or mis-parsed. If the goal is to document the options array shape for tooling, consider using an @var array{...} shape (or a @phpstan-type/@psalm-type) instead of @property entries for offsets.

Suggested change
* @property array $options
* @property string $options['token']
* @property LoggerInterface $options['logger']
* @property LoopInterface $options['loop']
* @property array|bool $options['loadAllMembers']
* @property array $options['disabledEvents']
* @property bool $options['storeMessages']
* @property array|bool $options['retrieveBans']
* @property int|null $options['large_threshold']
* @property array|null $options['shard']
* @property int|null $options['shard_id']
* @property int|null $options['num_shards']
* @property int|null $options['shardId']
* @property int|null $options['shardCount']
* @property array|null $options['presence']
* @property int $options['intents']
* @property array $options['socket_options']
* @property DnsConfig|string $options['dnsConfig']
* @property array $options['cache']
* @property string $options['collection']
* @property bool $options['useTransportCompression']
* @property array{
* token: string,
* logger: LoggerInterface,
* loop: LoopInterface,
* loadAllMembers: array|bool,
* disabledEvents: array,
* storeMessages: bool,
* retrieveBans: array|bool,
* large_threshold: int|null,
* shard: array|null,
* shard_id: int|null,
* num_shards: int|null,
* shardId: int|null,
* shardCount: int|null,
* presence: array|null,
* intents: int,
* socket_options: array,
* dnsConfig: DnsConfig|string,
* cache: array,
* collection: string,
* useTransportCompression: bool
* } $options

Copilot uses AI. Check for mistakes.
* @property bool $options['usePayloadCompression']
*/
class Discord
{
Expand Down Expand Up @@ -1711,7 +1735,19 @@ protected function resolveOptions(array $options = []): array
])
->setAllowedTypes('token', 'string')
->setAllowedTypes('logger', ['null', LoggerInterface::class])
->setNormalizer('logger', function ($options, $value) {
if (null === $options['logger']) {
Comment thread
valzargaming marked this conversation as resolved.
Outdated
$streamHandler = new StreamHandler('php://stdout', Level::Debug);
$lineFormatter = new LineFormatter(null, null, true, true);
$streamHandler->setFormatter($lineFormatter);

return new Monolog('DiscordPHP', [$streamHandler]);
}

return $value;
})
->setAllowedTypes('loop', LoopInterface::class)
->setNormalizer('loop', fn ($options, $value) => $value ?? Loop::get())
Comment thread
valzargaming marked this conversation as resolved.
Outdated
->setAllowedTypes('loadAllMembers', ['bool', 'array'])
->setAllowedTypes('disabledEvents', 'array')
->setAllowedTypes('storeMessages', 'bool')
Expand All @@ -1724,8 +1760,44 @@ protected function resolveOptions(array $options = []): array
->setAllowedTypes('shardCount', ['null', 'int'])
->setAllowedTypes('presence', ['null', 'array'])
->setAllowedTypes('intents', ['array', 'int'])
->setNormalizer('intents', function ($options, $value) {
if (is_array($value)) {
$intent = 0;
$validIntents = Intents::getValidIntents();

foreach ($value as $idx => $i) {
if (! in_array($i, $validIntents)) {
throw new IntentException('Given intent at index '.$idx.' is invalid.');
}

$intent |= $i;
}

$value = $intent;
}

return (int) $value;
})
->setAllowedTypes('socket_options', 'array')
->setAllowedTypes('dnsConfig', ['string', \React\Dns\Config\Config::class])
->setNormalizer('socket_options', function ($options, $value) {
// Discord doesn't currently support IPv6
// This prevents xdebug from catching exceptions when trying to fetch IPv6
// for Discord
$value['happy_eyeballs'] = false;

return $value;
})
->setAllowedTypes('dnsConfig', ['string', DnsConfig::class])
->setNormalizer('dnsConfig', function ($options, $value) {
if (null === $value) {
$value = DnsConfig::loadSystemConfigBlocking();
if (! $value->nameservers) {
$value->nameservers[] = '8.8.8.8';
}
Comment on lines +1791 to +1797
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dnsConfig normalizer only runs when the caller explicitly sets the option; since dnsConfig has no default in the resolver, omitting it will bypass this normalizer and rely on separate manual initialization later in resolveOptions(). To fully centralize normalization, consider setting a default (e.g. null) so the normalizer always executes, and then remove the later manual dnsConfig fallback block.

Copilot uses AI. Check for mistakes.
}

return $value;
})
Comment thread
valzargaming marked this conversation as resolved.
->setAllowedTypes('collection', 'string')
->setNormalizer('collection', function ($options, $value) {
if (is_string($value) && class_exists($value) && is_subclass_of($value, ExCollectionInterface::class)) {
Expand All @@ -1751,50 +1823,11 @@ protected function resolveOptions(array $options = []): array

$options = $resolver->resolve($options);

$options['loop'] ??= Loop::get();

if (null === $options['logger']) {
$streamHandler = new StreamHandler('php://stdout', Level::Debug);
$lineFormatter = new LineFormatter(null, null, true, true);
$streamHandler->setFormatter($lineFormatter);
$logger = new Monolog('DiscordPHP', [$streamHandler]);
$options['logger'] = $logger;
}

if (! isset($options['dnsConfig'])) {
$dnsConfig = \React\Dns\Config\Config::loadSystemConfigBlocking();
if (! $dnsConfig->nameservers) {
$dnsConfig->nameservers[] = '8.8.8.8';
}

$options['dnsConfig'] = $dnsConfig;
}

if (is_array($options['intents'])) {
$intent = 0;
$validIntents = Intents::getValidIntents();

foreach ($options['intents'] as $idx => $i) {
if (! in_array($i, $validIntents)) {
throw new IntentException('Given intent at index '.$idx.' is invalid.');
}

$intent |= $i;
}

$options['intents'] = $intent;
}

if ($options['loadAllMembers'] && ! ($options['intents'] & Intents::GUILD_MEMBERS)) {
throw new IntentException('You have enabled the `loadAllMembers` option but have not enabled the required `GUILD_MEMBERS` intent.'.
'See the documentation on the `loadAllMembers` property for more information: http://discord-php.github.io/DiscordPHP/#basics');
}

// Discord doesn't currently support IPv6
// This prevents xdebug from catching exceptions when trying to fetch IPv6
// for Discord
$options['socket_options']['happy_eyeballs'] = false;

return $options;
}

Expand Down