Очистите несколько чатов Twitch IRC

Я всегда боролся за создание устойчивого, организованного и чистого кода. Я попытался использовать метод Factory, и теперь он работает лучше, когда я создал другой класс.

Я знаю, что должен написать документацию, но это еще не тот совет, который мне нужен. Потому что я не думаю, что документация поможет организовать мой код.

Этот код очищает несколько чатов Twitch IRC и использует мой Laravel API для подсчета подписчиков нескольких стримеров. Код работает, но мне кажется, что становится все труднее реализовывать функции, изменять код и даже читать его.

Есть ли какой-нибудь шаблон дизайна, который я должен использовать для этого? Как мне сделать это лучше?

Запрос:

class Request
{
    private $requestUrl;
    private $requestFields;
    private $requestResult;
    private $requestStreamer;
    private $requestStatus;
    private $requestType;
    private $ch;

    public function __construct($streamer = null, $status = null, $url = null, $customFields = null)
    {
        $this->requestStreamer = $streamer;
        $this->requestStatus = $status;
        $this->requestFields = $customFields;
        $this->requestUrl = $url;
        $this->start();
    }
    public function setFields($fields = null)
    {
        if ($fields != null)
            $this->requestFields = $fields;

    }
    public function start()
    {
        if ($this->requestUrl == 'online') {
            $this->requestUrl="http://localhost:8000/api/streamers/changeOnline";
            $this->setFields(['streamer' => $this->requestStreamer, 'is_online' => $this->requestStatus]);
            $this->request();
            return;
        }
        if ($this->requestUrl == 'status') {
            $this->requestUrl="http://localhost:8000/api/streamers/changeStatus";
            $this->setFields(['streamer' => $this->requestStreamer, 'run' => $this->requestStatus]);
            $this->request();
            return;
        }
        if ($this->requestUrl == 'sub') {
            $this->requestUrl="http://localhost:8000/api/create/sub";
            $this->setFields();
            $this->request();
            dump($this->requestFields);
            return;
        }
        if ($this->requestUrl == 'checkTwitchOnline') {
            $this->requestUrl="https://api.twitch.tv/helix/streams/?user_login=" . $this->requestStreamer;
            $this->requestType="get";
            $this->setFields(array('Authorization: Bearer gokyy7wxa9apriyjr2evaccv6h71qn', 'Client-ID: gosbl0lt05vzj18la6v11lexhvpwlb'));
            $this->request();

            return $this->decode();
        }
        if ($this->requestUrl == 'getStreamers') {
            $this->requestUrl="http://localhost:8000/api/streamers/getAll";
            $this->requestType="get";
            $this->request();
            return $this->decode();
        }
        if ($this->requestUrl == null) {
            $this->requestUrl="http://localhost:8000/api/streamers/changeStatus";
            $this->setFields(['streamer' => $this->requestStreamer, 'run' => $this->requestStatus]);
            $this->request();

            $this->requestUrl="http://localhost:8000/api/streamers/changeOnline";
            $this->setFields(['streamer' => $this->requestStreamer, 'is_online' => $this->requestStatus]);
            $this->request();

            return;
        }
        return;
    }

    public function setRequestType()
    {
        curl_setopt($this->ch, CURLOPT_URL, $this->requestUrl);

        if ($this->requestType == 'get') {
            curl_setopt($this->ch, CURLOPT_HTTPGET, true);
            $this->setHeader();
        } else {
            curl_setopt($this->ch, CURLOPT_POST, true);
            $this->setHeader();
        }
        curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
    }
    public function setHeader()
    {
        if (!empty($this->requestFields)) {
            $fields_string = http_build_query($this->requestFields);

            if ($this->requestType == 'get') {
                curl_setopt($this->ch, CURLOPT_HTTPHEADER, $this->requestFields);
            } else {
                curl_setopt($this->ch, CURLOPT_POSTFIELDS, $fields_string);
            }
        }
    }
    public function request()
    {
        $this->ch = curl_init();
        $this->setRequestType();
        $this->requestResult = curl_exec($this->ch);

        return $this->result();
    }
    public function decode()
    {
        return json_decode($this->requestResult, true);
    }
    public function result()
    {
        //dump($this->requestResult);
        return $this->requestResult;
    }
}
class RequestFactory
{
    public static function create($streamer = null, $status = null, $url = null, $customFields = null)
    {
        return new Request($streamer, $status, $url, $customFields);
    }
}
$changeStatus = RequestFactory::create(null, null, 'getStreamers');

Главный файл:


use GhostZeroTmiClient;
use GhostZeroTmiClientOptions;
use GhostZeroTmiEventsTwitczhSubEvent;
use GhostZeroTmiEventsTwitchAnonSubGiftEvent;
use GhostZeroTmiEventsTwitchAnonSubMysteryGiftEvent;
use GhostZeroTmiEventsTwitchResubEvent;
use GhostZeroTmiEventsTwitchSubGiftEvent;
use GhostZeroTmiEventsTwitchSubMysteryGiftEvent;

include('requestFactory.php');
$streamers = RequestFactory::create(null, null, 'getStreamers');
$streamers = $streamers->decode();

for ($i = 0; $i <= count($streamers) - 1; ++$i) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        die('could not fork');
    } else if ($pid) {
        // we are the parent
        // pcntl_wait($status); //Protect against Zombie children
    } else {
        $GLOBALS['streamer'] = ($streamers[$i]['streamer']);
        cli_set_process_title($GLOBALS['streamer'] . 'run.php');

        $request = RequestFactory::create($GLOBALS['streamer'], '1', 'status');
        $request = RequestFactory::create($GLOBALS['streamer'], null, 'checkTwitchOnline');
        $data = $request->decode();
        
        if (empty($data['data'])) {
            $request = RequestFactory::create($GLOBALS['streamer'], '0');
            die();
        }

        $request = RequestFactory::create($GLOBALS['streamer'], '1', 'online');

        $client = new Client(new ClientOptions([
            'options' => ['debug' => false],
            'connection' => [
                'secure' => true,
                'reconnect' => true,
                'rejoin' => true,
            ],
            'channels' => [$GLOBALS['streamer']]
        ]));

        /**
         * @param SubGiftEvent $event
         */
        function giftedRequest($event, $type): void
        {

            $fields = ['recipient' => $event->recipient, 'plan' => $event->plan->plan, 'type' => $type, 'gifter' => $event->user, 'streamer' => $GLOBALS['streamer']];
            RequestFactory::create($GLOBALS['streamer'], null,'sub',$fields);
        }

        /**
         * @param SubEvent $event
         */
        function subbedRequest($event, $type): void
        {
            $fields = ['recipient' => $event->user, 'plan' => $event->plan->plan, 'type' => $type, 'gifter' => NULL, 'streamer' => $GLOBALS['streamer']];
            RequestFactory::create($GLOBALS['streamer'], null,'sub',$fields);
        }

        $client->on(SubEvent::class, function (SubEvent $event) {
            subbedRequest($event, 'SubEvent');
        });
        $client->on(AnonSubGiftEvent::class, function (AnonSubGiftEvent $event) {
            print_r($event);
        });

        $client->on(AnonSubMysteryGiftEvent::class, function (AnonSubMysteryGiftEvent $event) {
            print_r($event);
        });
        $client->on(ResubEvent::class, function (ResubEvent $event) {
            subbedRequest($event, 'ResubEvent');
        });
        $client->on(SubGiftEvent::class, function (SubGiftEvent $event) {
            giftedRequest($event, 'SubGiftEvent');
        });
        $client->on(SubMysteryGiftEvent::class, function (SubMysteryGiftEvent $event) {
            subbedRequest($event, 'SubMysteryGiftEvent');
        });
        $client->connect();
    }
}

Это мой первый вопрос, дайте мне знать, как я могу его улучшить.

1 ответ
1

Идея названия: Как провести рефакторинг простого кода API-клиента в PHP CLI

Вот пара советов:

  1. Я бы добавил if‘песок switchкак можно скорее. Например, вы настраиваете поля и заголовки Request::class внутри него, непосредственно перед выполнением запроса cURL. Почему не во время создания Request :: class? Выполните рефакторинг своей фабрики, чтобы добавить правильные параметры при создании экземпляра запроса. Параметры также можно сопоставить следующим образом:
    public static array $requestMapper = ['online' (something how you determine which request should be created) => [ 'type' => 'GET', 'uri' => '/changeOnline', ... (parameters) ], ...]'
    Тогда вы можете сделать что-то вроде:
    IF isset($mapper[$something]) THEN (new Request())->setUri($mapper[$something]['uri']). На этом этапе у вас должен быть готов к работе класс Request и просто выполнить его. Сосредоточьтесь на изоляции и извлечении / абстрагировании повторяющихся элементов.

  2. Ваш класс Request должен либо хранить данные, либо выполнять работу. Прямо сейчас он выполняет вызовы curl и хранит данные о запросе. Это известно как принцип единой ответственности (SRP). Я бы сохранил данные и переместил часть выполнения в CustomClient::class Например. Там у вас также будет экземпляр клиента Guzzle, так что часть из основного файла также должна быть удалена. Запрос должен содержать только информацию, необходимую для этого, а другой класс должен выполнять фактический вызов, используя Request :: class в качестве объекта передачи данных, который будет содержать информацию об URI, заголовках и т. Д.

  3. Я бы переместил обработчики событий в другой класс или классы.

  4. Любые жестко запрограммированные строки, не являющиеся частью кода, следует переместить в константы с осмысленными именами.

public const REQUEST_ONLINE = 'online'; 
...
public const EVENT_RESUB = 'ResubEvent';
...
  1. Попытайся избежать elses насколько это возможно, они просто дают вам больше дел, могут быть головной болью для проверки, но это может быть конкретным случаем и не обязательно верно для вас. Например:
$pid = pcntl_fork();
if ($pid == -1) {
   die('could not fork');
}

$GLOBALS['streamer'] = ($streamers[$i]['streamer']);
cli_set_process_title($GLOBALS['streamer'] . 'run.php');
$request = RequestFactory::create($GLOBALS['streamer'], '1', 'status');

Заметьте, нет «еще». Вы можете заметить это еще одно важное событие, когда начнете писать модульные тесты.

  1. Используйте общие решения общих проблем

Вместо:
for ($i = 0; $i <= count($streamers) - 1; ++$i) {

Вы могли бы использовать for ($i = 0, $streamerIndex = 1; $i < count($streamers); $i++, $streamerIndex++) {

Это личное предпочтение, но когда вы используете такие общие шаблоны, как эти, другим разработчикам намного проще пройти через эту часть, не тратя много времени на изучение необработанного кода. В этой части также есть небольшая хитрость: присвоение ей $ streamerIndex может привести к тому, что люди пропустят эту часть, то есть увидят ...$i < count($streamers); $i++) Я мог бы прекратить читать прямо здесь и пропустить ту часть, где мы назначаем $ streamerIndex, так что вы тоже можете подумать об этом. 🙂

Или полностью убрать путаницу

for ($i = 0; $i < count($streamers); $i++) {
$streamerIndex = $i + 1;
...
  1. Я бы не советовал использовать суперглобальные переменные ($ GLOBAL) таким образом, чтобы они менялись. Это может привести к неожиданным проблемам, увеличению технического долга, некрасивости и т. Д.

  • 1

    Вы можете объяснить преимущества $streamerIndex? Я не слежу. Почему вы увеличиваете его только на первой итерации? Я думаю, что не соблюдаю код.

    — микмакуса

  • Мое плохое, это было для примера. $streamerIndex также следует увеличивать. Точка извлечения его в переменную состоит в том, чтобы не делать что-то вроде $data[$i +1] позже в коде.

    — Домагой

  • 1

    … вот что я подумал, но хотел проверить. Пожалуйста, отредактируйте, когда у вас будет время.

    — микмакуса


  • Так есть ли проблема с установкой типа запроса curl в запросе? А как насчет возврата результата? Я очень запутался, потому что мне сложно, когда применять SRP. Прямо сейчас это выглядит так: github.com/komen205/twitch-scraper/blob/main/Scraper/… Пытаюсь все переместить на Фабрику @Domagoj

    — вызов


  • Интересно, должны ли мои setHeader и setRequestType быть внутри Factory?

    — вызов

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *