wor/sockets

Laravel 5.5+ 的 Sockets 管理器

v1.0.2 2019-05-11 17:53 UTC

This package is auto-updated.

Last update: 2024-09-12 05:54:12 UTC


README

这是一个开发需要通过 UDP/TCP Sockets 检索或发送数据的应用程序的好起点。

需求

  • PHP >= 7.0
  • Laravel >= 5.5

快速安装

$ composer require wor/sockets:"^1.0"

或者

composer.json 文件的 require 列表中添加 wor/sockets:"^1.0"

服务提供者 & Facade(在 Laravel 5.5+ 中可选)

在您的 config/app.php 文件中注册提供者和 Facade。

'providers' => [
    ...,
    Wor\Sockets\SocketsServiceProvider::class,
]

'aliases' => [
    ...,
    'Sockets' => Wor\Sockets\SocketsFacade::class,
]

配置(可选)

$ php artisan vendor:publish --provider="Wor\Sockets\SocketsServiceProvider"

调试模式

要启用调试模式,只需将 SOCKET_DEBUG 设置为 true,该软件包将回显通过 Sockets 发送和接收的原始有效载荷。

重要:请确保在您的应用程序处于生产状态时将 SOCKET_DEBUG 设置为 false。

使用方法

基本用法

  1. 将默认的 socket 远程添加到您的 .env 文件中

    ...
    SOCKET_CONNECTION=default
    SOCKET_HOST=<YOUR IP HERE>
    SOCKET_PORT=<YOUR PORT HERE>
    SOCKET_PROTOCOL=tcp
    SOCKET_DEBUG=true
    ...
    
  2. 在 Laravel 或 tinker 会话期间

    Sockets::write('Message');
    $response = Sockets::read(); //returns the string response from the socket.
    

预期用法

本软件包旨在进行扩展。这里有两个基本概念:Sockets 和 Requests。

Sockets

Sockets 是基本的通信层。它们应该被扩展以包含您可能想要使用或接收方服务期望的任何帧包装或编码。

它们应该扩展基础 Socket 类并实现 SocketInterface。

简而言之,您应该实现读 & 写方法以匹配您的预期 I/O。

<?php

namespace Wor\Salto\Connectors;

use Wor\Sockets\Connectors\Socket;
use Wor\Sockets\Connectors\SocketInterface;

class SaltoSocket extends Socket implements SocketInterface
{
    /**
     * @var string protocol identifier
     */
    public $protocol = 'STP';

    /**
     * @var string version of the salto protocol
     */
    public $version = '00';

    /**
     * Craft the ship packet header
     * @return string header
     */
    public function getHeader()
    {
        return "$this->protocol/$this->version/$this->length/";
    }

    /**
     * Salto SHIP protocol based write method   **overloads base write() method**
     * Wraps the message in the expected xml format and creates
     *
     * @param string $message to send
     *
     * @return bool success
     */
    public function write(string $message): bool
    {
        $data = '<?xml version="1.0" encoding="ISO-8859-1"?><RequestCall>'.$message.'</RequestCall>';
        $this->length = \strlen($data); //setups the length displayed in the header
        return parent::write($this->getHeader().$data);
    }

    /**
     * Salto SHIP protocol based read method   **overloads base write() method**
     * Reads header and uses expected message length to quickly load the response
     *
     * @param int $timeout in seconds
     *
     * @return string|null received message
     */
    public function read($timeout = 25): string
    {
        $this->protocol = stream_get_line($this->socket,4, '/');
        $this->version = stream_get_line($this->socket,3, '/');
        $this->length = (int) stream_get_line($this->socket,8, '/');

        stream_set_timeout($this->socket, $timeout);
        $read = stream_get_contents($this->socket, $this->length);
        return $read;
    }
}

Requests

Requests 用于封装执行的操作。它们应匹配您正在与之工作的服务的端点。把它们想象成数据库事务。一旦设置有效载荷并发出请求,它就会在提供的套接字上执行写和读操作,并返回解析后的结果。

Requests 扩展基础 Request 类并实现 RequestInterface。

<?php

namespace Wor\Salto\Requests;

use Wor\Salto\Connectors\SaltoSocket as Socket;
use Wor\Salto\Exceptions\EncoderConnectionException;
use Wor\Salto\Exceptions\EncoderTimeoutException;
use Wor\Salto\Exceptions\XMLDocumentException;
use Wor\Salto\Exceptions\OperationNotSupportedException;
use Wor\Salto\Exceptions\SaltoException;
use Wor\Sockets\Requests\Request;
use Wor\Sockets\Requests\RequestInterface;
use Illuminate\Support\Collection;

class SaltoRequest extends Request implements RequestInterface {

    /**
     * @var string ship protocol RequestName
     */
    protected $name;

    /**
     * @var array of parameters
     */
    protected $params = [];

    /**
     * @var string json encoded response
     */
    protected $json;

    /**
     * @var Collection final response object
     */
    protected $collection;

    /**
     * Salto Request constructor.
     *
     * @param Socket        $socket
     * @param string|array  $params
     * @param string        $name
     */
    public function __construct(Socket $socket, $params = [], $name = null)
    {
        $this->boot();
        parent::__construct($socket);

        if (\is_array($params)) {
            $this->setParams($params);
        } elseif (\is_string($params)) {
            // Allow to ignore parameters array and pass the name of the request directly
            $name = $params;
        }
        if($name !== null) { // Avoid overriding self defined default request name
            $this->setName($name);
        }
        $this->beforePayload();
        $this->prepPayload();
    }

    /**
     * Generic boot method to be changed in subclasses
     * ran before the rest of the constructor
     */
    public function boot(): void
    {
        //
    }

    /**
     * Generic boot method to be changed in subclasses
     * ran before setting up the payload
     */
    public function beforePayload(): void
    {
        //
    }

    /**
     * @param string $name
     */
    public function setName($name): void
    {
        $this->name = $name;
    }

    /**
     * @param array $params
     */
    public function setParams($params): void
    {
        $this->params = $params;
    }

    /**
     * Return formatted response of request
     *
     * @return Collection|mixed
     */
    public function getResponse()
    {
        //$this->checkException();
        return new Collection($this->collection);
    }

    /**
     * Parse name and params into xml payload
     */
    private function prepPayload(): void
    {
        $header = '<RequestName>'.$this->name.'</RequestName>';
        $params = '<Params>';

        foreach ($this->params as $key => $value) {
            if ($key !== null && $value !== null) {
                // cast boolean values to int for output to ship xml
                !\is_bool($value) ?: $value = (int) $value;

                // Output associative array as an ship parameter entry
                $params .= '<'.$key.'>'.$value.'</'.$key.'>';
            }
        }
        $this->payload = $header.$params.'</Params>';
    }

    /**
     * Run the request on socket and return the response
     *
     * @return bool|mixed false if failed else return formatted response
     *
     * @throws SaltoException
     */
    public function make()
    {
        if($this->socket !== null && $this->socket->write($this->payload)) {
            $this->response = $this->socket->read();
            $xml = simplexml_load_string($this->response);
            $this->json = json_encode($xml);
            $this->collection = collect(json_decode($this->json, true));
            $this->checkException();
            return $this->getResponse();
        }
        return false;
    }

    /**
     * Check the response for known ship protocol exceptions
     *
     * @return bool
     *
     * @throws SaltoException
     */
    public function checkException(): bool
    {
        if($this->collection->has('Exception')) {
            $exception = $this->collection['Exception'];

            switch ( (int) $exception['Code']) {
                case 4:
                    throw new OperationNotSupportedException($exception['Message']);
                    break;
                case 12:
                    throw new XMLDocumentException($exception['Message']);
                    break;
                case 401:
                    throw new EncoderConnectionException($exception['Message']);
                    break;
                case 403:
                    throw new EncoderTimeoutException($exception['Message']);
                    break;
                default:
                    throw new SaltoException($exception['Message'], $exception['Code']);
                    break;
            }
        }
        return false;
    }
}

然后您可以自由地实例化新的请求并针对您的套接字服务运行它们。

$socket = Socket::socket('default');
$params = []; //the parameters you want to send through
$request = new SaltoRequest($socket, $params, 'GetInfo');
$response = $request->get();

实际上,您可以进一步创建基于您新的遗传服务请求的动作请求。

$socket = Socket::socket('default');
$params = []; //the parameters you want to send through
$request = new GetInfoRequest($socket, $params);
$response = $request->get();
<?php

namespace Wor\Salto\Requests;

use Wor\Salto\Requests\SaltoRequest;

class GetInfoRequest extends SaltoRequest {

        protected $name = 'GetInfo'; //overload the default name with the request type
    
        public function getResponse() //overload the default getResponse action.
        {
            $response = parent::getResponse();
            //Manipulate the response collection as you see fit. maybe match to domain models..
            return $response;
        }
}

有问题吗?

如果您发现任何错误,请随时在问题跟踪器中报告。