Я всегда боролся за создание устойчивого, организованного и чистого кода. Я попытался использовать метод 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 ответ
Идея названия: Как провести рефакторинг простого кода API-клиента в PHP CLI
Вот пара советов:
Я бы добавил
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 и просто выполнить его. Сосредоточьтесь на изоляции и извлечении / абстрагировании повторяющихся элементов.Ваш класс Request должен либо хранить данные, либо выполнять работу. Прямо сейчас он выполняет вызовы curl и хранит данные о запросе. Это известно как принцип единой ответственности (SRP). Я бы сохранил данные и переместил часть выполнения в
CustomClient::class
Например. Там у вас также будет экземпляр клиента Guzzle, так что часть из основного файла также должна быть удалена. Запрос должен содержать только информацию, необходимую для этого, а другой класс должен выполнять фактический вызов, используя Request :: class в качестве объекта передачи данных, который будет содержать информацию об URI, заголовках и т. Д.Я бы переместил обработчики событий в другой класс или классы.
Любые жестко запрограммированные строки, не являющиеся частью кода, следует переместить в константы с осмысленными именами.
public const REQUEST_ONLINE = 'online';
...
public const EVENT_RESUB = 'ResubEvent';
...
- Попытайся избежать
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');
Заметьте, нет «еще». Вы можете заметить это еще одно важное событие, когда начнете писать модульные тесты.
- Используйте общие решения общих проблем
Вместо:
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;
...
- Я бы не советовал использовать суперглобальные переменные ($ GLOBAL) таким образом, чтобы они менялись. Это может привести к неожиданным проблемам, увеличению технического долга, некрасивости и т. Д.
Вы можете объяснить преимущества
$streamerIndex
? Я не слежу. Почему вы увеличиваете его только на первой итерации? Я думаю, что не соблюдаю код.— микмакуса
Мое плохое, это было для примера.
$streamerIndex
также следует увеличивать. Точка извлечения его в переменную состоит в том, чтобы не делать что-то вроде$data[$i +1]
позже в коде.— Домагой
… вот что я подумал, но хотел проверить. Пожалуйста, отредактируйте, когда у вас будет время.
— микмакуса
Так есть ли проблема с установкой типа запроса curl в запросе? А как насчет возврата результата? Я очень запутался, потому что мне сложно, когда применять SRP. Прямо сейчас это выглядит так: github.com/komen205/twitch-scraper/blob/main/Scraper/… Пытаюсь все переместить на Фабрику @Domagoj
— вызов
Интересно, должны ли мои setHeader и setRequestType быть внутри Factory?
— вызов