Pull to refresh
44
0
Несмеянов Кирилл @SerafimArts

Руководитель Отдела Сокрытия Раскрытых Рептилоидов

Send message

<душнила_mode_on>
KDE кроссплатформенный, так что формально можно тоже самое реализовать и в винде.


А даже если бы и не был кроссплатформенным, то wsl позволяет пробросить иксы наружу, где со стороны клиента (винды) достаточно установить что-то вроде VcXsrv, чтобы использовать абсолютно любое линуксовое приложение (включая плазму) одной командой в консоли.
</душнила_mode_on>

Возможно вы не поняли. Предметная область любой игры не ограничивается персистентным состоянием позиции игрока. Это всего лишь одна ~десятая всех задач.

Я вот по должности своей как раз разрабатываю один известный MMO шутер-лутер. И http (и grpc) для идемпотентных и стейтлесс запросов вполне себе занимает бОльшую часть всей предметной области, а то о чём вы говорите - лишь сессионная составляющая самого режима игры, которая частично может быть рассчитана и на клиенте в т.ч. в отличие от какой-нибудь операции "покупки" товара у "торговца".

Так что списывать технологию со счетов просто "потому что" не вижу смысла.

Медленный для чего? Ну вот, например, дефолтный ответ "самого медленного фреймворка в мире" (ака Laravel) внутри докера на винде - 8-10мс в режиме дебага. Этих 10мс не хватит чтоб получить информацию о профиле игрока? Получить список предметов? Получить информацию о точках спауна? Где именно эти 10мс сыграют значительную роль в нагрузке, когда один селект из базы может и в два, и в десять раз дольше работать?

Да вполне нормальный протокол для RPC. Клацаешь по кнопочке, с сервера по хттп грузанулись настройки персонажа, например и отобразилось окошко с ним Причем всё готовое уже есть: Сервер, лоадбалансер, дебаггеры, клиенты. Ну и логи потом дебажить приятнее, нежели какой-нибудь grpc

Ещё раз повторю, что далеко не в любом. А если и можно, то сменой одной буковки можно всё поломать.

Видимо вы пропустили тред про го и руби чуть выше.

Вопрос был, цитата:


А в каких-то распространённых языках есть строгое требование к виду имён переменных?

Я ответил: Да, в каких-то языках есть строгое требование к виду имён переменных. Даже привёл пример современных языков, где такие ограничения есть, т.к. в зависимости от того как оно выглядит — меняет свою семантику и поведение.

Go допускает и camelCase, и PascalCase, и snake_case.

И вы хотите сказать, что вообще ничего не изменится, если все ExampleVariable заменить на exampleVariable? А вот судя по этой ссылочке на туториал, кажется, это совсем не так: https://go.dev/tour/basics/3


Про руби вы тоже ответили: Язык явно диктует как именовать переменные/константы и от этого самого именования полностью зависит поведение языка.

Ну как минимум в Go и Ruby. Только там не то что бы "компилятор упадёт"… Скажем так, он может упасть при определённых обстоятельствах, т.к. от именования зависит вообще всё.

Качество очень хорошее, но таких игр 1.5 штуки

Steam:
Результатов по вашему запросу: 7,069

Не, понятно что в большинстве случаев это всё фигня, но как бы есть Half-Life 2, есть фантазмофобия, есть фаллаут, сабнавтика, но мэн скай, хитман 3 и прочее. Да, это всё порты, а не эксклюзивы, но тоже вполне себе прикольно играется. Тот же HL2 я прошёл с удовольствием.


Кстати, если уж говорить про стим, то не совсем понятно как Apple будет совместим с ним. Поддерживает ли он OpenVR стандарт? Как подключить контроллеры? Ну и прочее… А то что-то мне кажется, что в текущем виде это не конкурентный продукт. Тем более за цену в 4 раза превышающую всё тоже самое, что уже есть.


Я пока не могу понять зачем нужны вообще эти очки и какую задачу они будут решать? В голову приходит только просто отговорки на тему "чтобы были" + "экосистема эппл". А никаких объективных причин тратить четверть миллиона рублей на то, что будет пылиться на полке — не могу придумать.

Не знаю как в Go, но в JS async/await не решит этот придуманный сценарий, так как доходя до await выполнение "приостанавливается" и дальше ждет, что вернет вызов.

Это не так. await лишь делегирует задачу "наверх", т.е. это другая форма записи yield. Фактически альтернатива выглядела бы так:


// JS
let response = await request('...');

// PHP
yield from $process = request('...');
$response = $process->getReturn();

// PHP (или так, если верхний обработчик 
// умеет сам разворачивать вложенные
// генераторы и возвращать результат через ->send(...))
$response = yield request('...');

Что фактически может быть в PHP упрощено в сторону:


// PHP
$response = request('...');

// где внутренности request
while (!feof($stream)) {
    $body .= fread($stream, 1024);
    Fiber::getCurrent() && Fiber::suspend();
}

Ну можно вместо встроенного select просто проверить на eof и попытаться считать, если делать влоб.


С другой стороны в пыхе можно доустановить libev, libevent, ev или libuv, а там уже на выбор, хоть poll, хоть epoll, хоть kqueue, хоть devpoll, хоть evport, хоть ещё что. Накрайняк есть ffi и руками можно допилить нужное. Было бы желание.

Как объясняется на RisingStack, главная причина в его неблокирующемся вводе-выводе и эффективной обработке параллельных подключений

Осталось понять откуда в PHP внезапно проблемы с блокирующим io, когда обычная функция stream_set_blocking превращает любой io в неблокирующий. Хоть чтение с файловой системы, хоть сокеты, хоть что угодно...

При этом наличие файберов в PHP делает не то что ненужным аналог в JS из async/await, но и вообще ставит под сомнение их существование в природе, т.к. JS-ный аналог становится просто неудобным.

Мне кажется, подобное актуально, когда в языке нет строгой типизации (в первую очередь) и нет конструкций, обеспечивающих удобное взаимодествие с нуллами.


Например в Java, где любой объект может быть как инстансом, так и null, и язык на уровне типов не обеспечивает гарантии того, что объект является корректным инстансом. Поэтому, в Java намного практичнее было бы использовать подобные конструкции, т.к. они предоставляют безопасный инструмент для работы с nullable объектами.


Если говорить про PHP, то этот язык при работе с nullable-объектами более строгий и на уровне типов гарантирует то, что объект не является null. Поэтому и смысла меньше.


// class Example

public Some obj = null; // Java: OK

public Some $obj = null; // PHP: TypeError

Во-вторую очередь, операторы ?->, ?? и проч. позволяют удобно работать с nullable-объектами, поэтому смысла в подобных монадах становится ещё меньше. Например optional:


optional($objectOrNull)->safeMethodCall(); // null | method_result

// vs.

$objectOrNull?->safeMethodCall(); // null | method_result

В чём принципиальное отличие от?


$userData = $userStub;

if ($maybeUser !== null) {
  $smthBeforeConvert($maybeUser);
  $entityToApi($maybeUser);
  $smthAfterConvert($maybeUser);

  $userData = $maybeUser;
}

Не, не так уж и долго: гифка


Секунд 5 сборка занимает.


А если вместе с пересборкой всего PHP, то да, долго.

Можно собрать и статик, для этого прописывается --with-embed=static, например:


./configure --with-embed=static --disable-all
make

В результате вместо so/dll собирается a файл.


Потом этот файл линкуется (плюс 4 обязательных зависимости в ядре):


target_link_libraries(target_name PRIVATE path/to/result/libs/libphp.a
    m # математика
    dl # это для функций подключения либ
    resolv # это не помню, как и утилс
    util
)

Если требуется какая-то доп.зависимость, то это просто расширяется, например:


sudo apt install libffi-dev
./configure --with-embed=static --disable-all --with-ffi
make

target_link_libraries(target_name PRIVATE
    # список обязательных либ
    ffi
)

Или так:


sudo apt install libsodium-dev
./configure --with-embed=static --disable-all --with-sodium
make

target_link_libraries(target_name PRIVATE
    # ...
    sodium
)

Но у этого есть и обратная сторона, если собрать что-то более менее стандартное (например с расширениями:, Core, date, libxml, pcre, sqlite3, ctype, curl, dom, FFI, fileinfo, filter, hash, iconv, json, mbstring, standard, SPL, PDO, pdo_sqlite, Phar, posix, random, Reflection, shmop, SimpleXML, sockets, tokenizer, xml, xmlreader, xmlwriter), то в результате такой бинарь будет весить 45 мегабайт.


P.S. Причём стоит учитывать, что это вполне официальный способ, а не костыль:


Заголовок спойлера

image

В общем, дошли руки до того, чтобы поиграться с этой штукой. Сделал репку с примером: https://github.com/SerafimArts/PHP-Embedded/tree/master


В принципе всё очень просто:


Сами исходники PHP складываются, например, в vendor/php-8.2.6, затем требуется их собрать:


  • ./buildconf (генерация скрипта конфигурации)
  • ./configure --with-embed (конфигурация бинаря)
  • make -j "$(nproc)" (сборка)

После make появляется libs/libphp.so, который и будет использоваться.


Сам код с примером может быть таким (он просто подключает при запуске файл entrypoint.php):


Заголовок спойлера
#include <sapi/embed/php_embed.h>

int main(int argc, char **argv)
{
    PHP_EMBED_START_BLOCK(argc, argv)

    zend_file_handle file_handle;
    zend_stream_init_filename(&file_handle, "entrypoint.php");
    php_execute_script(&file_handle);

    PHP_EMBED_END_BLOCK()
}

И CMake файлу для сборки, вроде такого:


Заголовок спойлера
cmake_minimum_required(VERSION 3.16)

project(phpe C)

if (NOT CMAKE_C_STANDARD)
    set(CMAKE_C_STANDARD 99)
endif ()

set(PHP_VERSION 8.2.6)
set(PHP_SRC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/vendor/php-${PHP_VERSION}")

include_directories(
        "${PHP_SRC_PATH}"
        "${PHP_SRC_PATH}/main"
        "${PHP_SRC_PATH}/Zend"
        "${PHP_SRC_PATH}/TSRM"
        "${PHP_SRC_PATH}/ext"
        "${PHP_SRC_PATH}/ext/date/lib"
        "${PHP_SRC_PATH}/sapi")

configure_file(src/entrypoint.php entrypoint.php COPYONLY)

add_executable(phpe src/main.c)

target_link_libraries(phpe "${PHP_SRC_PATH}/libs/libphp.so")

После этого собираем и компилируем: cmake + cmake --build

Скорее уж лучше написать:


$user = $userRepository->getUserByEmail($email);

return new JsonResponse([
    'name' => $user?->getName() ?? 'Unknown',
    'email' => $user?->getName() ?? 'Unavailable',
]);

Как минимум читаемее будет

Да мало ли что там приводится? Это тривиальная логика: Нейминг "Service" является мусорным и не несёт никакого смысла. Зачем оно нужно? Удалить его и ничего не изменится вообще.


Репозиторий — тоже является сервисом предоставления данных из внешнего источника. Энтити (если она анемичная и тонкая, как у вас в примерах) — тоже является сервисом репрезентации записи в БД. И т.д. Почему всё тогда не складывать в директорию "Service", если это тоже сервисы?


И да, директория (в т.ч. неймспейс) должен соответствовать не задаче, а области ответственности. Что изменится, если вместо примитивной структуры "для новичков":


App\
    Entity\
        Order.php
    Service\
        OrderCheckoutService.php

Будет более адекватная и очевидная?


App\
    Domain\
        Order\
            Order.php
            CheckoutProcess.php

Если считаете что это "не удобно", то:


1) Куда будете складывать ошибки, возникшие в процессе оформления заказа? Про второй случай всё очевидно, в заказах (с вариациями) Order\***\NotEnoughMoneyException. А в первом?


2) А где будет валидатор, который проверяет состояние заказа?


3) А как написать архитектурные тесты (например через https://github.com/carlosas/phpat), которые будут проверять, что "заказ" не имеет доступа и не обращается ни к кому, кроме соседних "сервисов" в том же неймспейсе?

Директория Services является вырожденной (Так же как и Entity), т.к. не соответствует никакой задаче. Это плохой пример и явно не соответствует понятию "чистая архитектура", но допустимо в рамках небольшого ПО.


Можно взять себе за правило, что любой кейс, который содержит кейворды Service, Util, Manager, Support, etc почти всегда (как и в вашем случае) является некорректным с точки зрения нейминга и построения структуры ПО.

Information

Rating
4,322-nd
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity