Current section
Files
Jump to
Current section
Files
lib/phoenix/socket_client/message.ex
defmodule Phoenix.SocketClient.Message do
@moduledoc """
Defines the message structure and protocol for Phoenix Channels communication.
This module provides message encoding/decoding and handles both V1 and V2
Phoenix Channels protocol versions. It defines the structure for messages
sent between client and server.
## Message Structure
The message struct contains:
- `:topic` - The channel topic (e.g., "rooms:lobby")
- `:event` - The event name (e.g., "phx_join", "new_msg")
- `:payload` - The message payload (any term)
- `:channel_pid` - The channel process PID (internal use)
- `:ref` - Message reference for reply matching
- `:join_ref` - Reference for channel joins
## Protocol Versions
Supports both V1 and V2 Phoenix Channels protocols:
- V1: Legacy protocol with different message format
- V2: Current protocol with improved message structure
"""
@typedoc """
Represents a Phoenix Channels message.
"""
@type t :: %__MODULE__{
topic: String.t() | nil,
event: String.t() | nil,
payload: any(),
channel_pid: pid() | nil,
ref: String.t() | nil,
join_ref: String.t() | nil
}
defstruct topic: nil,
event: nil,
payload: nil,
channel_pid: nil,
ref: nil,
join_ref: nil
@doc """
Returns the appropriate serializer module for the given protocol version.
## Parameters
* `vsn` - Protocol version string ("1.0.0" or "2.0.0")
## Returns
* Module - Serializer module for the protocol version
## Deprecation Notice
V1 protocol ("1.0.0") is deprecated and will be removed in a future version.
Use V2 protocol ("2.0.0") for new applications.
"""
@spec serializer(String.t()) :: module()
def serializer("1.0.0") do
IO.warn("Phoenix Channels V1 protocol is deprecated. Use V2 (2.0.0) instead.")
__MODULE__.V1
end
def serializer("2.0.0"), do: __MODULE__.V2
def serializer(_), do: __MODULE__.V2
@doc """
Decodes a raw message string into a Message struct.
## Parameters
* `serializer` - Serializer module (V1 or V2)
* `msg` - Raw JSON message string
* `json_library` - JSON library module (e.g., Jason)
## Returns
* `Phoenix.SocketClient.Message.t()` - Decoded message struct
## Examples
message = Phoenix.SocketClient.Message.decode!(V2, raw_json, Jason)
"""
@spec decode!(module(), String.t(), module()) :: t()
def decode!(serializer, msg, json_library) do
json_library.decode!(msg)
|> serializer.decode!()
end
@doc """
Encodes a Message struct into a JSON string.
## Parameters
* `serializer` - Serializer module (V1 or V2)
* `msg` - Message struct to encode
* `json_library` - JSON library module (e.g., Jason)
## Returns
* String - JSON-encoded message
## Examples
json = Phoenix.SocketClient.Message.encode!(V2, message, Jason)
"""
@spec encode!(module(), t(), module()) :: String.t()
def encode!(serializer, %__MODULE__{} = msg, json_library) do
serializer.encode!(msg)
|> json_library.encode!()
end
@doc """
Creates a join message for channel subscription.
## Parameters
* `topic` - Channel topic (e.g., "rooms:lobby")
* `params` - Join parameters (map or keyword list)
## Returns
* `Phoenix.SocketClient.Message.t()` - Join message
## Examples
message = Phoenix.SocketClient.Message.join("rooms:lobby", %{user_id: 123})
"""
@spec join(String.t(), map() | keyword()) :: t()
def join(topic, params) do
ref = generate_ref()
%__MODULE__{
topic: topic,
event: "phx_join",
payload: params,
ref: ref,
join_ref: ref
}
end
@doc """
Creates a leave message for channel unsubscription.
## Parameters
* `topic` - Channel topic to leave
## Returns
* `Phoenix.SocketClient.Message.t()` - Leave message
## Examples
message = Phoenix.SocketClient.Message.leave("rooms:lobby")
"""
@spec leave(String.t()) :: t()
def leave(topic) do
%__MODULE__{
topic: topic,
event: "phx_leave",
payload: %{},
ref: generate_ref(),
join_ref: nil
}
end
@doc """
Creates a push message for sending data to a channel.
## Parameters
* `topic` - Channel topic
* `event` - Event name (e.g., "new_msg")
* `payload` - Message payload
## Returns
* `Phoenix.SocketClient.Message.t()` - Push message
## Examples
message = Phoenix.SocketClient.Message.push("rooms:lobby", "new_msg", %{body: "Hello"})
"""
@spec push(String.t(), String.t(), any()) :: t()
def push(topic, event, payload) do
ref = generate_ref()
%__MODULE__{
topic: topic,
event: event,
payload: payload,
ref: ref
}
end
@doc """
Generates a unique reference string for message correlation.
## Returns
* String - Unique reference string
"""
@spec generate_ref() :: String.t()
def generate_ref do
Integer.to_string(System.unique_integer([:positive]))
end
end