Что такое KPHP
KPHP - компилятор для PHP. Он конвертирует PHP код в код на C++, компилируя который, ускоряет производительность в десятки раз. Это open-source проект, созданный ВКонтакте. Благодаря ему собирается огромный монолит ВКонтакте на 9 миллионов строк PHP кода в обычный бинарник, запуская который вы локально поднимаете полноценный ВКонтакте.
Цель
Я расскажу про добавление новых функций в runtime KPHP. Точнее про тернистую дорогу на пути.
План
Подготовка
-
runtime
добавление функций
типы
флаги
изменение подключаемых библиотек
-
Тесты
cpp тесты
php тесты
pull_request
Подготовка
Устанавливаем kphp из репозитория
runtime
Добавление функций
В качестве примера возьмем ситуацию, когда нам нужно реализовать функцию mb_check_encoding
из php. Первым делом идем в доки:
Узнаем, что функция проверяет кодировку строки или массива строк. Массив строк обрабатывается рекурсивно, так что сфокусируемся на функции, работающей для строки. Теперь идем в код php смотреть как работает функция в php:
Идем смотреть в сишный файл, чтобы протрейсить реализацию:
Видим, что конвертация идет через входной параметр типа mbfl_encoding
. Ищем:
Сразу видим, что эта структура - часть какой-то библиотеки libmbfl
, которая видимо и занимается конвертацией. Ищем ее в интернете и находим репозиторий:
Отлично, мы поняли, что нам нужно лишь использовать эту библиотеку, которая возьмет всю конвертацию на себя, а мы просто допишем интерфейс. Скачиваем эту библиотеку, устанавливаем, изучаем ее и пишем реализацию в любом сишном файлике (не внутри kphp, сейчас нам надо, чтобы просто работало). Для проверки кодировки нужна функция конвертации mb_convert_encoding
, которую мы тоже реализуем:
#include <libmbfl/mbfl/mbfilter.h>
mbfl_string *mb_convert_encoding(const char *str, const char *to, const char *from) {
int len = strlen(str);
enum mbfl_no_encoding from_encoding, to_encoding;
mbfl_buffer_converter *convd = NULL;
mbfl_string _string, result, *ret;
/* from internal to mbfl */
from_encoding = mbfl_name2no_encoding(from);
to_encoding = mbfl_name2no_encoding(to);
/* init buffer mbfl strings */
mbfl_string_init(&_string);
mbfl_string_init(&result);
_string.no_encoding = from_encoding;
_string.len = len;
_string.val = (unsigned char*)str;
/* converting */
convd = mbfl_buffer_converter_new(from_encoding, to_encoding, 0);
ret = mbfl_buffer_converter_feed_result(convd, &_string, &result);
mbfl_buffer_converter_delete(convd);
/* fix converting with multibyte encodings */
if (len % 2 != 0 && ret->len % 2 == 0 && len < ret->len) {
ret->len++;
ret->val[ret->len-1] = 63;
}
return ret;
}
bool mb_check_encoding(const char *value, const char *encoding) {
/* init buffer mbfl strins */
mbfl_string _string;
mbfl_string_init(&_string);
_string.val = (unsigned char*)value;
_string.len = strlen((char*)value);
/* from internal to mbfl */
const mbfl_encoding *enc = mbfl_name2encoding(encoding);
/* get all supported encodings */
const mbfl_encoding **encs = mbfl_get_supported_encodings();
int len = sizeof(**encs);
/* identify encoding of input string */
/* Warning! String can be represented in different encodings, so check needed */
const mbfl_encoding *i_enc = mbfl_identify_encoding2(&_string, encs, len, 1);
/* perform convering */
const char *i_enc_str = (const char*)mb_convert_encoding(value, i_enc->name, enc->name)->val;
const char *enc_str = (const char*)mb_convert_encoding(i_enc_str, enc->name, i_enc->name)->val;
/* check equality */
/* Warning! strcmp not working, because of different encodings */
bool res = true;
for (int i = 0; i < strlen(enc_str); i++)
if (enc_str[i] != value[i]) {
res = false;
break;
}
free((void*)i_enc_str);
free((void*)enc_str);
return res;
}
Функции работают, но только для сишных строк, теперь нам нужно перенести эти локально работающие функции в runtime kphp. Для этого есть 4 шага:
Добавить php-интерфейс (txt)
Добавить код с интерфейсом (h)
Добавить код с реализацией (cpp)
Добавить файлы в сборку (cmake)
Добавить php-интерфейс (txt)
Для того, чтобы правильно перенести php интерфейс, нужно знать про типы в kphp тут
Итак, открываем файл builtin-functions/_functions.txt
. И в самый конец добавляем переделанный интерфейс из php. Например в php mb_check_encoding имеет следующий интерфейс:
function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool
в kphp это будет:
function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool;
Аналогично для mb_convert_encoding в php:
function mb_convert_encoding(array|string $string, string $to_encoding, array|string|null $from_encoding = null): array|string|false
в kphp:
function mb_convert_encoding(array|string $string, string $to_encoding, array|string|null $from_encoding = null): array|string|false;
Вот и все, теперь нужно добавить код.
Добавить код с интерфейсом (h)
Все модули рантайма находятся в папке runtime
. Наши функции являются частью расширения mbstring
для php. Оказывается уже есть файлик mbstring.h
. Давайте откроем и посмотрим:
Оказывается mb_check_encoding уже реализована, странно. Почему имя функций начинается на f$
? Так kphp понимате какие функции искать в builtin-functions/_functions.txt
. Посмотрим cpp файл:
Хм, функция уже реализована, но все работает только для двух кодировок (UTF-8 и Windows-1251). Ничего страшного, теперь мы покажем им все кодировки! (о последствиях расскажу в конце). Видим, что входными параметрами являются переменные типа string
. Ловушка! Это не string из I/O! Это string из kphp! Где про нее почитать? Все типы включаются из runtime/kphp_core.h
. Смотрим внутри: . Поменяем интерфейс mb_check_encoding
:
bool f$mb_check_encoding(const string &value, const string &encoding);
аналогично для mb_convert_encoding
:
string f$mb_convert_encoding(const string &str, const string &to_encoding, const string &from_encoding);
Отлично, идем дальше.
Добавить код с реализацией (cpp)
В предыдущем шаге мы нашли все типы, так что можно посмотреть в string.inl
и узнать, как доставать из нее const char *
, который нам уже знаком или как создать string из const char *
. Теперь перепишем функцию mb_check_encoding
:
bool f$mb_check_encoding(const string &value, const string &encoding) {
const char *c_encoding = encoding.c_str();
const char *c_value = value.c_str();
// ...
}
Теперь осталось сделать аналогичное для функции mb_convert_encoding
:
string f$mb_convert_encoding(const string &str, const string &to_encoding, const string &from_encoding) {
const char *c_string = s.c_str();
const char *c_to_encoding = to_encoding.c_str();
const char *c_from_encoding = from_encoding.c_str();
// ...
return string((const char*)ret->val, ret->len);
}
Добавить файлы в сборку (cmake)
Идем в runtime/runtime.cmake
. Все cpp должны оказаться в KPHP_RUNTIME_SOURCES
. Но для удобства можно группировать их. В нашем случае mbstring.cpp
уже включен в сборку. Но, можно сделать по другому:
prepend(KPHP_RUNTIME_MBSTRING_SOURCES
mbstring.cpp)
тогда можно в KPHP_RUNTIME_SOURCES
включать не отдельный файл mbstring.cpp
, а KPHP_RUNTIME_MBSTRING_SOURCES
:
prepend(KPHP_RUNTIME_SOURCES ${BASE_DIR}/runtime/
${KPHP_RUNTIME_MBSTRING_SOURCES}
...
Фух, теперь точно все. Билдим!
mkdir build && cd build && cmake .. -DDOWNNLOAD_MISSING_LIBRARIES=On && make -j$(nproc)
Создаем test.php:
echo mb_check_encoding("Hello World", "UTF-8");
Запускаем!
./objs/bin/kphp2cpp -M test.php && ./kphp_out/cli
Ура! Мы успешно добавили новые функции в рантайм kphp!
!!! Важно !!!
Не все так просто. Мы очень быстро их добавили (лишь бы работало). Теперь когда мы убедились, что все работает нужно переделать некоторые моменты.
И было бы хорошим тоном разграничивать логичное поведение функции и ее поведение в php. Например в php
mb_convert_encoding
если при конвертации есть символы, которых нет в выходной кодировке, то их байты заменяются на 0x63 ('?' в ASCII). Это поведение уникально, поэтому лучше ее реализовать вf$
функции, а всю логику вынести в обычную функцию.Нужны другие типы -
mb_check_enсoding
в качестве параметра данных может принимать массив строк илиnull
, нужно это предусмотреть. Причем поведение функции должно быть идентично php (даже если поведение в php нелогично). Аналогично дляmb_convert_encoding
, которая может и принимать и возвращать строку или массив строк.mbstring
это расширение kphp, которое вphp-src
, собирается и подключается только если указан определенный флаг при компиляции. Нужно сделать и это.Обработка разных версий php.
Хороший тон
Используйте
const &
для входных параметров (если они не меняются)Разграничение логики и поведения как в php (используйте
static
):
static bool check_enсoding(const char *value, const char *encoding) {
// ...
}
bool f$mb_check_encoding(const string &value, const string &encoding) {
const char *c_encoding = encoding.c_str();
const char *c_value = value.c_str();
bool res = check_enсoding(c_value, c_encoding);
// process differences in php
return res
}
Используйте
noexcept
если функция не вызывает исключений:
static bool check_enсoding(const char *value, const char *encoding) {
// ...
}
bool f$mb_check_encoding(const string &value, const string &encoding) noexcept {
const char *c_encoding = encoding.c_str();
const char *c_value = value.c_str();
bool res = check_enсoding(c_value, c_encoding);
// process differences in php
return res
}
Типы
mb_check_encoding
в качестве переменной данных может принимать string|array|null
(судя по php-интерфейсу). В C++ мы не можем записывать типы так, поэтому заменяем тип на mixed
(смешанный). Про mixed
можно почитать аналогично типу string
. Параметр кодировки mb_check_encoding
(судя по php-интерфейсу) имеет тип ?string
, что нужно заменить на Optional<string>
. Про Optional
можно почитать аналогично типу string
. Главная информация в том, что mixed
мы можем проверять на любой тип через .is_<тип>
и попытаться привести к любому типу через .to_<тип>
. Из Optional
можно достать значение через .val()
.
static bool check_enсoding(const char *value, const char *encoding) {
// ...
}
bool f$mb_check_encoding(const mixed &value, const Optional<string> &encoding) noexcept {
if (encoding.is_null() || value.is_null()) return 1;
const char *c_encoding = encoding.val().c_str();
if (value.is_string()) { // ... }
if (value.is_array()) { // ... }
return 1;
}
Расширение
mbstring
это расширение php, его нужно включить через флаг при сборке с cmake
. Пусть флагом будет - MBFL
Чтобы это сделать нужно всего 3 шага:
cmake/external-libraries.cmake
- добавить флаг в cmake и пробросить его в компилятор:
option(DOWNLOAD_MISSING_LIBRARIES "download and build missing libraries if needed" OFF)
option(MBFL "build mbstring" OFF)
# ...
runtime/runtime.cmake
- включить файлы в сборку по флагу
# ...
if (MBFL)
prepend(KPHP_RUNTIME_MBSTRING_SOURCES
mbstring.cpp)
endif()
# ...
prepend(KPHP_RUNTIME_SOURCES ${BASE_DIR}/runtime/
${KPHP_RUNTIME_MBSTRING_SOURCES}
# ...
В моем случае некоторые функции из mbstring.cpp
использовались в самом kphp (не в рантайме). Это значит, что мне нужно всегда собирать mbstring.cpp
, но не собирать большинство функции по флагу. Чтобы это сделать нужно 3 шага:
cmake/external-libraries.cmake
- добавить флаг в cmake и пробросить его в компилятор:
option(DOWNLOAD_MISSING_LIBRARIES "download and build missing libraries if needed" OFF)
option(MBFL "build mbstring" OFF)
# ...
compiler/compiler-settings.cpp
- пробросить флаг дальше из компилятора в рантайм:
void CompilerSettings::init() {
// ...
std::stringstream ss;
#ifdef MBFL
ss << " -DMBFL ";
#endif
// ...
runtime/mbstring.*
- через#ifdef
отслеживать флаг из cmake:
#ifdef MBFL
bool f$mb_check_encoding(const mixed &value, const Optional<string> &encoding) noexcept;
#endif
Теперь при сборке можно указать:
cmake .. -DMBFL=On
Обработка разных версий php
В разработке...
Изменение подключаемых библиотек
Все работает. Библиотека линкуется. Но было бы хорошо, если не нужно было бы ее устанавливать самому, чтобы kphp по флагу MBFL
скачивал библиотеку, собирал и линковал. Тогда не придется писать новые доки для установки библиотеки + можно заморозить версию библиотеки на форке. Разберем мой форк libmbfl
. Мне не повезло с библиотекой, она с 1992 года не обновляется, нет документации, нет статей с ее использование, нет комментариев к коду и билдится она по-своему. Чтобы kphp сам скачивал, билдил и линковал библиотеку нужно выполнить шагов:
Убрать сборку 'по-своему'
Создать таргеты, которые ищет kphp
Включить сборку и линковку в
cmake
Добавить библиотеку в импорт компилятора
Убрать сборку 'по-своему'
Если в сборке библиотеку есть всякие ./configure
, ./buildconf
и прочее - переносим это в cmake
. Нам нужно, чтобы библиотека собиралась только через cmake
+ make
. В моем случае был и ./configure
и ./buildconf
, которые отвечали проверку зависимостей и сборку (каждый файл не собирался в своей папке, его собирали рядом с зависимостями). Я вручную починил все зависимости, чтобы каждый файл могу собираться по своему пути и добавил проверку зависимостей в cmake.
Создать таргеты, который ищет kphp
Когда kphp будет билдить библиотеку он будет ожидать специальные таргеты, чтобы знать какой код скопировать в include
и где находится собранная библиотека.
Название библиотеки libmbfl
, тогда kphp будет ожидать следующие таргеты:
libmbfl
libmbfl_ALL_SOURCES
libmbfl-includecopy
Разберем по отдельности каждый, начнем с libmbfl
.
libmbfl
project(libmbfl
VERSION 1.0.0
DESCRIPTION "libmbfl"
HOMEPAGE_URL "https://github.com/andreylzmw/libmbfl")
# ...
set_target_properties(libmbfl PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${OBJS_DIR})
# ...
set_target_properties(libmbfl PROPERTIES PUBLIC_HEADER "libmbfl/mbfilter.h")
# ...
add_dependencies(libmbfl libmbfl-includecopy)
# ...
install(TARGETS libmbfl
COMPONENT KPHP
LIBRARY DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}"
PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/kphp/liblibmbfl")
# ...
add_library(libmbfl STATIC ${libmbfl_ALL_SOURCES})
add_library(vk::libmbfl ALIAS libmbfl)
libmbfl_ALL_SOURCES
set(libmbfl_ALL_SOURCES
filters/mbfilter_iso2022_jp_ms.c
filters/mbfilter_iso8859_14.c
filters/mbfilter_iso8859_6.c
filters/mbfilter_sjis_mobile.c
filters/mbfilter_koi8r.c
filters/mbfilter_cp850.c
filters/mbfilter_euc_jp.c
# ...
add_library(libmbfl STATIC ${libmbfl_ALL_SOURCES})
# ...
libmbfl-includecopy
add_dependencies(libmbfl libmbfl-includecopy)
# ...
add_custom_command(
COMMAND cp -R
${CMAKE_CURRENT_SOURCE_DIR}/mbfl
${CMAKE_CURRENT_SOURCE_DIR}/include/kphp/libmbfl
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/include/kphp/libmbfl/mbfl)
add_custom_command(
COMMAND cp -R
${CMAKE_CURRENT_SOURCE_DIR}/filters
${CMAKE_CURRENT_SOURCE_DIR}/include/kphp/libmbfl
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/include/kphp/libmbfl/filters)
add_custom_command(
COMMAND cp -R
${CMAKE_CURRENT_SOURCE_DIR}/nls
${CMAKE_CURRENT_SOURCE_DIR}/include/kphp/libmbfl
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/include/kphp/libmbfl/nls)
# ...
add_custom_target(libmbfl-includecopy ALL DEPENDS
include/kphp/libmbfl/mbfl/mbfilter.h)
# ...
Включить сборку и линковку в cmake. cmake/external-libraries.cmake:
# ...
FetchContent_Declare(libmbfl GIT_REPOSITORY https://github.com/andreylzmw/libmbfl)
FetchContent_MakeAvailable(libmbfl)
include_directories(${libmbfl_SOURCE_DIR}/include)
add_definitions(-DLIBMBFL_LIB_DIR="${libmbfl_SOURCE_DIR}/objs")
add_link_options(-L${libmbfl_SOURCE_DIR}/objs)
# ...
Добавить библиотеку в импорт компилятора
Чтобы kphp мог линковать нужную нам библиотеку libmbfl
нужно добавить ее (без lib
в начале) в external_static_libs
в compiler/compiler-settings.cpp
:
std::vector<vk::string_view> external_static_libs{..., "mbfl"};
Тесты
cpp тесты
cpp тесты это тесты реализации, запускаются через сtest. В них мы заранее должны знать что должна вернуть функция и просто проверять. Добавить их просто. Для каждой функции отдельный тест. cpp тесты находятся в tests/cpp/runtime/
. Чтобы их добавить создаем mbstring-test.cpp
внутри tests/cpp/runtime/
:
#include <gtest/gtest.h>
#include "runtime/mbstring/mbstring.h"
// Note: all tests written for php8.3
/* TEST ASCII DATA */
const string &ASCII_STRING = string("sdf234");
const array<string> &ASCII_ARRAY = array<string>::create(string("234"), string("wef"));
const string &ASCII_ENCODING = string("ASCII");
/* TEST WINDOWS1251 DATA */
const string &WINDOWS1251_STRING = string("sdfw3234ыва");
const array<string> &WINDOWS1251_ARRAY = array<string>::create(string("234"), string("wef"), string("ыва"));
const string &WINDOWS1251_ENCODING = string("Windows-1251");
/* TEST UTF8 DATA */
const string &UTF8_STRING = string("Өd5па");
const array<string> &UTF8_ARRAY = array<string>::create(string("sdыа"), string("wef"), string("ыва"), string("Ө"), string("Ä°nanç Esasları"));
const string &UTF8_ENCODING = string("UTF-8");
#ifdef MBFL
TEST(mbstring_test, test_mb_check_encoding) {
/* TEST ASCII ENCODING */
ASSERT_TRUE(f$mb_check_encoding(ASCII_STRING, ASCII_ENCODING));
ASSERT_TRUE(f$mb_check_encoding(ASCII_ARRAY, ASCII_ENCODING));
ASSERT_FALSE(f$mb_check_encoding(WINDOWS1251_STRING, ASCII_ENCODING));
ASSERT_FALSE(f$mb_check_encoding(WINDOWS1251_ARRAY, ASCII_ENCODING));
ASSERT_FALSE(f$mb_check_encoding(UTF8_STRING, ASCII_ENCODING));
ASSERT_FALSE(f$mb_check_encoding(UTF8_ARRAY, ASCII_ENCODING));
/* TEST WINDOWS1251 ENCODING */
ASSERT_TRUE(f$mb_check_encoding(WINDOWS1251_STRING, WINDOWS1251_ENCODING));
ASSERT_TRUE(f$mb_check_encoding(WINDOWS1251_ARRAY, WINDOWS1251_ENCODING));
ASSERT_TRUE(f$mb_check_encoding(ASCII_STRING, WINDOWS1251_ENCODING));
ASSERT_TRUE(f$mb_check_encoding(ASCII_ARRAY, WINDOWS1251_ENCODING));
ASSERT_TRUE(f$mb_check_encoding(UTF8_STRING, WINDOWS1251_ENCODING));
ASSERT_TRUE(f$mb_check_encoding(UTF8_ARRAY, WINDOWS1251_ENCODING));
/* TEST UTF8 ENCODING */
ASSERT_TRUE(f$mb_check_encoding(UTF8_STRING, UTF8_ENCODING));
ASSERT_TRUE(f$mb_check_encoding(UTF8_ARRAY, UTF8_ENCODING));
ASSERT_TRUE(f$mb_check_encoding(ASCII_STRING, UTF8_ENCODING));
ASSERT_TRUE(f$mb_check_encoding(ASCII_ARRAY, UTF8_ENCODING));
ASSERT_TRUE(f$mb_check_encoding(WINDOWS1251_STRING, UTF8_ENCODING));
ASSERT_TRUE(f$mb_check_encoding(WINDOWS1251_ARRAY, UTF8_ENCODING));
}
TEST(mbstring_test, test_mb_convert_encoding) {
/* input is string, encoding is string */
ASSERT_STREQ(f$mb_convert_encoding(ASCII_STRING, WINDOWS1251_ENCODING, ASCII_ENCODING).to_string().c_str(), ASCII_STRING.c_str());
ASSERT_STREQ(f$mb_convert_encoding(UTF8_STRING, UTF8_ENCODING, UTF8_ENCODING).to_string().c_str(), UTF8_STRING.c_str());
/* input is string, encoding is array of strings */
ASSERT_STREQ(f$mb_convert_encoding(ASCII_STRING, {WINDOWS1251_ENCODING, UTF8_ENCODING}, ASCII_ENCODING).to_string().c_str(), {ASCII_STRING.c_str(), UTF8_STRING.c_str()});
}
#endif
Сборка тестов находится в tests/cpp/runtime/runtime-tests.cmake
, нужно добавить cpp файл в RUNTIME_TESTS_SOURCES
:
prepend(RUNTIME_TESTS_SOURCES ${BASE_DIR}/tests/cpp/runtime/
# ...
mbstring-test.cpp
# ...
Теперь когда мы запустим ctest:
cd build && ctest -j$(nproc)
Мы увидим:
...
242/261 Test #242: mbstring_test.test_mb_check_encoding ....................................... Passed 0.24 sec
243/261 Test #243: mbstring_test.test_mb_convert_encoding ..................................... Passed 0.23 sec
...
php тесты
php тесты это тесты, которые сравнивают результат работы php и kphp, запускаются через tests/kphp_tester.py
. В них мы пишем php код который должен вести себя ожидаемого в kphp. php тесты находятся в tests/phpt/
. Чтобы их добавить создаем папку mbstring
внутри tests/phpt/
. Внутри создаем отдельный файл для каждой функции в формате 001_*****.php
, 002_*****.php
и тд, где *****
- названия функций. В нашем случае:tests/phpt/mbstring/001_mb_convert_encoding.php
:
@ok
<?php
function mb_convert_encoding_not_utf_8() {
$str = "Somebody was told: F$#$)UT#*HD]";
var_dump(mb_convert_encoding($str, "UTF-7", "EUC-JP"));
}
# If mbstring.language is "Japanese", "auto" is expanded to "ASCII,JIS,UTF-8,EUC-JP,SJIS"
function test_mb_convert_encoding_auto() {
$str = "Somebody was told: F$#$)UT#*HD]";
var_dump(mb_convert_encoding($str, "EUC-JP", "auto"));
}
mb_convert_encoding_not_utf_8();
test_mb_convert_encoding_auto();
tests/phpt/mbstring/002_mb_check_encoding.php
:
@ok
<?php
function mb_check_encoding_utf_8() {
$str = "The leading horse is white, the second horse is red, the third one is black, the last one is green";
var_dump(mb_check_encoding($str, "UTF-8"));
}
function mb_check_encoding_not_utf_8() {
$str = "The leading horse is white, the second horse is red, the third one is black, the last one is green";
var_dump(mb_check_encoding($str, "base64"));
}
mb_check_encoding_utf_8();
mb_check_encoding_not_utf_8();
На первой строчке мы указываем ожидаемое поведение kphp для этого теста, например:
@ok
, если kphp должен успешно скомпилировать этот код@kphp_should_fail
, если kphp не должен скомпилировать этот код и это ожидаемо@kphp_should_warn
, если kphp не должен скомпилировать этот код и это ожидаемо + должен вывести ворнинг
Запустим тесты:./kphp_tester.py phpt/mbstring/001_mb_convert_encoding.php
:
./kphp_tester.py phpt/mbstring/002_mb_check_encoding.php
:
pull_request
Мы добавили новые функции, учли все варианты поведения, добавили тесты. Теперь нужно грамотно оформить pull_request. Не поверите, но есть одно-единственное правило, на котором я погорел. Ломать не строить - нужно понимать, что целевым проектом для kphp является vk. vk это монолит из более 9 миллионов строк кода на php. Если вы заменяете какие-то функции своими, которые работают правильно (как я заменил все функции mbstring своими), то разработчикам vk нужно будет переписывать места использования этих функций, что им не нужно, так как vk работает и без ваших обновленных функций, поэтому лучшим вариантом является добавление новых (уже существующих) функций по флагам. Таким образом выигрывают все. Разработки vk не переписывают код и другие пользователи kphp могут получить ваш функционал для своих проектов.
Что касается остального правил особо нет - пишите, что вы добавили. Можете сделать ревью проще, прикрепив ссылки на документацию, откуда вы взяли функцию, которую реализовали. Если есть какие-то нюансы (по типу того, что вы используете свою модифицированную библиотеку), тоже просто пишите об этом.
Комментарии (4)
domix32
23.07.2023 12:57Наверное неплохо было бы прогнать каких-нибудь фаззинг-тестов на этой функции.
kovalensky
Деликатный вопрос: а можно сделать статью, чтобы скомпилировать исполняемый файл из скрипта на платформе Windows?
И возможно ли это?
SerafimArts
del (не так понял вопрос)