PHPackages                             janmarten/irc-message - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Parsing &amp; Serialization](/categories/parsing)
4. /
5. janmarten/irc-message

ActiveLibrary[Parsing &amp; Serialization](/categories/parsing)

janmarten/irc-message
=====================

IRC messages, parsing and formatting library

01PHPCI passing

Since Jan 15Pushed 5mo agoCompare

[ Source](https://github.com/janmartenjongerius/irc-message)[ Packagist](https://packagist.org/packages/janmarten/irc-message)[ RSS](/packages/janmarten-irc-message/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

[![PhpUnit](https://github.com/janmartenjongerius/irc-message/actions/workflows/phpunit.yml/badge.svg)](https://github.com/janmartenjongerius/irc-message/actions/workflows/phpunit.yml)

The purpose of this library is to understand IRC message syntax and expose its components as modern data structures inside PHP.

It implements IRC message representation following:

-
-

The aim is to be compliant with the IRCv3 specification as described in the aforementioned documentation.

Table of contents
=================

[](#table-of-contents)

- [Installation](#installation)
- [Parsing IRC message lines](#parsing-irc-message-lines)
    - [Parsing a message](#parsing-a-message)
    - [Parsing a command](#parsing-a-command)
    - [Parsing a source](#parsing-a-source)
    - [Parsing tags](#parsing-tags)
- [Composing IRC messages](#composing-irc-messages)
    - [Composing new messages](#composing-new-messages)
    - [Patching messages](#patching-messages)
- [Formatting IRC message lines](#formatting-irc-message-lines)
    - [Formatting a message](#formatting-a-message)
    - [Formatting a command](#formatting-a-command)
    - [Formatting a source](#formatting-a-source)
    - [Formatting tags](#formatting-tags)

Installation
============

[](#installation)

As a composer package, install it as follows:

```
composer require janmarten/irc-message
```

Parsing IRC message lines
=========================

[](#parsing-irc-message-lines)

Make sure that messages provided to the parser are following the IRC specification. This means that the message length, tag list length, and even the trailing CRLF are enforced according to the specification for IRCv3.

The following two example files show a custom implementation of IRC message signing using the HMAC method.

- [Sign an IRC message](examples/sign-message.php)
- [Verify an IRC message](examples/verify-message.php)

```
examples/sign-message.php Hello, World! | examples/verify-message.php
```

Parsing a message
-----------------

[](#parsing-a-message)

If the goal is to parse an entire IRC message line, use the message parser.

Either the `parse` function:

```
use function \JanMarten\IRC\Message\parse;

$message = parse('@acme.org/id=1 :Johnny!john@acme.org PRIVMSG #test :Hello, fellow chatters!');
```

> N.B.: The `parse` function will automatically append CRLF `\r\n` to the end of the string, if not present.

Or an instance of the message parser:

```
use JanMarten\IRC\Message\Contract\Parser\MessageParser;

/** @var MessageParser $parser */
$message = $parser->parseMessage(
    "@acme.org/id=1 :Johnny!john@acme.org PRIVMSG #test :Hello, fellow chatters!\r\n"
);
```

> N.B.: The message parser is stricter than the `parse` function and will find messages without trailing CRLF `\r\n` to not be a well-formed IRC message.

The message above can be represented by the following structure:

```
message:
  command:
    verb: 'PRIVMSG'
    arguments:
      - '#test'
      - 'Hello, fellow chatters!'

  source:
    mask: ':Johnny!john@acme.org'
    nick: 'Johnny'
    user: 'john'
    host: 'acme.org'

  tags:
    acme.org/id:
      key: 'acme.org/id'
      value: '1'
      clientOnly: false
```

The source object may be omitted when there is no source in the message. Contrary to the source, the tags will always be represented by a tag list object, even if there were no tags provided in the message, to make using tags as config easier.

The following exceptions can be produced when parsing a message:

Exception nameExpected when...`MalformedMessageException`When the message is not well-formed against the ABNF.`MessageTooLongException`When the message length exceeds the spec limits. Both including and excluding tags length.Parsing a command
-----------------

[](#parsing-a-command)

If the goal is to only parse the command and its arguments, then the following parser is able to simplify this workflow:

```
use JanMarten\IRC\Message\Contract\Parser\CommandParser

/** @var CommandParser $parser */
$command = $parser->parseCommand('PRIVMSG #test :Hello, world!');
```

The command above can be represented by the following structure:

```
command:
  verb: 'PRIVMSG'
  arguments:
    - '#test'
    - 'Hello, world!'
```

The following exceptions can be produced when parsing a command:

Exception nameExpected when...`MalformedCommandException`When the command is not a well-formed IRC command.Parsing a source
----------------

[](#parsing-a-source)

If the goal is to only parse the source of a message, then the following parser is able to simplify this workflow:

```
use JanMarten\IRC\Message\Contract\Parser\SourceParser;

/** @var SourceParser $parser */
$source = $parser->parseSource(':sugar');
```

The source above can be represented by the following structure:

```
source:
  mask: ':sugar'
  nick: 'sugar'
  user: null
  host: null
```

Note that the `nick` property can also represent a server, rather than a user, in case the source is meant to identify a server in server-to-server communication.

The parser cannot successfully know when `:sugar` or `:localhost` are representing a user or a server, so no attempt is made to solve this.

The following exceptions can be produced when parsing a source:

Exception nameExpected when...`MalformedSourceException`When the source is not well-formed.Parsing tags
------------

[](#parsing-tags)

If the goal is to only parse a list of tags, then the following parser is able to simplify this workflow:

```
use JanMarten\IRC\Message\Contract\Parser\TagListParser;

/** @var TagListParser $parser */
$tags = $parser->parseTags('@id=12;+irc.llm/generated;irc.twitch.tv/ban-reason=kick');
```

The tags above can be represented by the following structure:

```
tags:
  - key: id
    value: '12'
    clientOnly: false
    vendor: null
  - key: +irc.llm/generated
    value: true
    clientOnly: true
    vendor: irc.llm
  - key: irc.twitch.tv/ban-reason
    value: 'kick'
    clientOnly: false
    vendor: irc.twitch.tv
```

The tag list has the following helper functions:

SignatureDescription`get(string $key): string|bool`Get the value for a tag that matches `$key` or `false` when no tag was found.`is(string $key): bool`Produces `true` when a tag value matching `$key` can be found and its value is set to `true`, and `false` in all other circumstances.`unescape(string $key): string`Produces the unescaped value for `$key` which would otherwise have been the escaped value when calling `get($key)`. Throws `OutOfBoundsException` when no tag matching `$key` exists. Throws `\JanMarten\IRC\Message\Exception\EmptyTagException` when the selected tag value has no length.`contains(string $key): bool`Query whether a tag with the given key is set on the tags.The goal of those methods is to allow tags to be accessed as if they are configuration values.

```
use JanMarten\IRC\Message\Contract\Component\TagList;

/** @var TagList $tags */

// Conditional logic.
if ($tags->is('delayed')) {
    // ... Forward message to message queue
    return;
}

// Get information from metadata.
if ($isBanned) {
    $reason = $tags->get('irc.twitch.tv/ban-reason');
}

// Read information that could contain reserved characters.
$adminNote = $tags->unescape('irc.twitch.tv/ban-reason-comment');
```

The following exceptions can be produced when parsing a list of tags:

Exception nameExpected when...`MalformedTagListException`When the tag list is not well-formed.Composing IRC messages
======================

[](#composing-irc-messages)

While manually creating the correct IRC message objects is a perfectly valid approach, the `message` and `fromMessage` helper functions are available to expose a builder pattern for composing messages.

To compose a message, use the following example:

- [Compose a message](examples/compose-message.php)

Composing new messages
----------------------

[](#composing-new-messages)

Using default values for the message builder, composing a message can be as simple as the following:

```
use function \JanMarten\IRC\Message\message;

echo message()->command('PRIVMSG', '#test', 'Hello, World!');
```

This may produce the following, depending on your local environment:

```
:janmarten!janmarten@localhost PRIVMSG #test :Hello, World!

```

The automatically created source `:janmarten!janmarten@localhost` is sensitive to the following environment variables:

Environment variableFallbackDescription`IRC_NICK``get_current_user()`The IRC nickname.`IRC_USER``get_current_user()`The IRC user.`IRC_HOST``'localhost'`The host of the message source.In that sense, the following two lines work identically:

```
use JanMarten\IRC\Message\Component\ImmutableSource;
use function JanMarten\IRC\Message\message;

$builder1 = message();
$builder2 = message(ImmutableSource::fromEnv());
```

The `message()` function produces a message builder, which allows to register tags that are applicable to all commands produced using the `command()` method on the message builder.

```
use function \JanMarten\IRC\Message\message;

// All commands from this builder have a `acme.org/hash-algo=sha256` tag.
$builder = message()->withAddedTag('hash-algo', 'sha256', 'acme.org');

// Prepare a message for the #test channel.
$welcome = $builder->command('PRIVMSG', '#test');

// Say hi.
echo $welcome
    ->withAddedArgument('Hello, World!')
    ->withAddedTag('id', bin2hex(random_bytes(12)), 'acme.org');

// Ping the remote party.
echo $builder->command('PING', '_');
```

Would produce something like the following:

```
@acme.org/hash-algo=sha256;acme.org/id=f369169e73df5de830b03837 :janmarten!janmarten@localhost PRIVMSG #test :Hello, World!
@acme.org/hash-algo=sha256 :janmarten!janmarten@localhost PING _

```

Patching messages
-----------------

[](#patching-messages)

When patching an existing message object, the `fromMessage` helper function can be used to produce a builder that builds on top of the existing message information.

```
use function JanMarten\IRC\Message\fromMessage;
use function JanMarten\IRC\Message\parse;

$originalMessage = parse('PING _');

echo fromMessage($originalMessage)
    ->withSourceFromEnv()
    ->withAddedTag('id', bin2hex(random_bytes(16)))
;
```

Would produce something like the following:

```
@id=b2156ad7835214cab67a1408e168279d :janmarten!janmarten@localhost PING _

```

Formatting IRC message lines
============================

[](#formatting-irc-message-lines)

When a message is ready to be converted (back) into string format, the `format`helper can be used to format a message or a message component into a string.

```
use function JanMarten\IRC\Message\format;
use function JanMarten\IRC\Message\parse;

$message = parse('@id=1 :foo!foo@localhost PRIVMSG #test Hello');

echo format($message);
printf("  Command: %s\r\n", format($message->command));

if ($message->source) {
    printf("  Source:  %s\r\n", format($message->source));
}

if ($message->tags) {
    printf("  Tags:    %s\r\n", format($message->tags));
}
```

Will produce:

```
@id=1 :foo!foo@localhost PRIVMSG #test Hello
  Command: PRIVMSG #test Hello
  Source:  :foo!foo@localhost
  Tags:    @id=1

```

Formatting a message
--------------------

[](#formatting-a-message)

If the goal is to specifically format complete IRC messages, use the text message formatter.

```
use JanMarten\IRC\Message\Formatter\Message\TextMessageFormatter;
use function JanMarten\IRC\Message\parse;

$message = parse('@id=1 :foo!foo@localhost PRIVMSG #test Hello');

/** @var TextMessageFormatter $formatter */
echo $formatter->formatMessage($message);
```

Produces the following output:

```
@id=1 :foo!foo@localhost PRIVMSG #test Hello

```

Formatting a command
--------------------

[](#formatting-a-command)

When the goal is to format a command without the source and tags, use the command text formatter.

```
use JanMarten\IRC\Message\Formatter\Command\TextCommandFormatter;
use function JanMarten\IRC\Message\parse;

$message = parse('@id=1 :foo!foo@localhost PRIVMSG #test Hello');

/** @var TextCommandFormatter */
printf("%s\r\n", $formatter->formatSource($message->command));
```

Produces the following output:

```
PRIVMSG #test Hello

```

Formatting a source
-------------------

[](#formatting-a-source)

When specifically the source needs formatting, use the text source formatter.

```
use JanMarten\IRC\Message\Formatter\Source\TextSourceFormatter;
use function JanMarten\IRC\Message\parse;

$message = parse('@id=1 :foo!foo@localhost PRIVMSG #test Hello');

/** @var TextSourceFormatter $formatter */
printf("%s\r\n", $formatter->formatSource($message->source));
```

Produces the following output:

```
:foo!foo@localhost

```

Formatting tags
---------------

[](#formatting-tags)

To format a tag or a list of tags, use the text tag formatter.

```
use JanMarten\IRC\Message\Formatter\Tag\TextTagFormatter;
use function JanMarten\IRC\Message\parse;

$message = parse('@id=1;+janmarten.name/key=foo :foo!foo@localhost PRIVMSG #test Hello');

/** @var TextTagFormatter $formatter */
printf("%s\r\n", $formatter->formatTagList($message->tags));

foreach ($message->tags as $tag) {
    printf("  %s\r\n", $formatter->formatTag($tag));
}
```

Produces the following:

```
@id=1;+janmarten.name/key=foo
  id=1
  +janmarten.name/key=foo

```

The following exceptions can be produced when formatting a tag list:

Exception nameExpected when...`EmptyTagListException`When the tag list does not contain at least 1 tag.

###  Health Score

17

—

LowBetter than 6% of packages

Maintenance48

Moderate activity, may be stable

Popularity1

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity13

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/4f105dc2b106ecb7ac1819cf18ea0276730a95d04e11918e23a2bef60efc3bbd?d=identicon)[janmarten\_jongerius](/maintainers/janmarten_jongerius)

---

Top Contributors

[![janmartenjongerius](https://avatars.githubusercontent.com/u/642417?v=4)](https://github.com/janmartenjongerius "janmartenjongerius (19 commits)")

### Embed Badge

![Health badge](/badges/janmarten-irc-message/health.svg)

```
[![Health](https://phpackages.com/badges/janmarten-irc-message/health.svg)](https://phpackages.com/packages/janmarten-irc-message)
```

###  Alternatives

[mck89/peast

Peast is PHP library that generates AST for JavaScript code

19139.2M47](/packages/mck89-peast)[sauladam/shipment-tracker

Parses tracking information for several carriers, like UPS, USPS, DHL and GLS by simply scraping the data. No need for any kind of API access.

9843.5k](/packages/sauladam-shipment-tracker)[jstewmc/rtf

Read and write Rich Text Format (RTF) documents with PHP

45153.1k6](/packages/jstewmc-rtf)[tcds-io/php-jackson

A lightweight, flexible object serializer for PHP, inspired by FasterXML/jackson

113.2k10](/packages/tcds-io-php-jackson)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
