PHP的异步SMPP协议实现。

v0.1.7 2022-04-04 09:46 UTC

This package is not auto-updated.

Last update: 2024-09-16 20:41:54 UTC


README

ci Codecov Software License

❗ 该库在第一次主要版本发布之前将处于实验性状态。

有关协议的更多信息,请参阅规范

内容

安装

composer require operation-hardcode/smpp-php

要求

此库需要PHP 8.1或更高版本。

建议安装phpinnacle/ext-buffer扩展以加速phpinnacle/buffer

特性

  • BIND_RECEIVER
  • BIND_TRANSMITTER
  • BIND_TRANSCEIVER
  • ALERT_NOTIFICATION
  • CANCEL_SM
  • DATA_SM
  • DELIVER_SM
  • ENQUIRE_LINK
  • GENERIC_NACK
  • OUTBIND
  • QUERY_SM
  • REPLACE_SM
  • SUBMIT_SM
  • UNBIND
  • SUBMIT_MULTI

用法

接收器

<?php

declare(strict_types=1);

require_once __DIR__.'/../vendor/autoload.php';

use OperationHardcode\Smpp;
use OperationHardcode\Smpp\Interaction\Connector;
use OperationHardcode\Smpp\Protocol\PDU;
use OperationHardcode\Smpp\Transport\ConnectionContext;

Amp\Loop::run(function (): \Generator {
    $executor = Connector::connect()
        ->asReceiver(ConnectionContext::default(uri: 'smscsim.melroselabs.com:2775', systemId: '900238', password: 'c58775'));

    try {
        yield $executor->consume(function (PDU $pdu, Smpp\Interaction\SmppExecutor $executor): \Generator {
            var_dump($pdu);

            yield $executor->fin();
        });
    } catch (Smpp\Interaction\ConnectionWasNotEstablished) {
        yield $executor->fin();

        Amp\Loop::stop();
    }
});

发送器

<?php

declare(strict_types=1);

require_once __DIR__.'/../vendor/autoload.php';

use OperationHardcode\Smpp\Interaction\Connector;
use OperationHardcode\Smpp\Protocol\Command\SubmitSm;
use OperationHardcode\Smpp\Protocol\Destination;
use OperationHardcode\Smpp\Transport\ConnectionContext;

Amp\Loop::run(function (): \Generator {
    $transmitter = Connector::connect()
        ->asTransmitter(ConnectionContext::default(uri: 'smscsim.melroselabs.com:2775', systemId: '900238', password: 'c58775'));

    yield $transmitter->produce(new SubmitSm(new Destination('xxxx'), new Destination('xxxxx'), 'Hello, world'));
    yield $transmitter->fin();
});

收发器

<?php

declare(strict_types=1);

require_once __DIR__.'/../vendor/autoload.php';

use Amp\Log\ConsoleFormatter;
use Amp\Log\StreamHandler;
use Monolog\Logger;
use Monolog\Processor\MemoryPeakUsageProcessor;
use Monolog\Processor\MemoryUsageProcessor;
use Monolog\Processor\PsrLogMessageProcessor;
use OperationHardcode\Smpp;
use OperationHardcode\Smpp\Interaction\Connector;
use OperationHardcode\Smpp\Protocol\PDU;
use OperationHardcode\Smpp\Transport\ConnectionContext;
use Psr\Log\LoggerInterface;

function stdoutLogger(string $loggerName): LoggerInterface
{
    $handler = new StreamHandler(Amp\ByteStream\getStdout());
    $handler->setFormatter(new ConsoleFormatter());

    return new Logger($loggerName, [$handler], [new PsrLogMessageProcessor(), new MemoryUsageProcessor(), new MemoryPeakUsageProcessor()]);
}

Amp\Loop::run(function (): \Generator {
    $transceiver = Connector::connect()->asTransceiver(
        ConnectionContext::default(uri: 'smscsim.melroselabs.com:2775', systemId: '900238', password: 'c58775'),
        stdoutLogger('transceiver'),
    );

    try {
        yield $transceiver->consume(function (PDU $pdu, Smpp\Interaction\SmppExecutor $executor): \Generator {
            if ($pdu instanceof Smpp\Protocol\Command\Replyable) {
                yield $executor->produce($pdu->reply());
            }

            var_dump($pdu);

            return new Amp\Success();
        });
    } catch (\Throwable $e) {
        echo $e->getMessage() . \PHP_EOL;

        Amp\Loop::stop();
    }
});

信号

<?php

declare(strict_types=1);

require_once __DIR__.'/../vendor/autoload.php';

use Amp;
use OperationHardcode\Smpp\Interaction\Connector;
use OperationHardcode\Smpp\Transport\ConnectionContext;
use OperationHardcode\Smpp;

Amp\Loop::run(function (): \Generator {
    $logger = stdoutLogger('transceiver');

    $transceiver = Connector::connect()
        ->asTransceiver(
            ConnectionContext::default(uri: 'smscsim.melroselabs.com:2775', systemId: '900238', password: 'c58775'),
            $logger
        );

    Amp\Loop::unreference(
        Amp\Loop::onSignal(\SIGINT, function () use ($transceiver): \Generator {
            yield $transceiver->fin();
        })
    );

    try {
        yield $transceiver->consume(function (PDU $pdu, SmppExecutor $executor): \Generator {
           if ($pdu instanceof Smpp\Protocol\Command\Replyable) {
                $reply = $pdu->reply();

                yield $executor->produce($reply);
            }

            return new Amp\Success();
        });
    } catch (\Throwable $e) {
        echo $e->getMessage() . \PHP_EOL;

        Amp\Loop::stop();
    }
});

扩展

如果您在处理库时需要更多选项,可以编写一个扩展。该库提供了4个钩子,这些钩子在执行器工作不同时间被调用。

  • 如果您想扩展成功连接的行为,实现OperationHardcode\Smpp\Interaction\Extensions\AfterConnectionEstablishedExtension接口。
  • 如果您想扩展断开连接的行为,实现OperationHardcode\Smpp\Interaction\Extensions\AfterConnectionClosedExtension接口。
  • 如果您想扩展执行器产生的每个PDU的行为,实现OperationHardcode\Smpp\Interaction\Extensions\AfterPduProducedExtension接口。
  • 或者相反,如果您想扩展执行器消耗的每个PDU的行为,实现OperationHardcode\Smpp\Interaction\Extensions\AfterPduConsumedExtension接口。
<?php

declare(strict_types=1);

require_once __DIR__.'/../vendor/autoload.php';

use Amp;
use OperationHardcode\Smpp\Interaction\Connector;
use OperationHardcode\Smpp\Transport\ConnectionContext;
use OperationHardcode\Smpp;
use OperationHardcode\Smpp\Interaction\SmppExecutor;
use OperationHardcode\Smpp\Protocol\PDU;
use Psr\Log\LoggerInterface;
use OperationHardcode\Smpp\Interaction\Extensions\AfterConnectionEstablishedExtension;
use OperationHardcode\Smpp\Interaction\Extensions\AfterConnectionClosedExtension;
use OperationHardcode\Smpp\Interaction\Extensions\AfterPduConsumedExtension;
use OperationHardcode\Smpp\Interaction\Extensions\AfterPduProducedExtension;

final class Debug implements
    AfterConnectionEstablishedExtension,
    AfterConnectionClosedExtension,
    AfterPduConsumedExtension,
    AfterPduProducedExtension
{
    public function __construct(private LoggerInterface $logger)
    {
    }

    public function afterConnectionEstablished(SmppExecutor $smppExecutor): Amp\Promise
    {
        return Amp\call(function (): void {
           $this->logger->debug('Connection was established.');
        });
    }

    public function afterConnectionClosed(?\Throwable $e = null): Amp\Promise
    {
        return Amp\call(function () use ($e): void {
            $this->logger->debug('Connection was closed.');
        });
    }

    public function afterPduConsumed(PDU $pdu, SmppExecutor $smppExecutor): Amp\Promise
    {
        return Amp\call(function () use ($pdu): void {
            $this->logger->debug('The pdu "{pdu}" was consumed.', [
                'pdu' => get_class($pdu),
            ]);
        });
    }

    public function afterPduProduced(PDU $pdu, SmppExecutor $smppExecutor): Amp\Promise
    {
        return Amp\call(function () use ($pdu): void {
            $this->logger->debug('The pdu "{pdu}" was produced.', [
                'pdu' => get_class($pdu),
            ]);
        });
    }
}

Amp\Loop::run(function (): \Generator {
    $logger = stdoutLogger('transceiver');

    $transceiver = Connector::connect()
        ->asTransceiver(
            ConnectionContext::default(uri: 'smscsim.melroselabs.com:2775', systemId: '900238', password: 'c58775'),
            $logger
        )
        ->withExtensions([
            new Debug($logger),
        ]);

    Amp\Loop::unreference(
        Amp\Loop::onSignal(\SIGINT, function () use ($transceiver): \Generator {
            yield $transceiver->fin();
        })
    );

    try {
        yield $transceiver->consume(function (PDU $pdu, SmppExecutor $executor): \Generator {
           if ($pdu instanceof Smpp\Protocol\Command\Replyable) {
                $reply = $pdu->reply();

                yield $executor->produce($reply);
            }

            return new Amp\Success();
        });
    } catch (\Throwable $e) {
        echo $e->getMessage() . \PHP_EOL;

        Amp\Loop::stop();
    }
});

心跳

该库提供了一个Heartbeat扩展,它定期发送SMPP规范要求的ENQUIRE_LINK命令。您可以配置间隔和超时,以便在此期间必须接收到带有命令状态ESME_ROKENQUIRE_LINK_RESP

<?php

declare(strict_types=1);

require_once __DIR__.'/../vendor/autoload.php';

use Amp;
use OperationHardcode\Smpp\Interaction\Connector;
use OperationHardcode\Smpp\Transport\ConnectionContext;
use OperationHardcode\Smpp\Interaction\SmppExecutor;
use OperationHardcode\Smpp\Interaction\Heartbeat\Heartbeat;
use OperationHardcode\Smpp\Time;

Amp\Loop::run(function (): \Generator {
    $logger = stdoutLogger('transceiver');

    $transceiver = Connector::connect()
        ->asTransceiver(
            ConnectionContext::default(uri: 'smscsim.melroselabs.com:2775', systemId: '900238', password: 'c58775'),
            $logger
        )
        ->withExtensions([
            new Heartbeat(
                Time::fromSeconds(10), // interval
                Time::fromSeconds(2), // timeout
                $logger,
            ),
        ]);

    Amp\Loop::unreference(
        Amp\Loop::onSignal(\SIGINT, function () use ($transceiver): \Generator {
            yield $transceiver->fin();
        })
    );

    try {
        yield $transceiver->consume(function (PDU $pdu, SmppExecutor $executor): \Generator {
           if ($pdu instanceof Smpp\Protocol\Command\Replyable) {
                $reply = $pdu->reply();

                yield $executor->produce($reply);
            }

            return new Amp\Success();
        });
    } catch (\Throwable $e) {
        echo $e->getMessage() . \PHP_EOL;

        Amp\Loop::stop();
    }
});

测试

$ composer test

许可

MIT许可(MIT)。有关更多信息,请参阅许可文件