PHP code example of hamzi / portflow

1. Go to this page and download the library: Download hamzi/portflow library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

hamzi / portflow example snippets


use Hamzi\PortFlow\Domain\Events\ProductScanned;

class HandleBarcodeScan
{
    public function handle(ProductScanned $event): void
    {
        $product = Product::where('barcode', $event->barcode)->firstOrFail();
        // process $product ...
    }
}

Event::listen(ProductScanned::class, HandleBarcodeScan::class);

// config/portflow.php
'default_driver' => 'raw-json',

'driver_options' => [
    'raw-json' => [
        'delimiter' => "\n",
        'max_bytes'  => 16384,   // rolling buffer ceiling
    ],
],

'mappings' => [
    [
        'driver'              => 'raw-json',
        'payload_field'       => 'type',
        'equals'              => 'barcode.scan',
        'event'               => ProductScanned::class,
        'event_payload_field' => 'barcode',
    ],
],

$bytes = PortFlow::print('receipts.order', ['order' => $order]);
// $bytes can be sent directly to the printer via Web Serial

use Hamzi\PortFlow\Infrastructure\Printing\EscPosBuilder;

$bytes = (new EscPosBuilder)
    ->align('center')
    ->bold()
    ->text('ACME STORE')
    ->bold(false)
    ->divider()
    ->align('left')
    ->text('Item 1 ................. $9.99')
    ->text('Item 2 ................. $4.50')
    ->divider()
    ->bold()
    ->text('TOTAL ................. $14.49')
    ->bold(false)
    ->feed(3)
    ->cut()
    ->bytes();

$frame->payload['weight']   // "12.500"
$frame->payload['segments'] // ["12.500", "kg", "SCALE-A1"]
$frame->payload['raw']      // "12.500;kg;SCALE-A1"

PortFlow::encode('rs232', ['TARE', '0', 'RESET']);
// → "TARE,0,RESET\n"

// config/portflow.php
'drivers' => [
    'my-scale' => \App\SerialDrivers\MyScaleDriver::class,
],

use Hamzi\PortFlow\Domain\Contracts\SerialDriver;
use Hamzi\PortFlow\Domain\DTO\SerialFrame;

final class MyModbusDriver implements SerialDriver
{
    public function name(): string
    {
        return 'modbus';
    }

    public function configure(array $options = []): void
    {
        // Store $options for use in parse/encode
    }

    public function encodeOutbound(array|string $payload): string
    {
        // Serialize $payload to device-specific bytes
        return is_string($payload) ? $payload : json_encode($payload);
    }

    /** @return array<int, SerialFrame> */
    public function parseInbound(string $chunk, array $context = []): array
    {
        return [
            SerialFrame::now($this->name(), ['data' => $chunk], $context),
        ];
    }
}

PortFlow::registerDriver('modbus', MyModbusDriver::class);

use Hamzi\PortFlow\Domain\Contracts\SerialEvent;

final class WeightReceived implements SerialEvent
{
    public function __construct(
        public readonly string $value,
        public readonly array  $context = [],
    ) {}
}

'backend' => [
    'allowed_devices' => [
        '/dev/ttyUSB0',
        '/dev/ttyACM*',
        'COM*',
        '\\\\.\\COM*',
    ],
    'default_chunk_bytes' => 256,
    'default_read_sleep_us' => 20000,
],

// config/portflow.php
'mappings' => [
    // Fire ProductScanned when a raw-json frame has type = "barcode.scan"
    [
        'driver'              => 'raw-json',
        'payload_field'       => 'type',
        'equals'              => 'barcode.scan',
        'event'               => ProductScanned::class,
        'event_payload_field' => 'barcode',
    ],

    // Match every ESC/POS frame (no payload filter)
    [
        'driver' => 'escpos',
        'event'  => ProductScanned::class,
        'event_payload_field' => 'barcode',
    ],

    // Persist weight readings directly to an Eloquent model
    [
        'driver'    => 'rs232',
        'model'     => \App\Models\WeightReading::class,
        'field_map' => [
            'value' => 'weight',   // model column => payload key
            'unit'  => 'segments.1',
        ],
    ],
],

// config/portflow.php  (or .env)
'queue_routing' => env('PORTFLOW_QUEUE_ROUTING', false),

$bytes = PortFlow::print('receipts.order', ['order' => $order]);
// Send $bytes to the printer via Web Serial write() or a TCP socket

use Hamzi\PortFlow\Domain\Services\IoTFrameBuffer;

$buffer = new IoTFrameBuffer(delimiter: "\n", maxBytes: 8192);

$frames = $buffer->push("partial-");  // → []
$frames = $buffer->push("data\n");    // → ["partial-data"]

// Flush any incomplete frame before closing the connection
$remainder = $buffer->flushRemainder();

// config/portflow.php

return [
    /*
    |--------------------------------------------------------------------------
    | Default Driver
    |--------------------------------------------------------------------------
    */
    'default_driver' => env('PORTFLOW_DEFAULT_DRIVER', 'raw-json'),

    /*
    |--------------------------------------------------------------------------
    | Ingest Endpoint
    |--------------------------------------------------------------------------
    */
    'ingest_path' => env('PORTFLOW_INGEST_PATH', '/portflow/ingest'),

    /*
    |--------------------------------------------------------------------------
    | Ingest Middleware
    |--------------------------------------------------------------------------
    | Do NOT add TrimStrings or ConvertEmptyStringsToNull — they corrupt
    | binary delimiters (\n, \r\n) in serial chunk data.
    */
    'ingest_middleware' => ['web'],

    /*
    |--------------------------------------------------------------------------
    | Security
    |--------------------------------------------------------------------------
    */
    'max_chunk_bytes'   => env('PORTFLOW_MAX_CHUNK_BYTES', 16384),
    'ingest_rate_limit' => env('PORTFLOW_RATE_LIMIT', 60),

    /*
    |--------------------------------------------------------------------------
    | Queue Routing
    |--------------------------------------------------------------------------
    | Set to true to dispatch SerialFrames via the queue instead of
    | processing them synchronously inside the HTTP request cycle.
    */
    'queue_routing' => env('PORTFLOW_QUEUE_ROUTING', false),

    /*
    |--------------------------------------------------------------------------
    | Drivers
    |--------------------------------------------------------------------------
    */
    'drivers' => [
        'raw-json' => \Hamzi\PortFlow\Infrastructure\Drivers\RawJsonDriver::class,
        'escpos'   => \Hamzi\PortFlow\Infrastructure\Drivers\EscPosDriver::class,
        'rs232'    => \Hamzi\PortFlow\Infrastructure\Drivers\Rs232Driver::class,
    ],

    /*
    |--------------------------------------------------------------------------
    | Driver Options
    |--------------------------------------------------------------------------
    */
    'driver_options' => [
        'raw-json' => [
            'delimiter' => "\n",
            'max_bytes' => 16384,
        ],
        'rs232' => [
            'delimiter' => "\n",
        ],
        'escpos' => [],
    ],

    /*
    |--------------------------------------------------------------------------
    | Mappings
    |--------------------------------------------------------------------------
    | Automatically route frames to Events or Eloquent models.
    | Supported keys per mapping:
    |   driver              — match only frames from this driver (optional)
    |   payload_field       — payload key to match on (optional)
    |   equals              — expected value of payload_field (optional)
    |   event               — fully-qualified event class to dispatch
    |   event_payload_field — payload key passed as first constructor arg
    |   model               — Eloquent model class to create
    |   field_map           — [ 'column' => 'payload_key' ]
    */
    'mappings' => [
        [
            'driver'              => 'raw-json',
            'payload_field'       => 'type',
            'equals'              => 'barcode.scan',
            'event'               => \Hamzi\PortFlow\Domain\Events\ProductScanned::class,
            'event_payload_field' => 'barcode',
        ],
        [
            'driver'              => 'escpos',
            'event'               => \Hamzi\PortFlow\Domain\Events\ProductScanned::class,
            'event_payload_field' => 'barcode',
        ],
    ],
];

[
  'barcode' => '4006381333931',
  'raw' => '4006381333931\r',
  'length' => 13,
]

'default_driver' => 'barcode-line',

'driver_options' => [
    'barcode-line' => [
        'delimiter' => "\n",
        'strip_prefix' => [],
        'strip_suffix' => ["\r", "\n", "\t"],
    ],
],

[
  'tag' => '7A005B0FF8D6',
  'raw' => '7A005B0FF8D6\r\n',
  'raw_hex' => '023741303035423046463844360D0A03',
  'format' => 'stx-etx-ascii',
]

'driver_options' => [
    'rfid-ascii' => [
        'stx' => "\x02",
        'etx' => "\x03",
        'uppercase' => true,
    ],
],

[
  'packet_type' => 7,
  'packet_type_name' => 'ack',
  'address_hex' => 'FFFFFFFF',
  'data_hex' => '00',
  'checksum' => 11,
  'checksum_calculated' => 11,
  'checksum_valid' => true,
  'raw_hex' => 'EF01FFFFFFFF07000300000B',
]

'driver_options' => [
    'fingerprint-packet' => [
        'start_code_hex' => 'EF01',
    ],
],
bash
php artisan vendor:publish --tag=portflow-config
bash
php artisan vendor:publish --tag=portflow-assets
bash
php artisan vendor:publish --tag=portflow-stubs
bash
php artisan portflow:make-driver MyScale
# creates app/SerialDrivers/MyScaleDriver.php

php artisan portflow:make-driver Modbus --namespace="App\\Hardware\\Drivers"
# creates app/Hardware/Drivers/ModbusDriver.php
bash
php artisan portflow:listen /dev/ttyUSB0 --driver=barcode-line --baud=115200
bash
php artisan portflow:listen COM3 --driver=barcode-line --baud=115200
bash
php artisan queue:work
bash
composer analyse