Oihana PHP Signals

Signaler

Contract for objects that communicate through signal/slot patterns.

This interface defines the core API for event-driven communication systems where objects can emit signals and connect receivers to handle those signals. It provides a decoupled, flexible way to implement the observer pattern with priority-based execution and lifecycle management.

Implementing classes should provide:

  • Registration and removal of receivers (observers/listeners)
  • Signal emission to notify all connected receivers
  • Priority-based execution ordering
  • Auto-disconnect capability for one-time listeners
  • Thread-safe receiver management (if applicable)

Use cases:

  • Event systems (user interactions, system events)
  • Plugin architectures (extensibility points)
  • State change notifications (model updates)
  • Command pattern implementations
  • Message buses and pub/sub systems
Tags
example

Basic implementation usage

// Any class implementing Signaler can be used polymorphically
function setupEventHandlers( Signaler $signal ): void
{
    $signal->connect( fn($data) => logEvent($data), priority: 10 );
    $signal->connect( fn($data) => sendNotification($data) );

    if ( $signal->connected() ) {
        $signal->emit( 'Event data' );
    }
}

$mySignal = new Signal();
setupEventHandlers( $mySignal );
author

Marc Alcaraz (ekameleon)

since
1.0.0
see
Signal

The primary implementation of this interface

Receiver

Interface for objects that receive signals

Table of Contents

Properties

$length  : int
The number of currently connected receivers.

Methods

connect()  : bool
Connects a receiver to this signal.
connected()  : bool
Checks if any receivers are connected.
disconnect()  : bool
Disconnects a receiver or all receivers from this signal.
emit()  : void
Emits a signal, notifying all connected receivers with provided values.
hasReceiver()  : bool
Checks if a specific receiver is currently connected.

Properties

$length read-only virtual

The number of currently connected receivers.

public int $length

This read-only property provides a quick way to check how many receivers are currently listening to this signal. Useful for debugging, monitoring, and conditional logic based on listener count.

Always >= 0. Returns 0 when no receivers are connected.

Tags
example
function emitIfListeners( Signaler $signal, mixed $data ): void
{
    if ( $signal->length > 0 ) {
        echo "Notifying {$signal->length} listener(s)..." . PHP_EOL;
        $signal->emit( $data );
    } else {
        echo "No listeners attached, skipping emit." . PHP_EOL;
    }
}
Hooks
public int get

Methods

connect()

Connects a receiver to this signal.

public connect(callable|Receiver $receiver[, int $priority = 0 ][, bool $autoDisconnect = false ]) : bool

Registers a callable or Receiver object to be notified when this signal is emitted. Receivers are executed in priority order (highest first). Duplicate connections are prevented - attempting to connect the same receiver twice returns false.

Parameters
$receiver : callable|Receiver

The receiver to connect. Can be:

  • A closure or arrow function: fn($x) => process($x)
  • A function name: 'myFunction'
  • An array callable: [$object, 'method']
  • A Receiver object: Must implement Receiver interface
$priority : int = 0

Execution priority. Higher values execute first. Default: 0 (normal priority)

  • Use positive values (1-100+) for high-priority handlers
  • Use negative values (-1 to -100) for low-priority handlers
$autoDisconnect : bool = false

If true, receiver disconnects after first emit. Default: false (persistent connection)

  • true: One-time listener (useful for initialization)
  • false: Permanent listener (standard behavior)
Tags
example
$signal = new Signal();

// Connect various receiver types
$signal->connect( fn() => echo 'Lambda' );                    // Closure
$signal->connect( [$logger, 'log'], priority: 100 );         // High priority method
$signal->connect( $receiver, autoDisconnect: true );         // One-time

// Duplicate connection prevented
$handler = fn() => echo 'test';
$signal->connect( $handler );  // true (first connection)
$signal->connect( $handler );  // false (already connected)
Return values
bool —

True if successfully connected, false if:

  • Receiver is already connected (prevents duplicates)
  • Receiver is not callable and not a Receiver object

connected()

Checks if any receivers are connected.

public connected() : bool

Provides a boolean check for whether this signal has active listeners. More semantic than checking $length > 0 and useful for conditional signal emission or validation logic.

Tags
example
function notifyUsers( Signaler $userSignal, string $message ): void
{
    if ( !$userSignal->connected() ) {
        throw new RuntimeException( 'No notification handlers registered' );
    }

    $userSignal->emit( $message );
}

// Conditional emit
if ( $signal->connected() ) {
    $signal->emit( $data );
} else {
    // Fallback behavior when no listeners
    logWarning( 'No listeners for event' );
}
Return values
bool —

True if at least one receiver is connected, false if none.

disconnect()

Disconnects a receiver or all receivers from this signal.

public disconnect([callable|Receiver|null $receiver = null ]) : bool

Removes receivers from the signal's notification list. Can disconnect a specific receiver or clear all receivers at once. Safe to call multiple times - returns false if receiver is not found.

Parameters
$receiver : callable|Receiver|null = null

The receiver to disconnect:

  • If null: Disconnects ALL receivers (complete reset)
  • If callable/Receiver: Disconnects only that specific receiver
Tags
example
$signal = new Signal();
$handler1 = fn() => echo 'A';
$handler2 = fn() => echo 'B';

$signal->connect( $handler1 );
$signal->connect( $handler2 );

// Disconnect specific receiver
$signal->disconnect( $handler1 );     // true (found and removed)
$signal->disconnect( $handler1 );     // false (not found)

// Disconnect all receivers
$signal->disconnect();                // true (cleared all)
$signal->disconnect();                // false (already empty)

// Use case: Cleanup in destructor
class EventEmitter
{
    private Signaler $signal;

    public function __destruct()
    {
        $this->signal->disconnect(); // Clean up all listeners
    }
}
Return values
bool —

True if successfully disconnected, false if:

  • Specific receiver was not found (already disconnected)
  • No receivers exist when trying to disconnect all

emit()

Emits a signal, notifying all connected receivers with provided values.

public emit(mixed ...$values) : void

Invokes all connected receivers in priority order (highest priority first), passing the specified values to each receiver. Receivers marked with autoDisconnect are automatically removed after execution.

Execution characteristics:

  • Receivers execute in priority order (higher values first)
  • All receivers get the same parameter values
  • Auto-disconnect receivers are removed after their call
  • Empty receiver list is handled gracefully (no-op)
  • Exceptions in receivers should be handled by implementation
Parameters
$values : mixed

Zero or more values to pass to each receiver. All receivers receive all values in the same order.

Tags
example
$signal = new Signal();

// Connect receivers
$signal->connect( fn($user, $action) =>
    logActivity($user, $action),
    priority: 10
);

$signal->connect( fn($user, $action) =>
    sendNotification($user, $action)
);

// Emit with multiple parameters
$signal->emit( 'Alice', 'logged_in' );
// Both receivers called with: ('Alice', 'logged_in')

// Emit with no parameters
$signal->connect( fn() => echo 'Tick!' . PHP_EOL );
$signal->emit();
// Receivers with no required params are called

// Safe to emit with no receivers
$emptySignal = new Signal();
$emptySignal->emit( 'data' ); // No-op, no error

Advanced: Error handling pattern

class SafeSignal implements Signaler
{
    public function emit( mixed ...$values ): void
    {
        foreach( $this->receivers as $entry ) {
            try {
                call_user_func_array( $entry->receiver, $values );
            } catch ( \Throwable $e ) {
                $this->handleReceiverError( $entry, $e );
            }
        }
    }
}
Return values
void —

No return value. Fire-and-forget notification pattern.

hasReceiver()

Checks if a specific receiver is currently connected.

public hasReceiver(callable|Receiver $receiver) : bool

Determines whether a given receiver is in this signal's active listener list. Useful for preventing duplicate connections, conditional logic, and debugging signal state.

Parameters
$receiver : callable|Receiver

The receiver to search for. Must be the exact same reference as when connected.

Tags
example
$signal = new Signal();
$handler = fn() => echo 'test';

// Check before connecting
if ( !$signal->hasReceiver( $handler ) ) {
    $signal->connect( $handler );
}

// Verify connection
assert( $signal->hasReceiver( $handler ) === true );

// Check after disconnecting
$signal->disconnect( $handler );
assert( $signal->hasReceiver( $handler ) === false );

// Use case: Conditional connection
function ensureHandler( Signaler $signal, callable $handler ): void
{
    if ( !$signal->hasReceiver( $handler ) ) {
        echo "Handler not found, connecting..." . PHP_EOL;
        $signal->connect( $handler );
    } else {
        echo "Handler already connected." . PHP_EOL;
    }
}

Reference equality behavior

$signal = new Signal();

$signal->connect( fn() => echo 'A' );
$signal->hasReceiver( fn() => echo 'A' ); // false (different instance)

$handler = fn() => echo 'A';
$signal->connect( $handler );
$signal->hasReceiver( $handler ); // true (same reference)
note

Reference equality: The receiver must be the exact same instance. Two different closures with identical code are considered different.

Return values
bool —

True if the receiver is connected, false otherwise.

On this page

Search results