Расскажу, как нам удалось написать линтер, который получился достаточно быстрым, чтобы проверять изменения во время каждого git push и делать это за 5?10 секунд при кодовой базе в 5 миллионов строк на PHP. Мы назвали его NoVerify.

NoVerify поддерживает базовые вещи вроде перехода к определению и поиску использований и умеет работать в режиме Language Server. В первую очередь наш инструмент ориентирован на поиск потенциальных ошибок, но умеет проверять и стилистику. Сегодня его исходные коды появились в open-source на GitHub. Ищите ссылку в конце статьи.

Зачем нам свой линтер


В середине 2018 года мы решили, что пора внедрять линтер для PHP-кода. Цели было две: уменьшить количество ошибок, которые видят пользователи, и строже следить за соблюдением code style. Основной упор при этом мы сделали на предотвращение типичных ошибок: наличия в коде необъявленных и неиспользуемых переменных, недостижимого кода и других. Также хотелось, чтобы статический анализатор работал максимально быстро на нашей кодовой базе (5?6 миллионов строк кода на PHP на момент написания статьи).

Как вы, вероятно, знаете, исходный код большей части сайта написан на PHP и компилируется с помощью KPHP, поэтому логично было бы добавить эти проверки в компилятор. Но на самом деле не весь код имеет смысл выполнять через KPHP — например, компилятор слабо совместим со сторонними библиотеками, так что для некоторых частей сайта до сих пор используется обычный PHP. Они тоже важны и должны проверяться линтером, поэтому, к сожалению, нет возможности встроить его в KPHP.

Почему NoVerify


Учитывая объем PHP-кода (напомню, это 5?6 миллионов строк), не представляется возможным «исправить» его сразу, чтобы он проходил наши же проверки в линтере. Тем не менее хочется, чтобы меняющийся код постепенно становился чище и строже следовал стандартам кодирования, а также содержал меньше ошибок. Поэтому мы решили, что линтер должен уметь проверять изменения, которые разработчик собирается запушить, и не ругаться на остальное.

Чтобы это сделать, линтеру нужно проиндексировать весь проект, полностью проанализировать файлы до изменений и после и посчитать разницу между сгенерированными предупреждениями. Новые предупреждения показываются разработчику, и мы требуем их исправить перед тем, как можно будет сделать push.

Но бывают ситуации, когда такое поведение нежелательно, и тогда разработчики могут выполнить push без локальных хуков — с помощью команды git push --no-verify. Опция --no-verify и дала название линтеру :)

Какие были альтернативы


Кодовая база в VK мало использует ООП и в основном состоит из функций и классов со статическими методами. Если классы в PHP поддерживают autoload, то функции — нет. Поэтому мы не можем без существенных модификаций использовать статические анализаторы, которые основывают свою работу на том, что autoload загрузит весь недостающий код. К таким линтерам относится, например, psalm от компании Vimeo.

Мы рассмотрели следующие инструменты статического анализа:

  • PHPStan — однопоточный, требует autoload, анализ кодовой базы дошел до 30% за полчаса;
  • Phan — даже в quick-режиме с 20 процессами анализ застопорился на 5% через 20 минут;
  • Psalm — требует autoload, анализ занял 10 минут (всё равно хотелось бы намного быстрее);
  • PHPCS — проверяет стиль, но не логику;
  • phpcf — проверяет только форматирование.

Как можно догадаться из заголовка статьи, ни один из этих инструментов не удовлетворяет нашим требованиям, поэтому мы написали свой.

Как создавался прототип


Сначала мы решили построить небольшой прототип, чтобы понять, стоит ли вообще пытаться сделать полноценный линтер. Поскольку одно из важных требований к линтеру — это скорость его работы, вместо PHP мы выбрали Go. «Быстро» — это давать фидбэк разработчику как можно оперативнее, желательно не более чем за 10?20 секунд. В противном случае цикл «поправить код, прогнать линтер ещё раз» начинает существенно замедлять разработку и портить настроение людям :)

Поскольку для прототипа выбран Go, нужен парсер PHP. Таких существует несколько, но наиболее зрелым нам показался проект php-parser. Этот парсер не идеален и всё ещё дорабатывается, но для наших целей он вполне подошёл.

Для прототипа было решено попробовать реализовать одну из самых простых, на первый взгляд, инспекций: обращение к неопределённой переменной.

Основная идея для реализации такой инспекции выглядит просто: для каждого ветвления (например, для if) создаём отдельную вложенную область видимости и объединяем типы переменных на выходе из неё. Пример:

<?php
if (rand()) {
    $a = 42;
    // область видимости: { $a: int }
} else {
    $b = "test";
    $a = "another_test";
    // область видимости: { $b: string, $a: string }
}
// итоговая область видимости: { $b: string?, $a: int|string }
echo $a, $b;
// здесь мы должны вывести сообщение о том,
// что переменная $b не всегда определена

Выглядит просто, не так ли? В случае обычных условных операторов всё работает хорошо. Но мы должны обрабатывать, например, switch без break;

<?php
switch (rand()) {
    case 1:
        $a = 1; // { $a: int }
    case 2:
        $b = 2; // { $a: int, $b: int }
    default:
        $c = 3; // { $a: int, $b: int, $c: int }
}
// { $a: int?, $b: int?, $c: int }

По коду не сразу понятно, что $c будет на самом деле определена всегда. Конкретно этот пример — выдуманный, но он хорошо иллюстрирует, какие бывают сложные моменты для линтера (и для человека в данном случае тоже).

Рассмотрим более сложный пример:

<?php
exec("hostname", $out, $retval);
echo $out, $retval;
// { $out: ???, $retval: ??? }

Не зная сигнатуру функции exec, нельзя сказать, будут ли определены $out и $retval. Сигнатуры встроенных функций можно взять из репозитория github.com/JetBrains/phpstorm-stubs. Но те же проблемы будут при вызове пользовательских функций, а их сигнатуру можно узнать, только проиндексировав весь проект. Функция exec принимает второй и третий аргументы по ссылке, а значит переменные $out и $retval могут быть определены. Здесь обращение к этим переменным — не обязательно ошибка, и линтер не должен ругаться на такой код.

Аналогичные проблемы с неявной передачей по ссылке возникают с методами, но заодно добавляется необходимость выводить типы переменных:

<?php
if (rand()) {
    $a = some_func();
} else {
    $a = other_func();
}
$a->some_method($b);
echo $b;

Мы должны знать, какие типы возвращают функции some_func() и other_func(), чтобы потом найти метод под названием some_method в этих классах. Только тогда сможем сказать, будет переменная $b определена или нет. Ситуацию осложняет то, что зачастую у простых функций и методов нет phpdoc-аннотаций, поэтому нужно ещё уметь вычислять типы функций и методов, исходя из их реализации.

При разработке прототипа пришлось реализовать примерно половину всей функциональности, чтобы простейшая инспекция заработала как надо.

Работа в качестве language server


Чтобы было легче отлаживать логику линтера и проще видеть предупреждения, которые он выдаёт, мы решили добавить режим работы в качестве language server для PHP. В режиме интеграции с Visual Studio Code это выглядит примерно так:



В таком режиме удобно тестировать гипотезы и проверять сложные случаи (после этого надо написать тесты, конечно). Также хорошо тестировать производительность: даже на больших файлах php-parser на Go показывает неплохую скорость работы.

Поддержка language server далека от идеала, поскольку её основное предназначение — отладка правил линтера. Тем не менее в этом режиме есть несколько дополнительных возможностей:

  1. Подсказки для имён переменных, констант, функций, свойств и методов.
  2. Подсветка выведенных типов переменных.
  3. Переход к определению.
  4. Поиск использований.

«Ленивый» вывод типов


В режиме language server требуется, чтобы работало следующее: вы меняете код в одном файле, и потом, когда переключаетесь на другой, должны работать с уже обновлённой информацией о том, какие типы возвращаются в функциях или методах. Представьте, что файлы редактируются в такой последовательности:

<?php
// Файл A.php, версия 1
class A {
    /** @var int */
    public $prop;
}
// Файл B.php, не меняется
class B {
    public static function something() {
        $obj = new A;
        return $obj->prop;
    }
}
// Файл C.php, не меняется
$c = B::something();
// $c имеет тип int
// Файл A.php, версия 2
class A {
    /** @var string   <--- теперь тут string */
    public $prop;
}
// Файл C.php, не меняется
$c = B::something();
// $c это теперь string, хотя ни B.php, ни C.php не менялись

Учитывая, что мы не заставляем разработчиков всегда писать PHPDoc (особенно в таких простых случаях), нужен способ хранить информацию о том, какой тип возвращает функция B::something(). Чтобы, когда изменится файл A.php, в файле C.php информация о типах сразу была актуальной.

Одно из возможных решений — хранить «ленивые типы». Например, тип возвращаемого значения у метода B::something() на самом деле представляет собой тип выражения (new A)->prop. В таком виде линтер и хранит информацию о типе, и благодаря этому можно закешировать всю метаинформацию по каждому файлу и обновлять её, только когда этот файл изменится. Делать это нужно осторожно, чтобы нигде случайно не просочилась слишком конкретная информация о типах. Ещё необходимо изменять версию кеша, когда меняется логика вывода типов. Тем не менее такой кеш ускоряет фазу индексации (о которой расскажу позже) в 5?10 раз по сравнению с повторным парсингом всех файлов.

Две фазы работы: индексация и анализ


Как мы помним, даже для простейшего анализа кода требуется информация как минимум обо всех функциях и методах в проекте. Это означает, что нельзя проанализировать только один файл отдельно от проекта. И ещё — что это невозможно сделать за один проход: например, PHP позволяет обращаться к функциям, которые объявлены дальше в файле.

Из-за этих ограничений работа линтера состоит из двух фаз: первичная индексация и последующий анализ только нужных файлов. Теперь подробнее об этих двух фазах.

Фаза индексации


В этой фазе парсятся все файлы и делается локальный анализ кода методов и функций, а также кода на верхнем уровне (например, для определения типов глобальных переменных). Собирается информация об объявленных глобальных переменных, константах, функциях, классах и их методах и записывается в кеш. Для каждого файла в проекте кеш представляет собой отдельный файл на диске.

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

*Кроме режима работы в качестве language server, когда на каждую правку проводится индексация и анализ измененного файла.

Фаза анализа


В этой фазе мы можем пользоваться метаинформацией (о функциях, классах…) и уже непосредственно анализировать код. Вот список того, что по умолчанию умеет проверять NoVerify:

  • недостижимый код;
  • обращение к объектам, как к массиву;
  • недостаточное количество аргументов при вызове функции;
  • вызов неопределенного метода/функции;
  • доступ к отсутствующему свойству класса/константе;
  • отсутствие класса;
  • неверный PHPDoc;
  • обращение к неопределённой переменной;
  • обращение к переменной, которая не всегда определена;
  • отсутствие «break;» после case в конструкциях switch/case;
  • ошибка синтаксиса;
  • неиспользуемая переменная.

Список довольно короткий, но можно добавлять проверки, специфичные для вашего проекта.

В процессе эксплуатации линтера оказалось, что самая полезная инспекция как раз последняя (неиспользуемая переменная). Такое часто бывает, когда вы рефакторили код (или писали новый) и опечатались в названии переменной: этот код является валидным с точки зрения PHP, но ошибочным по логике.

Скорость работы


Сколько времени проверяются изменения, которые мы хотим запушить? Всё зависит от количества файлов. С NoVerify процесс может занимать до минуты (так было, когда я изменил 1400 файлов в репозитории), но если правок было немного, то обычно все проверки проходят за 4?5 секунд. За это время происходит полная индексация проекта, парсинг новых файлов, а также их анализ. У нас вполне получилось создать линтер для PHP, который работает быстро даже с нашей большой кодовой базой.

Что же в итоге?


Поскольку решение написано на Go, то требуется использовать репозиторий github.com/JetBrains/phpstorm-stubs, чтобы иметь определения всех встроенных в PHP функций и классов. Взамен мы получили высокую скорость работы (индексация 1 миллиона строк в секунду, анализ 100 тысяч строк в секунду) и смогли добавить проверки линтером в качестве одного из первых шагов в хуках для git push.

Была разработана удобная база для создания новых инспекций и достигнут уровень понимания кода, близкий к PHPStorm. Благодаря тому, что из коробки поддерживается режим работы с подсчётом диффа, есть возможность постепенно улучшать код, не допуская новых потенциально проблемных конструкций в новом коде.

Подсчет диффа не идеален: например, если один большой файл был разнесен на несколько мелких, то git, а следовательно и NoVerify, не смогут определить, что код был перемещен, и линтер будет требовать исправления всех найденных проблем. В этом плане подсчет диффа мешает проводить крупный рефакторинг, поэтому в таких случаях его зачастую отключают.

Написание линтера на Go имеет ещё одно преимущество: не только AST-парсер работает быстрее и потребляет меньше памяти, чем на PHP, но и последующий анализ тоже очень шустрый по сравнению с чем угодно, что можно было бы сделать на PHP. Это значит, что наш линтер может проводить более сложный и глубокий анализ кода, сохраняя при этом высокую производительность (например, фича «ленивые типы» требует выполнения довольно большого количества вычислений в процессе работы).

Open-source


NoVerify доступен в open-source на GitHub

Приятного использования в вашем проекте!

UPD: Я подготовил демо, которое работает через WebAssembly. Единственное ограничение этой демки — отсутствие определений функций из phpstorm-stubs, поэтому на встроенные функции линтер будет ругаться.

Юрий Насретдинов, разработчик отдела инфраструктуры ВКонтакте

Комментарии (46)


  1. Sergo96
    01.03.2019 18:52

    1) Можно как-то исключить папку (например vendor), если сканируешь весь проект?
    2) Попробовал на одном проекте на laravel — не видит функции, которые автоподгружаются из ядра laravel (напр., config, old) — «ERROR Call to undefined function config at...»


    1. youROCK Автор
      01.03.2019 18:54

      1) Можно исключить директорию из вывода отчётов, но не запретить её индексировать (иначе линтер будет считать, что функции и классы из этих директорий не объявлены). Для этого служит параметр -exclude=…
      2) Странно, может быть вы только поддиректорию анализируете?


  1. Bartlab
    01.03.2019 19:10
    -1

    Начина читать статью, сложилось впечатление, что ее цель — рассказать, сколько млн. строк кода на PHP в вашем продукте…


    1. mikechips
      02.03.2019 21:22
      +3

      Каждый видит то, что видит. Миллионы строк были объяснением, зачем писать своё с нуля, а не каким-то хвастовством.


    1. youROCK Автор
      03.03.2019 14:03

      Строго говоря, это вообще не плюс. Чем больше строк кода в продукте, тем сложнее с ним работать и тем больше усилий нужно прикладывать, чтобы всё это не разваливалось.


  1. Programmer
    01.03.2019 21:19

    Судя по всему линтер очень плохо понимает интерфейсы. Неужели в vk их не используют?


    1. youROCK Автор
      01.03.2019 21:41
      +1

      Да, интерфейсы мы почти не используем, поскольку KPHP (пока что) их не поддерживает :). Создавайте issue на гитхабе с примерами, постараемся поправить.


      1. aliday
        02.03.2019 11:20
        +2

        А почему было не попробовать использовать именно php storm для этих целей?


        1. youROCK Автор
          02.03.2019 11:33
          +1

          Это первое, что приходит на ум, но, к сожалению, это не самый лучший вариант по нескольким причинам:

          1) Я не большой фанат Java в целом :)
          2) PHPStorm хоть и не самая медленная IDE для PHP в мире, но скорость индексации и анализа у него не такая высокая, как мне бы хотелось. Даже если использовать встроенный кеш PHPStorm, всё равно время переиндексации при переключении веток оставляет желать лучшего (я анализирую как минимум 2 ветки кода при пуше — до и после)
          3) Мне неизвестно ни об одном кейсе успешного внедрения PHPStorm в качестве консольной утилиты на сервере. Даже если у PHPStorm есть соответствующий API, есть серьезные сомнения, что эта конструкция будет стабильная, производительная и простая в поддержке. То есть, в теории, в PHPStorm есть всё, что нам нужно, но вот на практике заставить это работать так, как мы хотим, по моим представлениям, потребовало бы очень больших усилий
          4) Мы хотим проверять ещё какие-то свои правила и не проверять какие-то вещи, которые проверяет PHPStorm, или делать это лучше. Это решается плагинами, но намного легче эту конструкцию поддерживать, когда у вас полный контроль над тем, как парсится код
          5) Вопрос лицензии — у меня нет особого понимания, сколько лицензий нам потребуется и как в целом это должно работать. Например, нужно ли держать phpstorm постоянно запущенным на сервере (иначе время только на запуск IDE будет весьма существенным)? Как с ним взаимодействовать?

          В общем, я бы хотел иметь возможность работать с PHPStorm таким способом, но мне кажется, что здесь лежит слишком много (потенциальных) проблем во время эксплуатации. Отдельная утилита будет быстрее, надежнее и проще.


    1. SerafimArts
      01.03.2019 23:03

      Насколько я помню, в ВК, из-за хайлоада (разработчики так аргументировали) используются подходы ~20 летней давности, с процедурками, глобалами и прочими весёлыми штуками, а kPHP ( github.com/vk-com/kphp-kdb ) не умеет в ООП и давно заброшен. Отсюда и «детские болячки» с отсутствием поддержки современного кода.

      Поправьте, если я ошибаюсь.


      1. youROCK Автор
        01.03.2019 23:06

        Все-таки внутри ВК компилятор развивается, и сейчас он уже поддерживает ООП в некотором урезанном виде, и его поддержка потихоньку расширяется. Более того, новый код зачастую проектируется уже с учетом ООП. Тем не менее, старый код действительно написан без него.


        1. SerafimArts
          01.03.2019 23:14
          +2

          Я заранее извиняюсь за излишнюю циничность в словах, но пользуясь случаем, хотел бы поинтересоваться:

          Почему бы не делать как FB, Badoo и прочие, и контрибьютить в то, что всем полезно и нужно, в основной язык, вместо того, чтобы тратить время на велосипед, который никому не нужен, кроме VK? Это позволило бы как минимум идти в ногу со временем, а не отставать на 15+ лет (потому что причислять «автолоад» к минусам, а не к плюсам — это как говорить, что «компьютер плохой, т.к. не поддерживает перфокарты»). Плюс, был бы нормальный фидбек со стороны сообщества, плюс позитивный пиар компании, плюс… Короче, очень много положительных факторов.

          P.S. Ну и да, линтер бы прогонялся в боевых условиях, а не kPHP проекте. Тогда бы и с интерфейсами был бы полный порядок.


          1. youROCK Автор
            01.03.2019 23:18

            Поверьте, KPHP не отстает на 15+ лет по фичам :). К тому же, в KPHP есть много фич, которых нет в PHP и которые нужны нам и не нужны никому больше.

            > Да и причислять «автолоад» к минусам, а не к плюсам… Ну это как говорить, что «компьютер плохой, т.к. не поддерживает перфокарты».
            Это минусы для нашей кодовой базы. Она до сих пор во многом основана на функциях (в основном это про старый код), и если линтер требует автолоада для работы, то проанализировать часть нашей кодовой базы он просто не сможет.


            1. SerafimArts
              01.03.2019 23:24

              К тому же, в KPHP есть много фич, которых нет в PHP и которые нужны нам и не нужны никому больше.


              Ну, допустим, все эти «фичи» добавляются с помощью расширений или какого-нибудь pre. За исключением, конечно, бизнес-логики.

              А что на счёт предложения обратно переехать на нормальный язык? Он за последнее время довольно сильно ускорился и если 7.0 уже местами перегоняла HHVM, то 8ка с asm-jit предполагает ещё больший прирост.


              1. youROCK Автор
                01.03.2019 23:41
                +1

                На PHP 7.2 сайт работает в целом сносно, но бОльшая часть разделов всё ещё работает существенно медленней, чем на KPHP. Это связано с тем, что кодовая база VK уже весьма давно существует в рамках того, что позволяет компилятор KPHP и есть довольно много фич, которые сделаны специально для ускорения существующей кодовой базы и наоборот, много кода написано с учетом специфики именно KPHP. Ситуация похожа на то, как у фейсбука с HHVM и Hack — я бы сказал, что назад возвращаться стоило бы бОльших усилий, чем допиливать нужные для нас вещи в KPHP. Но это моё личное мнение, у кого-то может быть противоположное.


                1. SerafimArts
                  02.03.2019 06:05

                  Ну понимаете же сами, что учитывая ту публикацию скольких-там-лет-давности о «котиковом пыхе» вообще ничего известно не было, по-этому в некоторых кругах и сложилось подобное впечатление об этом проекте, как и об организации в целом. Это замечательно, что вы его развиваете, но печально, что положили болт на то, что выложили, т.к. открытых иссью в 3 раза больше чем закрытых, а PR висят с 2014го года.

                  Я не знаю как у других, но лично у меня доверие к ВК после такого подорвано и текущий анонс полурабочего линтера воспринимаю точно так же. «Вбросили, добавили пару коммитов и забыли», а значит и смотреть и оценивать даже не имеет смысла.

                  И всё же надеюсь что это не так, и в этот раз вы не оплошаете, т.к. анализатор в качестве lang сервера — это действительно крутой вариант.

                  P.S. Так а всё же на тему оригинального языка, может всё же стоит посмотреть в его сторону тоже? Оформите там RFC с вашими синтаксическими наработками, например, которые показали себя на практике с положительной стороны. М?


                  1. AterCattus
                    02.03.2019 07:54

                    В KPHP нет синтаксиса, отсутствующего в обычном PHP, т.к. это потребовало бы введение поддержки во всех IDE и редакторах, которые используются в нашей команде. На это мы пока не готовы.
                    Так что единственное отличие (ну за некоторыми синтаксически совместимыми особенностями): поддержка особых phpdoc, которые помогают kphp лучше понимать намерения разработчика и генерить более правильный и нативный C++ код. Именно большее понимание кода и вывод типов (даже довольно сложных) позволяет дать C++ компилятору код, который будет скомпилен более оптимально. За счет этого и выигрыш по скорости, причем существенный.
                    Мы прикладываем много усилий, чтобы типы выводились более точно. И тут линтер — один из инструментов. Но это не единственное его предназначение.


                    1. SerafimArts
                      02.03.2019 09:25

                      Да, теперь я прекрасно понимаю почему это нельзя оформить даже в виде RFC. Просто не примут, т.к. слишком специфичный подход к оптимизации.


                  1. AterCattus
                    02.03.2019 08:03

                    Что касается той выкладки kphp в паблик. И тут уже мое личное мнение.
                    Это было сделано зря, но не нам уже судить это решение, тем более что с тех пор команда сильно поменялась. Сейчас снова выкладывать kphp и поддерживать этот вариант у нас не готовы. Не в последнюю очередь потому, что мы гораздо внимательнее относимся к публичной леятельности и опенсорсу в частности. Это долгоиграющая ответственность, а не разовый шаг. Что мы можем и готовы публиковать в опенсорс и подлерживать — мы это делаем. Линтер — это один из таких примеров. KPHP, к сожалению ?, сейчас не такой проект.
                    Повторюсь, это все мое личное мнение.


                    1. SerafimArts
                      02.03.2019 09:46

                      Думаю многие согласны с этим. А по поводу линтера, надеюсь вы доведёте его до ума, чтобы он не выкидывал ошибки, вроде:

                      ERROR Call to undefined function \ucfirst at ...\Support.php:153
                      return \ucfirst(\strtolower(\gettype($value)));
                      ^^^^^^^^
                      ERROR Call to undefined function \strtolower at ...\Support.php:153
                      return \ucfirst(\strtolower(\gettype($value)));
                      ^^^^^^^^^^^


                      1. youROCK Автор
                        02.03.2019 09:51
                        +1

                        Ошибка выглядит так, что вы не указали (или неправильно указали?) путь к phpstorm-stubs директории (например, эта директория пустая). Вроде линтер должен проверять хотя бы наличие директории, если этого не происходит, то заведите issue, пожалуйста.


                        1. SerafimArts
                          02.03.2019 09:59

                          Да, всё верно: #4. Просто такой километр логов, что подобная информация просто затерялась. Думаю стоит добавить ещё "-output" для этого?

                          А так да, всё круто и очень быстро, но очень много ложных срабатываний (691 critical errors против 0 у Psalm). Я боюсь что довольно проблематично будет заводить иссью под каждый false-positive. В каком формате мне в этом случае стоит прислать фидбек?


                          1. youROCK Автор
                            02.03.2019 10:07
                            +1

                            По поводу issue #4 — под windows я не тестировал :), поэтому cache-dir так странно себя ведёт :). Пока я это не починил, можете просто не указывать cache-dir вообще, этот параметр не является обязательным.


                            Что касается фидбека — если вам не запрещает NDA, можете прислать мне исходники проекта и я сам смогу посмотреть :). Второй вариант — попробовать начать использовать noverify в своем проекте в режиме с подсчётом диффа и видеть только новые предупреждения. Тогда их будет так много сразу и легче будет изолировать отдельные проблемы (мы так у себя внутри и делаем, собственно).


                            1. SerafimArts
                              02.03.2019 10:45
                              +1

                              Второй вариант — попробовать начать использовать noverify в своем проекте в режиме с подсчётом диффа и видеть только новые предупреждения. Тогда их будет так много сразу и легче будет изолировать отдельные проблемы (мы так у себя внутри и делаем, собственно).

                              Да, это хорошая идея. Можно добавить его в CI и время от времени присылать фидбек с новыми проблемами.


                              1. youROCK Автор
                                03.03.2019 22:42

                                Кстати я там починил парочку (штук 10) issues, в том числе работу кеша под Windows. Проверьте, пожалуйста.


                                1. SerafimArts
                                  03.03.2019 23:47

                                  Ничего себе у вас там сурово в ВК. На дворе воскресенье, а вы иссью закрываете


                                  1. youROCK Автор
                                    04.03.2019 00:04

                                    У меня это заняло около полутора часов, и никто меня об этом даже не просил :). Большинство вещей исправить было очень легко, остальное я оставил на подумать.


                    1. mikechips
                      02.03.2019 21:35
                      +1

                      Тут уж беда в том, что после того случая не хочется особо трогать ваши технологии кроме как ради интереса. Они во многом крутые, но вопрос вот в чём: представьте, что кто-то обрадовался появлению исходников kPHP (и части ВК по сути следом) и начал его использовать в боевом проекте, а потом вдруг оказалось, что вы не готовы поддерживать его в опенсорсе, поэтому надо либо самому допиливать, либо уходить обратно в чистый PHP.


                      Я бы объяснил это тем, что код опубликован чисто для образования народа, а не боевых проектов, но вы это так не позиционировали же? И теперь непонятно, как быть — вдруг кто-то возьмёт этот линтер и опять что-то поменяется?


                      1. AterCattus
                        03.03.2019 06:25

                        Сейчас у нас совсем по другому относятся к выкладыванию чего-либо. Плюс сил и желания поддерживать несопоставимо больше.
                        Пользоваться или нет — тут уже выбор каждого. Можно, например, подождать и посмотреть :)
                        А вот брать kphp, если он, представим себе, будет повторно выложен, будет ли кто? Кто есть из крупных проектов с подобными нагрузками и крепко сидящих на php, кто не мигрирует на какой-то другой ЯП, либо написал собственную вундервафлю?


                        1. SerafimArts
                          03.03.2019 06:42
                          +1

                          Сейчас у нас совсем по другому относятся к выкладыванию чего-либо.


                          А можно я тогда чуть вброшу на вентилятор тогда? +)

                          Берём github.com/VKCOM/vk-php-sdk
                          1) Где тесты?
                          2) Где коверейдж?
                          3) Где настроенный CI?
                          4) Где PSR? Не только второй, но и для HTTP запросов, для кеша, для миддлварей, хоть какой-нибудь.
                          5) Где шаблоны для issue и pr?
                          6) Почему написано, что php 7.1+, а внутри код эпохи 5ки с «array()» и прочими прелестями?

                          </вентилятор-mode>


                          1. AterCattus
                            03.03.2019 06:55

                            Интересно. Спасибо. Пойду спрошу у выложивших :)


                        1. mikechips
                          03.03.2019 18:29

                          Скажем так: у нас был заказчик с проектом, претендующим на высокие нагрузки (именно ранняя стадия разработки). Супербыстрого да инновационного обновления PHP ещё не было, нужно было выбирать из того, что есть. Стоял выбор: kPHP, HHVM или jPHP. Причём о всех трёх, как ни странно, узнали именно благодаря Хабру.


                          В итоге всё же выбрали самый обычный 5-й PHP в виду его стабильности и в надежде потом переписать на Python, когда будут люди. А ещё позже появился 7-й PHP и все вопросы отпали.


                          А теперь представим, что мы выбрали бы kPHP, рассчитывая, что когда-нибудь его и в опенсорсе будут обновлять. Конечно, вы ничем никому не обязаны и это личное право, но всё равно было бы неприятно от такой ситуации.


                          К тому же, как говорит оригинальная статья:


                          С помощью публикации этих разработок мы возвращаем долг open-source сообществу, которому многим обязаны.
                          Мы надеемся, что теперь они помогут разрабатываемым сейчас проектам, как в своё время MySQL, Memcache, nginx и PHP помогли создать ВКонтакте.

                          Но по сути абстрактный долг никто не погасил, потому что Memcache, nginx и PHP стабильны, поддерживаются, имеют кучу документации и прочей макулатуры и развиваются как отдельный продукт. kPHP же так и остался корпоративным неуниверсальным решением, потому именно как open-source продукт не может стоять с ними в одном ряду.


                          А посему на будущее очень хотелось бы попросить изначально чётко давать понимание, как вы относитесь к продукту. Овцы тут, волки тут: kPHP — в большей степени наше, линтер — для всех, оформили и будем обновлять. Иначе "возвращение долга" сконвертируется в недоверие да баттхёрт. Спасибо большое)


                          upd: Я понимаю, что команда с тех пор менялась, это скорее обобщённое обращение, чем лично к кому-то.


                  1. Zibx
                    02.03.2019 17:17

                    Доверие к ВК было утрачено когда они выдавали Дурова, когда без решения суда сливали все данные. Сразу как в проекте появляется мэил.ру груп — к нему больше не стоит прикасаться. Какие моральные принципы могут быть у компании которая сделала мэйл-ру-агент? Которая использовала методики распространения и слежки за пользователями в лучших традициях залива троянов?
                    Этот проект может только показать что технические специалисты менеджерятся в этих структурах точно по тем же принципам что и остальные.


                    1. mikechips
                      02.03.2019 21:36
                      +2

                      Мы здесь вроде как обсуждаем именно сторону разработки и кода, а не менеджмента?)


  1. sumanai
    01.03.2019 22:54

    There is no official extension for VS Code that supports this mode, so you will need to take, for example, marketplace.visualstudio.com/items?itemName=felixfbecker.php-intellisense VS Code extension and replace extension.js to the one provided in this repo.

    Я ожидал готового расширения, а не странных игр с файлами другого.
    Но проект интересен, скорости работы никогда не бывает достаточно!


    1. youROCK Автор
      01.03.2019 22:56

      Согласен, хорошо бы сделать отдельное расширение, но пока что руки не дошли. Все-таки первостепенная задача была сделать линтер, а не IDE :).


  1. OnYourLips
    02.03.2019 12:24

    А можете рассказать, почему вы используете KPHP?
    Он реально сильно производительнее, чем C# или Java для ваших задач? А если сравнить с хардкорным C++?

    Я прочитал векти комментариев, и судя по всему, это было бы единственным разумным объяснением.
    Ведь фактически вы создаете свои инструменты, вкладываетесь в обучение сотрудников, платите зарплаты сильно выше рынка (чтобы компенсировать демотивацию, вызванную отсутствием прироста опыта работы с мейнстрим экосистемами у сотрудников) и все это стоит денег. А ради чего?


    1. AterCattus
      02.03.2019 12:43
      +1

      Юра несколько раз говорил, что у нас очень много уже написанного кода на PHP, и от него никуда не деться. Это данность, с которой приходится уживаться. Там, где мы можем не использовать PHP, мы его и не используем. У нас много кода на C и C++, также прилично на Go и python (последний по наслышке).


    1. youROCK Автор
      02.03.2019 12:50

      > А можете рассказать, почему вы используете KPHP?
      Изначальная кодовая база сайта была на PHP, и начиная с некоторого объема кода (который был к тому моменту явно достигнут), было легче написать свой транслятор из PHP в C++, чем руками портировать весь этот код на другой язык, попутно сломав много чего, и сильно затормозив развитие сайта на это время.

      KPHP позволяет более плавно перейти с PHP с его полностью динамической типизацией (и сопутствующими принципиальными ограничениями по производительности) в более статически типизированный код, который будет эффективно трансливаться в С++. При этом учить новый язык не нужно, статическая типизация в KPHP работает на уровне phpdoc-аннотаций и специальных комментариев.

      К тому же, разработка на PHP имеет и свои плюсы — в development окружении мы используем обычный PHP, поэтому разработчики могут быстро итерироваться во время разработки и обратная связь после правок дается сразу — достаточно перезагрузить страницу или сделать новый API-запрос на сервер. Если с Java и C# в некоторых случаях можно к этому приблизиться, то с C++ так точно не выйдет. Лично я не большой фанат языков с виртуальной машиной и JIT'ом, и, насколько я знаю, наши админы тоже :). А на чистом C++ разработка будет медленной, подверженной ошибкам, и очень дорогой по сравнению с PHP/KPHP.

      > Ведь фактически вы создаете свои инструменты
      Начиная с некоторого объема кодовой базы, а также начиная с какого-то количества серверов и нагрузки, становится выгоднее разрабатывать свои инструменты, чем пытаться подогнать уже существующие под наши реалии. Это не значит, что мы не смотрим в сторону существующих решений, когда они нам подходят — например, мы используем у себя ClickHouse и очень им довольны.

      > чтобы компенсировать демотивацию, вызванную отсутствием прироста опыта работы с мейнстрим экосистемами у сотрудников
      Моё мнение, что мотивация у каждого человека своя. Например, мне наоборот очень интересен хайлоад и огромные системы, поэтому для меня наоборот это интересная возможность поработать над таким проектом и пилить свои инструменты там, где стандартные не работают. Для кого-то это возможность повлиять на продукт, которым пользуются почти 100 млн человек ежемесячно, для кого-то просто работать в историческом центре Санкт-Петербурга, и т.д.

      > и все это стоит денег. А ради чего?
      Как правило, необходимость в своих инструментах возникает тогда, когда вы достигаете определеного масштаба. В ВК несколько десятков тысяч серверов, на которых крутится сайт, поэтому, если, например, можно написать что-то на 30% эффективнее, чем существующие инструменты, то это нам сэкономит тысячи серверов, и оно однозначно того стоит. Ведь бизнес же прежде всего про зарабатывание денег, а свои инструменты позволяют тратить меньше ресурсов и делать так, чтобы сайт работал быстрее, надежнее, и т.д. Плюс, разрабатывать свои инструменты — это же весело :), особенно когда они действительно кому-то нужны.


      1. OnYourLips
        02.03.2019 15:03

        AterCattus, тут часть ответов и вам

        что у нас очень много уже написанного кода на PHP, и от него никуда не деться
        Изначальная кодовая база сайта была на PHP
        Но ведь KPHP не умеет выполнять код на PHP хотя бы из-за ООП. Вам все равно пришлось переписывать.
        Или вы писали без ООП даже до появления KPHP? (Зачем? Производительностью нельзя объяснить, т.к. PHP раньше был медленным хоть с ООП, хоть без него, при этом были гораздо более производительные альтернативы)

        И зачем в таком случае развивать KPHP, если можно оставить его как есть в виде легаси, а новые сервисы писать уже на Java или Go, постепенно заменяя ими старые?

        также прилично на Go и python
        А почему python, а не PHP? Учитывая благоприятное состояние рынка труда в РФ для PHP, общих с KPHP навыков и производительности?

        Лично я не большой фанат языков с виртуальной машиной и JIT'ом, и, насколько я знаю, наши админы тоже :)
        Но ведь PHP — тоже VM, причем в следующих версиях JIT обещают.

        поэтому, если, например, можно написать что-то на 30% эффективнее, чем существующие инструменты, то это нам сэкономит тысячи серверов, и оно однозначно того стоит
        Так почему не Java? KPHP на ваших задачах будет производительнее?


        1. youROCK Автор
          02.03.2019 15:10

          Но ведь KPHP не умеет выполнять код на PHP хотя бы из-за ООП. Вам все равно пришлось переписывать.
          Или вы писали без ООП даже до появления KPHP? (Зачем? Производительностью нельзя объяснить, т.к. PHP раньше был медленным хоть с ООП, хоть без него, при этом были гораздо более производительные альтернативы)

          Писали почти без ООП, например потому, что реализация ООП раньше в PHP была ещё медленней, чем на объектах.

          Но ведь PHP — тоже VM, причем в следующих версиях JIT обещают.

          Это всё сильно будущее время :). Решение принималось очень давно, когда не было очевидно даже то, что PHP7 так сильно всё ускорит.

          Так почему не Java? KPHP на ваших задачах будет производительнее?

          Здесь я могу лишь высказать своё мнение — мне лично Java не нравится, как минимум, следующим:
          — излишняя формальность и «энтерпрайзность»
          — многословность
          — производительность
          — потребление памяти
          — неудобство эксплуатации (к эксплуатации программы добавляется необходимость ставить и тюнить JVM)

          У этого языка есть и плюсы, конечно, но если выбирать между Java и PHP, я наверное выберу второе. Но если давать вообще выбор серверного языка, то это будет ни то, ни другое, это будет скорее какой-нибудь Go :).


          1. Fortop
            02.03.2019 16:15

            мне лично Java не нравится, как минимум, следующим:
            — излишняя формальность и «энтерпрайзность»
            — многословность
            — производительность

            Ни разу не поклонник Java, но, справедливости ради, решение на ней занимает 6 место…


            https://highloadcup.ru/ru/rating/round/5/


            1. youROCK Автор
              02.03.2019 16:18
              +1

              Безусловно, на Java можно писать производительный код, который после того, как прогреется JIT, будет показывать хорошую производительность. Но так пишут далеко не всегда :). Go в этом плане мне нравится намного больше — там нет JIT, он работает быстро прямо на старте, не слишком многословен и позволяет быстро писать работающий код. Но это больше вопрос вкуса, как по мне, чем каких-то объективных критериев.


        1. AterCattus
          03.03.2019 06:49

          Но ведь KPHP не умеет выполнять код на PHP хотя бы из-за ООП. Вам все равно пришлось переписывать.
          Или вы писали без ООП даже до появления KPHP? (Зачем? Производительностью нельзя объяснить, т.к. PHP раньше был медленным хоть с ООП, хоть без него, при этом были гораздо более производительные альтернативы)

          Все так, ООП код по замерам того времени выполнялся медленее, чем на функциях. Массовое использование классов php'шных у нас началось не так давно, особенно после того, как kphp научился транслировать php классы в плюсовые структуры без какого-либо ООП'шного оверхеда.
          Тут важно вспомнить, что переход на KPHP у нас начался где-то во времена PHP 5.3. Но уже и к тому моменту кода было прилично уже написано.

          И зачем в таком случае развивать KPHP, если можно оставить его как есть в виде легаси, а новые сервисы писать уже на Java или Go, постепенно заменяя ими старые?

          А тут все просто: у нас монолит. Если собирается специфическая сборка, она вкомпиливает в себя весь достижимый код из указанной точки входа. Соответственно, добавление любого функционала сайту — это добавление PHP кода в этот же монолит.
          Последнее время мы занимаемся уменьшением связности и некоторым техдолгом, чтобы что-то с этим сделать.
          А если мы можем сделать что-то обособляемое, то как раз и пишем это на Go или Python. Есть небольшие ex-PHP/Python штуки, которые мы переписали на Go: сбор ошибок, демоночки на региональных кешах, обработка анонсируемых префиксов в тех же регионах… Некоторые сложные вычислительные задачи наши C/C++ разработчики оформляют в виде демонов/сервисов.

          Так что мы не пишем все на PHP и кайфуем от этого :)

          А почему python, а не PHP?

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

          Так почему не Java?

          И после вышенаписанного совершенно не понятно, зачем нам Java. Где мы завязаны на PHP — там будет KPHP, в остальных нагруженных местах — C++ и Go.


          1. OnYourLips
            03.03.2019 13:40

            Все так, ООП код по замерам того времени выполнялся медленее, чем на функциях.
            PHP тех времен сам выполнялся очень медленно. Почему вы не выбрали другой более подходящий язык?
            Это дало бы гораздо больше прироста производительности, чем попытки писать без ООП.

            Последнее время мы занимаемся уменьшением связности и некоторым техдолгом, чтобы что-то с этим сделать.
            Правильно ли я понимаю, что вы высказываете свои мысли о том, что был сделан неправильный выбор?

            И после вышенаписанного совершенно не понятно, зачем нам Java.
            Вы в минусы джавы написали про производительность. И это сравнивая с низкой производительностью PHP того времени. Вы делали тесты и замеры?
            Ведь производительность — это один из основных критериев выбора в пользу джавы.


            1. AterCattus
              03.03.2019 15:12

              PHP тех времен сам выполнялся очень медленно. Почему вы не выбрали другой более подходящий язык?

              Так к тому времени уже была куча кода на PHP, которую было не переписать, а в скорость работы уже уперлись.

              Правильно ли я понимаю, что вы высказываете свои мысли о том, что был сделан неправильный выбор?

              Ну, монолит — это не лучшая архитектура при развитии проекта и росте команды. Но он удобен на первых этапах. Так что в целом нормально.

              Вы в минусы джавы написали про производительность. И это сравнивая с низкой производительностью PHP того времени. Вы делали тесты и замеры?

              Ну тут опять же про то, что у нас уже была куча кода на PHP.