Comments 20
Наверное неплохо было бы прогнать каких-нибудь фаззинг-тестов на этой функции.
кстати у команды vk нет интереса в сторону llvm?
mb_check_encoding в качестве переменной данных может принимать string|array|null (судя по php-интерфейсу). В C++ мы не можем записывать типы так, поэтому заменяем тип на mixed (смешанный).
Хм, но ведь это уже BC с оригинальным пыхом, т.к. та же самая рефлексия функции тогда вернёт не юнион, как в PHP, а mixed. Так?
Более того, если передать в эту функцию что-то отличное от описанных типов в PHP, то она вернёт TypeError, а в вашем случае, получается, молча проглотит?
Ну и с наследованием (слава щупальцам Ктулху, что его в данном примере нет и это просто функция) будут проблемы с LSP, так как если произвольный метод string|array|null
расширять, например, до string|\Stringable|array|null
будет ОК, то в случае сужения mixed
до указанной сигнатуры — будет фатал. Того же ArrayAccess я в репке не нашёл (https://github.com/VKCOM/kphp/tree/master/runtime/spl), поэтому я так понимаю, что алгебраические типы просто не поддерживаются kphp?
У KPHP действительно есть отличия от оригинальной пыхи. KPHP не поддерживает все то, что не дружит со статической типизацией и не может быть скомпилено. Например, не поддерживается рефлексия, вызовы функций по динамическому имени, всякие eval'ы и прочее.
Еще KPHP не разрешает вещи, которую ломают систему типов - например нельзя смешивать классы с примитвами, т.е. mixed не может быть классом.
Более подробно можно почитать об отличиях в доке.
В целом, все эти ограничения заставляют писать более понятный (как для людей так и для IDE), статически типизизруемый код, который чаще всего еще и более эффективен.
Алгебраические типы сейчас действительно не совсем честные, так как string|int
выводится как mixed
, и получается может содержать array
. Возможно в будущем мы с этим что-то сделаем, есть кое-какие идеи на этот счет.
Но зато в случае со строго типизированным кодом, компилятор все четко проверяет, и если попытаться например передать в mixed
объект класса, или в interface несовместимый класс, то получим ошибку на этапе компиляции, а не TypeError
в рантайме как в пхп.
Например, не поддерживается рефлексия, вызовы функций по динамическому имени, всякие eval'ы и прочее.
Ну, допустим, рефлексия как раз может быть скомпилена. Вон, тот же Unreal Engine по ней строит свои интерфейсы для DTO\VO, например.
Вызов функции по динамическому имени тоже вполне возможна. Достаточно иметь словарь имён и их адресов. Опять же в пример устройство работы Vulkan и OpenGL — там все методы выше версии 1.0 вызываются динамически (например: glXGetProcAddress
или wglGetProcAddress
) по имени этой функции.
Но да, это всё достигается за счёт макросов/темплейтов, это да, минус.
Еще KPHP не разрешает вещи, которую ломают систему типов — например нельзя смешивать классы с примитвами, т.е. mixed не может быть классом.
Не совсем понимаю. Указатель на Х — это ptrdiff_t, который фактически int64/int32, скалярная строка — тоже указатель int64/int32, float/double — тоже вмещается туда. Что мешает делать обычный union с передачей типа (ну т.е. из двух полей) для таких случаев? То что каждый скаляр придётся оборачивать в такой "враппер" с дополнительной инфой о типе? Ну, кажется, это не так уж и критично.
Но зато в случае со строго типизированным кодом, компилятор все четко проверяет, и если попытаться например передать в mixed объект класса, или в interface несовместимый класс, то получим ошибку на этапе компиляции, а не TypeError в рантайме как в пхп.
Любые ошибки с типами в современном PHP ловятся ещё до запуска ;) Для этого используются обычные статические анализаторы, вроде pslam (phpstan не рекомендую, т.к. он очень слаб в этом плане). Более того, какой-нибудь void в PHP во время компиляции кинет ошибку, что что-то не так, а не во время рантайма (но это исключение из правил скорее, согласен).
Дополнительно, хочу заметить, что наличие алгебраических типов данных не означает, что язык НЕ строго типизирован. PHP в этом плане вполне себе строг) Ну и например тот же Idris (https://www.idris-lang.org/) является строго и статически типизировнным и компилируется (транслируется) в конечном итоге в код на C.
"который фактически int64/int32"
Извините моего зануду, но нет, это не так. Из-за того, что это не так приходится вводить ключевое слово nullptr
Хм, немного не понял вашего замечания.
Возможно я ошибаюсь (ибо даже не близко С-разработчик), но представляю это следующим образом, поправьте где не прав, пожалуйста: Проблема кейворда int в том, что его размер явно зависит от модели данных (LP32 — 16, ILP32, LLP64, LP64 — 32), что является мегакостылём, чтоб программы написанные Одиссеем в Древней Греции, где указатели помечены интом — работали и на современных ОС, и на тех, что будут через 1000 лет и будут написаны на Клингонском. Так что в современных реалиях, используя тип int
(вместо int32_t
, например) — фактически разработчик говорит "сделай мне что-то числовое, с каким-нибудь рандомным размером от 16 до 64х бит".
А void*
— указатель, размер которого строго зависит от разрядности ОС и её адресного пространства (x86 — 32, x64 — 64, MS DOS — 16), что является синонимом как раз ptrdiff_t
.
Поэтому я и написал, что на современных ОС (включая и другие процессоры ARM/LVIW/RISV-V/MIPS/etc) ptrdiff_t соответствует или 32 битам, или 64.
Почему вы пишите про int, а потом пишите про void*? Они не связаны, совсем, никак.
Не стоит рассчитывать на выбор из двух (32 или 64), это может изменится очень стремительно, а код, который на это предположение заточен, будет изменить уже не так просто... Я уже сейчас могу представить KPHP, который крутится на Embox в msp430 или ещё где, где разрядность void* и не 32 и не 64.
Я даже могу представить проект и вовсе без ОС, но и там указатели конечно будут.
Почему вы пишите про int, а потом пишите про void*?
Потому что указатель — это ptrdiff_t
, который в свою очередь — int
неопределённого размера (например: https://github.com/torvalds/linux/blob/v6.5-rc3/include/uapi/asm-generic/posix_types.h#L70). На других ЦП это может быть long int
, может быть short int
, но всё равно в конечном итоге это int
.
Да и указатели в вашем Embox — это тоже int
: https://github.com/embox/embox/blob/d5ec971b90ad99f018bceadd620c3b4b434124d5/src/include/asm-generic/types32.h#L26
А то что размер int
в Embox по вашим словам не соответствует общепринятым LP32
/ILP32
/LLP64
/LP64
— это уже другая проблема. Судя тогда по исходникам, где явно написано typedef signed int __s32;
— это означает, что int32 там имеет разрядность не 32, что нарушает вообще всё, разве нет?
Потому что указатель — это
ptrdiff_t
Указатель - это указатель. ptrdiff_t
- это результат вычитания двух указателей, представленный в целочисленном виде. intptr_t
и uintptr_t
- это целочисленные представления void*
, при этом нет никаких гарантий, что они побайтово совпадают с внутренним представлением собственно void*
.
при этом нет никаких гарантий, что они побайтово совпадают с внутренним представлением собственно
void*
.
Хотя нет, тут я неправ. reinterpret_cast
для конверсий из указателя в intptr_t
и обратно гарантированно работает, значит, побайтово они обязаны совпадать.
Ну вот поэтому я и писал, что не сишник. Т.к. считал, что раз ptrdiff_t
служит для адресной арифметики, то гарантированно соответствует его размеру. А тут оказывается ещё intptr_t
есть, кто ж знал)
Блин, вы этим еще страдаете? зачем? в пхп 8 уже производительность на уровне. но вместо декомпозиции вашего монолита вы героически тянете свой движок пыхи. кстате, одна из причин почему я не хочу идти в вк - у вас много самописных движков, которые никому не интересны на рынке.
в пхп 8 уже производительность на уровне
На уровне семерки. Добавленные JITы показывают какие-то видимые улучшения разве что на синтетических тестах.
вместо декомпозиции вашего монолита вы героически тянете свой движок пыхи
"Декомпозиция монолита" сама по себе прироста производительности не дает, обычно происходит как раз наоборот.
Блин, вы этим еще страдаете? зачем? в пхп 8 уже производительность на уровне. но вместо декомпозиции вашего монолита вы героически тянете свой движок пыхи. кстате, одна из причин почему я не хочу идти в вк — у вас много самописных движков, которые никому не интересны на рынке.
Если пытаться как-то более вдумчиво критиковать, а не "вбрасывать на вентилятор", как это у вас получилось, то:
По-моему, проблема kphp не в том, что "VK его тянет", а в том, что для сообщества это не привносит ничего нового и полезного, в отличие от того же HipHop/HHVM в своё время, благодаря которому и часть ядра 7ки появилась, и синтаксис тайпхинтов, и синтаксис промоутед полей, и спецификация языка (https://phplang.org/), и куча другого всякого, что я не упомянул.
Помимо этого мешает отсутствие критичных компонентов ядра, что не позволяет использовать kphp в нормальном виде, т.к. отсутствие какой-нибудь рефлексии, например, это сразу минус все DI-контейнеры/гидраторы/сериалайзеры/мапперы и прочее.
Ну и последнее: В первую очередь, язык — это экосистема. Т.е. это наличие набора библиотек, инструментов и сообщества. И вот первое — это самое критичное, т.к. без композера даже не чихнуть нынче.
Учитывая всё это — я не понимаю, почему бы не реализовать, например:
1) Спецификацию для ограниченного подмножества PHP "для компиляции" и набор инструментов для тестирования кода, который бы соответствовал этому подмножеству.
2) Если это будет реализовано достаточно абстрактно, то что мешает отправить PR с автотестом пакетов в пакагист, где для таких пактов будет специальная галка "compat with specification X"?
3) Или можно добавить в PHP "стираемые типы" (не обязательно стираемые), которые бы помогли существующим в PHP стат.анализаторам, и в перспективе их можно было допиливать до "нормальных". Тогда бы никому не нужны были в KPHP эти аннотации типов, а сама грамматика была бы совместимой.
3.1) Более того — существующие аннотации типов KPHP не совместимы с PHP, так что если определить какой-нибудь@param callable(?int<1, max>, list<array{key:non-empty-string}>=):void $example
тайп-хинт, то kphp на нём помрёт.
4) Или можно было выкинуть нафиг весь фронтэнд KPHP, а тупо взять оригинальный PHP. А в качестве бекенда подсунуть какой-нибудь LLVM (как это сделал ircmaxell, например), который бы работал по AST/CFG/Опкодам. Получая на выходе, фактически, ровно тоже самое: Наполовину работающий функционал. Только при этом это было бы "легально", бекенд сам бы писал, что вызов "функции Х недоступен" или "получение Х доступно только в режиме интерпретации" (понятно, что этот последний пункт номер 4 из разряда "а вот если бы", а фактически довольно тяжкая инженерная задача).
… да ещё кучу всего можно придумать, чтобы позволило бы kphp быть полезным инструментом для сообщества и использовать ему существующие ресурсы, а не быть велосипедом для развлекухи а-ля JPHP, Phalanger -> peachpie, HipHop -> Hack/HHVM, etc.
А что ВК это монолит?
Как два байта переслать: контрибьютим в KPHP