Как расширить возможности runtime KPHP

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Всем доброго дня, уважаемые читатели. В данной статье вы узнаете как добавить новые функции в runtime KPHP.

Совсем вкратце расскажу о том, что такое KPHP и на примере какой задачи вы узнаете о расширении возможностей runtime KPHP.

О KPHP

KPHP - компилируемый PHP. Де-факто PHP, транслированный в C++. В свою очередь это увеличивает производительность исходного кода как минимум потому что скомпилировано в бинарный файл.

О нашей задаче

Задача, которую мы решим, заключается в следующем - реализовать две функции для парсинга строк и файлов в формате ENV. Исключительно для демонстрации всех этапов добавления новых функций в runtime.

Приступаем

Итак, перейдём к нашему плану:

  1. Подготовим всё необходимое

  2. Добавим новые функции

  3. Напишем тесты

  4. Проверим работоспособность

Подготовим всё необходимое

Я работаю под Ubuntu 20.04. И для начала нам нужно установить следующее (в том случае, если у вас их нет):

  • git

  • make, cmake

  • g++,

  • python3,

  • pip3

  • php7.4

После установки вышеупомянутых пакетов, необходимо установить vk`шные. А перед тем, надо добавить репозитории:

sudo apt-get update

sudo apt-get install -y --no-install-recommends apt-utils ca-certificates gnupg wget

sudo wget -qO - https://repo.vkpartner.ru/GPG-KEY.pub | sudo apt-key add -

echo "deb https://repo.vkpartner.ru/kphp-focal/ focal main" >> /etc/apt/sources.list

И уже затем установить пакеты:

sudo apt-get update

sudo apt install git cmake make g++ gperf python3-minimal python3-jsonschema \
            curl-kphp-vk libuber-h3-dev kphp-timelib libfmt-dev libgtest-dev libgmock-dev libre2-dev libpcre3-dev \
            libzstd-dev libyaml-cpp-dev libnghttp2-dev zlib1g-dev php7.4-dev libmysqlclient-dev libnuma-dev

Проделав это, переходим к сборке KPHP из исходников:

# Клонируем репозиторий
git clone https://github.com/VKCOM/kphp.git

# Заходим в папку репозитория
cd kphp

# Переключаемся на ветку
git checkout -b 'pmswga/env_parsing'

# Создаём папку build
mkdir build

# Заходим в папку build
cd build

# Просим cmake по CMakeLists.txt сотоврить нам чудо
cmake ..

# Также просим make сотворить чудо 
make -j6 all

Сборка должна пройти успешно. Что мы получили в итоге? В корне репозитория мы получим новую папку objs и содержимое при ней:

kphp/
├─ build/                      <-- Если вы ещё не вышли из этой папки, то вы тут :)
├─ objs/
│  ├─ bin/
│  │  ├─ kphp2cpp              <-- Наш kphp компилятор. Остальное нас не интересует :(
│  │  ├─ tl2php
│  │  ├─ tl-compiler
│  ├─ flex/
│  │  ├─ libvk-flex-data.o
│  │  ├─ libvk-flex-data.so
│  ├─ generated/*
│  ├─ vkext/
│  │  ├─ modules/
│  │  │  ├─ vkext.so
│  │  ├─ modules7.4/
│  │  │  ├─ vkext.so

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

Добавим новые функции

Кратко обрисую алгоритм добавления новых функций, в такой схеме:

kphp/
├─ builin-functions/_functions.txt  1) Добавить интерфейс функции сюда
├─ runtime/
│  ├─ *.h                           2) Добавить h-файлы с объявлением функций 
│  ├─ *.cpp                         3) Добавить cpp-файлы с реализацией функций
│  ├─ runtime.cmake                 4) Добавить имена cpp-файлов в переменную KPHP_RUNTIME_SOURCES

После чего можно смело запускать make и убедиться, что всё добавлено без ошибок и собирается.

Теперь на нашем конкретном примере:

  1. В файле _functions.txt добавим интерфейсы функций parse_env_file и parse_env_string. Обратите внимание, на то как указываются типы. В целом всё ясно. Принимают строки, возвращают массивы строк.

function parse_env_file($filename ::: string) ::: string[];
function parse_env_string($env_string ::: string) ::: string[];
  1. Добавляем parsing_functions.h со следующим содержимым:

#pragma once

#include "runtime/kphp_core.h"
#include <regex>
#include <fstream>
#include <sstream>

/*
 * Cool functions. А именно функции для очистки строк от ненужного
 */

string clearSpecSymbols(const string &str);

string clearSpaces(const string &str);

string clearEOL(const string &str);

string clearQuotes(const string &str);

string clearString(const string &str);

string trim(const string &str);

/*
 * The best funtions. 
 * А именно функции, которые проверяют строки по регулярным выражениям 
 * и функции, которые возвращают части одной ENV-записи
 */

bool isEnvComment(const string &env_comment);

bool isEnvVar(const string &env_var);

bool isEnvVal(const string &env_val);

string get_env_var(const string &env_entry);

string get_env_val(const string &env_entry);

/*
 * Env file|string parsing functions. 
 * А именно функции будут подставляться в сгенерированном коде
 */

array<string> f$parse_env_file(const string &filename);

array<string> f$parse_env_string(const string &env_string);
  1. Добавляем в parsing_functions.cpp следующий код:

#include "parsing_functions.h"

string clearSpecSymbols(const string &str)
{
  return string(
    std::regex_replace(str.c_str(), std::regex(R"([\t\r\b\v])"), "").c_str()
  );
}

string clearSpaces(const string &str)
{
  return string(
    std::regex_replace(str.c_str(), std::regex(" += +"), "=").c_str()
  );
}

string clearEOL(const string &str)
{
  return string(
    std::regex_replace(str.c_str(), std::regex("\\n"), " ").c_str()
  );
}

string clearQuotes(const string &str)
{
  return string(
    std::regex_replace(str.c_str(), std::regex("[\"\']"), "").c_str()
  );
}

string clearString(const string &str)
{
  string clear_string = clearSpecSymbols(str);
  clear_string = clearSpaces(clear_string);
  clear_string = clearQuotes(clear_string);
  clear_string = trim(clear_string);

  return clear_string;
}

string trim(const string &str)
{
  if (str.empty()) {
    return {};
  }

  size_t s = 0;
  size_t e = str.size()-1;

  while (s != e && std::isspace(str[s])) {
    s++;
  }

  while (e != s && std::isspace(str[e])) {
    e--;
  }

  return str.substr(s, (e-s)+1);
}

/* Example: #APP_NAME=Laravel */
bool isEnvComment(const string &env_comment)
{
  return std::regex_match(
    env_comment.c_str(), 
    std::regex("^#.*", std::regex::ECMAScript)
  );
}

/* Example: APP_NAME */
bool isEnvVar(const string &env_var)
{
  return std::regex_match(
    env_var.c_str(), 
    std::regex("^[A-Z]+[A-Z\\W\\d_]*$", std::regex::ECMAScript)
  );
}

/* Example: Laravel */
bool isEnvVal(const string &env_val)
{
  return std::regex_match(
    env_val.c_str(), 
    std::regex("(.*\n(?=[A-Z])|.*$)", std::regex::ECMAScript)
  );
}

/* Example: APP_NAME=Laravel -> APP_NAME */
string get_env_var(const string &env_entry)
{
  string::size_type pos = env_entry.find_first_of(string("="), 0);

  if (pos == string::npos) {
    return {};
  }

  return env_entry.substr(0, pos);
}

/* Example: APP_NAME=Laravel -> Laravel */
string get_env_val(const string &env_entry)
{
  string::size_type pos = env_entry.find_first_of(string("="), 0);

  if (pos == string::npos) {
    return {};
  }

  pos++;
  
  return env_entry.substr(pos, env_entry.size() - pos);
}

/*
 * Вот собственно реализация parse_env_file
 */
array<string> f$parse_env_file(const string &filename)
{
  if (filename.empty()) {
    return {};
  }

  std::ifstream ifs(filename.c_str());

  if (!ifs.is_open()) {
    php_warning("File not open");
    return {};
  }

  array<string> res(array_size(1, 0, true));

  std::string env_entry;
  while (getline(ifs, env_entry)) {
    string env_entry_copy = clearString(string(env_entry.c_str()));

    if (!env_entry_copy.empty() && !isEnvComment(env_entry_copy)) {
      string env_var = get_env_var(env_entry_copy);

      if (env_var.empty()) {
        php_warning("Invalid env string format %s", env_entry_copy.c_str());
        return {};
      }

      string env_val = get_env_val(env_entry_copy);

      if (isEnvVar(env_var) && isEnvVal(env_val)) {
        res.set_value(env_var, env_val);
      } else {
        php_warning("Invalid env string format %s", env_entry_copy.c_str());
        return {};
      }
    }
  }

  ifs.close();

  return res;
}

/*
 * Вот собственно реализация parse_env_string
 */
array<string> f$parse_env_string(const string &env_string)
{
  if (env_string.empty()) {
    return {};
  }

  array<string> res(array_size(0, 0, true));

  string env_string_copy = clearString(env_string);
  env_string_copy = clearEOL(env_string_copy);

  std::stringstream ss(env_string_copy.c_str());
  std::string str;

  while (getline(ss, str, ' ')) {
    string env_entry = string(str.c_str());

    if (!isEnvComment(env_entry)) {
      string env_var = get_env_var(env_entry);

      if (env_var.empty()) {
        php_warning("Invalid env string format %s", env_entry.c_str());
        return {};
      }

      string env_val = get_env_val(env_entry);

      if (isEnvVar(env_var) && isEnvVal(env_val)) {
        res.set_value(env_var, env_val);
      } else {
        php_warning("Invalid env string format %s", env_entry.c_str());
        return {};
      }
    }
  }

  return res;
}

Как видите, для того чтобы функции реально работали в runtime их нужно называть с префиксом f$ в начале. Ибо именно они будут подставляться в сгенерированном коде (позже сами увидите). В остальном, плодите кода столько, сколько хотите :)

Поговорим о двух важных вещах - это array<string> и string. Это реализация массивов и строк в самом runtime KPHP, а не std`шная (Сам бы Александр Степанов дал бы по рукам за такие методы как set_value и другие).

array<string> позволяет нам делать ассоциативные и обычные массивы.

string позволяет привести себя в int, float, bool, string.

  1. И последнее, добавляем в наш parsing_functions.cpp в cmake файл:

# тут ещё немного cmake 

prepend(KPHP_RUNTIME_SOURCES ${BASE_DIR}/runtime/
        ${KPHP_RUNTIME_DATETIME_SOURCES}
        ${KPHP_RUNTIME_MEMORY_RESOURCE_SOURCES}
        ${KPHP_RUNTIME_MSGPACK_SOURCES}
        ${KPHP_RUNTIME_JOB_WORKERS_SOURCES}
        ${KPHP_RUNTIME_SPL_SOURCES}
        ${KPHP_RUNTIME_PDO_SOURCES}
        ${KPHP_RUNTIME_PDO_MYSQL_SOURCES}
        allocator.cpp
        array_functions.cpp
        bcmath.cpp
        common_template_instantiations.cpp
        confdata-functions.cpp
        confdata-global-manager.cpp
        confdata-keys.cpp
        critical_section.cpp
        curl.cpp
        exception.cpp
        files.cpp
        from-json-processor.cpp
        instance-cache.cpp
        instance-copy-processor.cpp
        inter-process-mutex.cpp
        interface.cpp
        json-functions.cpp
        json-writer.cpp
        kphp-backtrace.cpp
        mail.cpp
        math_functions.cpp
        mbstring.cpp
        memcache.cpp
        memory_usage.cpp
        migration_php8.cpp
        misc.cpp
        mixed.cpp
        mysql.cpp
        net_events.cpp
        on_kphp_warning_callback.cpp
        openssl.cpp
        parsing_functions.cpp                            <-- Наш файл
        php_assert.cpp
        profiler.cpp
        regexp.cpp
        resumable.cpp
        rpc.cpp
        serialize-functions.cpp
        storage.cpp
        streams.cpp
        string.cpp
        string_buffer.cpp
        string_cache.cpp
        string_functions.cpp
        tl/rpc_tl_query.cpp
        tl/rpc_response.cpp
        tl/rpc_server.cpp
        typed_rpc.cpp
        uber-h3.cpp
        udp.cpp
        url.cpp
        vkext.cpp
        vkext_stats.cpp
        ffi.cpp
        zlib.cpp
        zstd.cpp)

# и тут ещё немного cmake 

Ура! Можно компилировать и проверять работоспособность.

Напишем тесты

Однако, никто же не поверит, что у вас всё работает, если вы не напишите для этого тесты... И никто всерьёз вас не воспримет, когда вы без тестов отправите pull-request. Поэтому приступим.

Что надо знать о тестах?

kphp/
├─ tests/
│  ├─ cpp/                        <---- Здесь cpp тесты 
│  │  ├─ compiler                 <---- Тесты компилятора
│  │  ├─ runtime                  <---- Тесты runtime
│  │  │   ├─ *.cpp                1) Добавляем cpp файлы тестов
│  │  │   ├─ runtime-tests.cmake  2) Добавлем имена cpp файлов в переменную RUNTIME_TESTS_SOURCES
│  │  ├─ server                   <---- Тесты сервера
│  ├─ phpt/                       <---- Здесь php тесты
│  │  ├─ my_folder_with_tests     3) Создаём свою папку для тестов
|  |  |   ├─ 001_*.php            4) Создаём свои *.php тесты с нумерацией
|  ├─ kphp_tester.py              5) Запустить ранее написанные тесты с помощью этого скрипта

CPP тесты написаны с помощью gtest и являются обычными unit-тестами.

Однако, php тесты работают следующим образом. Пишется код на php, в том числе с функциями, которые есть только kphp. Затем они запускаются с помощью kphp_tester.py и выполняются как обычный php код, так и kphp. Затем их результаты сравниваются и делается вывод, тест пройден или нет.

Вопрос вот в чём, откуда обычный php узнает о той же функции parse_env_string и parse_env_file, если их в принципе нет? Для этого нужны php-polyfills (в своём роде заглушки). Далее всё увидите сами.

Для запуска cpp тестов:

# Перейдём в папку build
cd build

# Соберём всё ещё раз
make -j6 all

# Запустим тесты
ctest -j6

В результате все тесты должны выполниться успешно.

Для запуска php тестов нужно проделать следующее:

# Сначала скачать php-polyfiils
git clone https://github.com/VKCOM/kphp-polyfills.git

# Зайдём в папку kphp-polyfiils
cd kphp-polyfills

# Установим пакеты и сгенерируем autoload 
composer install

# Зададим переменную окружения KPHP_TESTS_POLYFIILS_REPO 
export KPHP_TESTS_POLYFILLS_REPO=$(pwd)

Такая многоходовочка даёт нам следующее, что при запуске php тестов, они будут обращаться по этому пути подтягивать "заглушки".

И вот теперь, действительно, для запуска php тестов:

# Запуск всех тестов
tests/kphp_tester.py

# Запуск конкретного теста
tests/kphp_tester.py 001_*.php

Вуаля!

CPP тесты

Теперь к нашим барашкам (к f$parse_env_string и f$parse_env_file). Добавим parsing-functions-tests.cpp со следующим кодом:

#include <gtest/gtest.h>
#include "runtime/parsing_functions.h"

TEST(parsing_functions_test, test_isEnvComment) {
  ASSERT_FALSE(isEnvComment(string("")));
  ASSERT_FALSE(isEnvComment(string("APP_NAME=Laravel")));
  ASSERT_TRUE(isEnvComment(string("#APP_NAME=Laravel")));
}

TEST(parsing_functions_test, test_isEnvVar) {
  ASSERT_FALSE(isEnvVar(string("")));
  ASSERT_FALSE(isEnvVar(string("!APP_NAME")));
  ASSERT_TRUE(isEnvVar(string("APP_NAME")));
}

TEST(parsing_functions_test, test_isEnvVal) {
  ASSERT_TRUE(isEnvVal(string("")));
  ASSERT_TRUE(isEnvVal(string("true")));
  ASSERT_TRUE(isEnvVal(string("local")));
  ASSERT_TRUE(isEnvVal(string("80")));
  ASSERT_TRUE(isEnvVal(string("127.0.0.1")));
  ASSERT_TRUE(isEnvVal(string("https://localhost")));
  ASSERT_TRUE(isEnvVal(string("\'This is my env val\'")));
  ASSERT_TRUE(isEnvVal(string("\"This is my env val\"")));
}

TEST(parsing_functions_test, test_get_env_var) {
  string str("APP_NAME=Laravel");
  string env_var = get_env_var(str);

  ASSERT_STREQ(string("").c_str(), get_env_var(string("")).c_str());
  ASSERT_STREQ("APP_NAME", env_var.c_str());
  ASSERT_EQ(strlen("APP_NAME"), env_var.size());
}

TEST(parsing_functions_test, test_get_env_val) {
  string str("APP_NAME=Laravel");
  string env_val = get_env_val(str);

  ASSERT_STREQ("Laravel", env_val.c_str());
  ASSERT_EQ(string("Laravel").size(), env_val.size());
}

/*
 * Тестируем функцию parse_env_string
 */
TEST(parsing_functions_test, test_parse_env_string) {
  string env_string = string(R"(APP_NAME=Laravel APP_ENV=local #APP_KEY=base64:mtlb8hldh5hZ0GlLzbhInsV531MSylspRI4JsmwVal8= APP_DEBUG=true T1="my" T2='my')");

  array<string> res(array_size(0, 0, true));

  res = f$parse_env_string(env_string);
  ASSERT_EQ(res.size().string_size, 5);

  ASSERT_TRUE(res.has_key(string("APP_NAME")));
  ASSERT_STREQ(res.get_value(string("APP_NAME")).c_str(), string("Laravel").c_str());
  ASSERT_TRUE(res.has_key(string("APP_ENV")));
  ASSERT_STREQ(res.get_value(string("APP_ENV")).c_str(), string("local").c_str());
  ASSERT_TRUE(res.has_key(string("APP_DEBUG")));
  ASSERT_STREQ(res.get_value(string("APP_DEBUG")).c_str(), string("true").c_str());
  ASSERT_TRUE(res.has_key(string("T1")));
  ASSERT_STREQ(res.get_value(string("T1")).c_str(), string("my").c_str());
  ASSERT_TRUE(res.has_key(string("T2")));
  ASSERT_STREQ(res.get_value(string("T2")).c_str(), string("my").c_str());
}

/*
 * Тестируем функцию parse_env_file
 */
TEST(parsing_functions_test, test_parse_env_file) {
  std::ofstream of(".env.example");

  if (of.is_open()) {
    of << "APP_NAME=Laravel "<< std::endl;
    of << "APP_ENV=local" << std::endl;
    of << "APP_DEBUG=true" << std::endl;
    of.close();
  }

  array<string> res(array_size(0, 0, true));

  res = f$parse_env_file(string("file not found"));
  ASSERT_EQ(res.size().string_size, 0);

  res = f$parse_env_file(string(".env.example"));
  ASSERT_TRUE(res.has_key(string("APP_NAME")));
  ASSERT_STREQ(res.get_value(string("APP_NAME")).c_str(), string("Laravel").c_str());
  ASSERT_TRUE(res.has_key(string("APP_ENV")));
  ASSERT_STREQ(res.get_value(string("APP_ENV")).c_str(), string("local").c_str());
  ASSERT_TRUE(res.has_key(string("APP_DEBUG")));
  ASSERT_STREQ(res.get_value(string("APP_DEBUG")).c_str(), string("true").c_str());
}

Теперь их можно запустить и убедиться, что они успешно выполняются.

Результаты cpp тестов
Результаты cpp тестов

PHP тесты

Вспоминаем про php-polyfills, идём в соседний репозиторий, в корне которого находим файл kphp_polyfiils.php добавляем в него следующий код:

#ifndef KPHP

# тут много какого-то кода

#region env parsing

/**
 * parse_env_string return associative array by parsed string
 * 
 */
function parse_env_string(string $env_string) {
  if (empty($env_string)) {
    return [];
  }

  $get_env_entry = function ($env_string) {
    $env_entry = explode('=', $env_string, 2);

    if (count($env_entry) !== 2) {
      die("parse error\n");
    }

    return [
      'env_var' => trim($env_entry[0]),
      'env_val' => trim($env_entry[1])
    ];
  };

  $lines = explode(' ', $env_string);
  $env = [];


  foreach ($lines as $line) {
    $env_entry = $get_env_entry($line);

    $env[trim($env_entry['env_var'])] = trim($env_entry['env_val']); 
  }

  return $env;
}

/**
 * parse_env_string return associative array by parsed file
 * 
 */
function parse_env_file(string $filename) {

  if (empty($filename)) {
    return [];
  }

  if (!is_file($filename)) {
    return [];
  }

  if (!file_exists($filename)) {
    return [];
  }

  $env_string = file_get_contents($filename);

  return parse_env_string($env_string);
}

#endregion

#endif

По существу мы реализовали парсинга env строк и файлов в формате ENV. Что собственно и можно было сделать изначально, создав даже целую либу (kenv).

Теперь же создадим по пути tests/phpt/parsing/001_parsing_env.php и добавим в него следующий код.

@ok                                              # <-- Тег обозначает должен ли этот код компилироваться на KPHP
<?php

require_once 'kphp_tester_include.php';          # <-- Подключаем php-polyfills

function test_parse_env_string_empty() {         # <-- Сами "тесты"
    var_dump(parse_env_string(''));
}

function test_parse_env_string_one() {
    var_dump(parse_env_string('APP_NAME=Laravel'));
}

function test_parse_env_string_many() {
    var_dump(parse_env_string('APP_NAME=Laravel APP_DEBUG=true APP_ENV=local'));
}

function test_parse_env_file_empty() {
    var_dump(parse_env_file(''));
}

function test_parse_env_file_not_found_empty() {
    var_dump(parse_env_file('file not found'));
}

function test_parse_env_file_one() {
    $filename = tempnam("", "wt");
    $fp = fopen($filename, "a");
    fwrite($fp, "APP_NAME=Laravel");
    fclose($fp);

    var_dump(parse_env_file($filename));
}

function test_parse_env_file_many() {
    $filename = tempnam("", "wt");
    $fp = fopen($filename, "a");
    fwrite($fp, "APP_NAME=Laravel");
    fwrite($fp, "APP_DEBUG=true");
    fwrite($fp, "APP_ENV=local");
    fclose($fp);

    var_dump(parse_env_file($filename));
}

test_parse_env_string_empty();                   # <-- Вызов функций
test_parse_env_string_one();
test_parse_env_string_many();

test_parse_env_file_empty();
test_parse_env_file_not_found_empty();
test_parse_env_file_one();
test_parse_env_file_many();

Запустим написанный тест:

tests/kphp_tester.py 001_parse_env

И вот, заветное слово passed.

Результаты php тестов
Результаты php тестов

Проверяем работоспособность

Итого, вот какие изменения мы внесли, чтобы реализовать функции parse_env_file и parse_env_string.

# Репозиторий kphp
kphp/
├─ builin-functions/_functions.txt       <-- Добавили интерфейсы parse_env_file и parse_env_string
├─ runtime/
│  ├─ parsing_functions.h                <-- Добавили объявление функций 
│  ├─ parsing_functions.cpp              <-- Добавили реализацию функций
│  ├─ runtime.cmake                      <-- Добавили parsing_functions.cpp в переменную KPHP_RUNTIME_SOURCES
├─ tests/
│  ├─ cpp/
│  │  ├─ runtime
│  │  │   ├─ parsing-functions-tests.cpp <-- Добавили cpp тесты
│  │  │   ├─ runtime-tests.cmake         <-- Добавили parsing-functions-tests.cpp в переменную RUNTIME_TESTS_SOURCES
│  ├─ phpt/
│  │  ├─ parsing                         <-- Создали папку parsing
|  |  |   ├─ 001_parse_env.php           <-- Добавили php тесты

# Репозиторий kphp-polyfills
kphp-polyfills/
├─ kphp_polyfills.php                    <-- Добавили php`шные реализации parse_env_file и parse_env_string

Теперь можем посмотреть на наши плоды. Создадим index.php и напишем следующий код:

<?php
    $env_string = "APP_NAME=Laravel APP_DEBUG=true APP_ENV=local";

    $res = parse_env_string($env_string);

    print_r('<pre>');
    print_r($res);
    print_r('</pre>');

    $res = parse_env_file('.env.example');

    print_r('<pre>');
    print_r($res);
    print_r('</pre>');

Скомпилируем его:

./kphp2cpp index.php

Запустим и получим следующее:

./kphp_out/server -H 8000 -f 2
                   ^       ^
                   |       |
                   |     Поднимаем двух рабочих работу работать
                   |
                 Поднимаем localhost:8000
Результат работы скомпилированного index.php
Результат работы скомпилированного index.php

А вот что сгенерировано на С++:

//crc64:912a10e8beed9098
//crc64_with_comments:bc9f187534f26ce2
#include "runtime-headers.h"
#include "o_6/src_indexbccbb8a09559268e.h"
extern string v$env_string;

extern array< string > v$res;

extern bool v$src_indexbccbb8a09559268e$called;

extern string v$const_string$us3e8066aa5eeccc54;

extern string v$const_string$us531c70314bd2d991;

extern string v$const_string$usd04f12c090cf2e22;

extern string v$const_string$use301963cf43e4d3a;

//source = [index.php]
//3:     $env_string = "APP_NAME=Laravel APP_DEBUG=true APP_ENV=local";
Optional < bool > f$src_indexbccbb8a09559268e() noexcept  {
  v$src_indexbccbb8a09559268e$called = true;
  v$env_string = v$const_string$us3e8066aa5eeccc54;
//4: 
//5:     $res = parse_env_string($env_string);
  v$res = f$parse_env_string(v$env_string);                       <-- Вот вызов нашей функции 
//6: 
//7: print_r('<pre>');
  f$print_r(v$const_string$usd04f12c090cf2e22);
//8:     print_r($res);
  f$print_r(v$res);
//9: print_r('</pre>');
  f$print_r(v$const_string$us531c70314bd2d991);
//10: 
//11: 
//12:     $res = parse_env_file('.env.example');
  v$res = f$parse_env_file(v$const_string$use301963cf43e4d3a);    <-- И вот вызов нашей функции  
//13: 
//14: print_r('<pre>');
  f$print_r(v$const_string$usd04f12c090cf2e22);
//15:     print_r($res);
  f$print_r(v$res);
//16: 
  f$print_r(v$const_string$us531c70314bd2d991);
  return Optional<bool>{};
}

Заключение

Спасибо всем кто дочитал до конца. Надеюсь что поставленная цель выполнена.

Если вы хотите присоединиться к сообществу KPHP, то добро пожаловать в чат.

По изложенной теме:

  • Сборка из исходников

  • runtime kphp

  • contributing to kphp

  • Написание тестов

Дополнительные ссылки:

  • Репозиторий vk.com/kphp

  • Документация по KPHP

  • Другие проекты VK

Источник: https://habr.com/ru/post/701216/


Интересные статьи

Интересные статьи

Данный текст не является руководством по внедрению двухфакторной аутентификации в Linux на основе токенов / смарт-карт. Подобные (и весьма подробные) руководства вы сможете найти как на сайтах произво...
Пандемия выступила как мощный стимул для ускоренной цифровизации всех отраслей экономики. Массовый переход на удалёнку, спешное развёртывание облачной инфраструктуры и другие авральные действия привел...
Прошлой осенью мы выпустили новую версию системы КОМПАС-3D Home для любительского 3D-моделирования и домашнего использования. Мейкерам, 3D-печатникам, домашним мастерам и блогерам доступны все возможн...
Любая компания рано или поздно сталкивается с вопросом обработки большого объема входящей документации. В первую очередь это может быть, например, бухгалтерия со своими первичными документами: счетами...
У всех есть любимые книжки про магию. У кого-то это Толкин, у кого-то — Пратчетт, у кого-то, как у меня, Макс Фрай. Сегодня я расскажу вам о моей любимой IT-магии — о BPF и соврем...