Патч PHP-скрипт в стиле KSplice - PullRequest
1 голос
/ 04 декабря 2011

Это в основном эксперимент , но я думаю, что он имеет практическое применение.

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

Например, допустим, я написал сервер WebSocket на PHP, и этот сервер работает под управлением следующего класса ...

class MyServerApp extends WebSocketServerApp {
    protected $clients = array();

    public function onConnect($client){
        $this->clients[$client->getId()] = $client;
    }

    public function onDisconnect($client){
        unset($this->clients[$client->getId()]);
    }

    public function onData($client, $data){
        $client->send($data); // perform echo functionality
    }

}

По сути, сервер создает один экземплярMyServerApp класс и вызывает его соответственно.Вышеуказанное приложение является эхо-сервером;он отвечает на все запросы клиентов в точности так, как они просили.

Теперь предположим, что я изменил исходный код сервера и хочу, чтобы существующий сервер работал, но изменил поведение (чтобы не потерять существующих клиентов),Сервер приложений удобно имеет событие onTick(), которое мы можем использовать для проверки изменений в исходном коде:

class MyServerApp extends WebSocketServerApp {
    // the existing code from above goes here

    /**
     * @var integer Timestamp of when the server was last patched.
     */
    public $last_patch = 0;

    public function __construct(){
        $this->last_patch = time();
    }

    public function onTick(){
        if($this->last_patch < filemtime(__FILE__)){
            // include __FILE__;
        }
    }
}

Проверка исправлений, вероятно, будет работать хорошо, , но фактический метод исправления выиграл 't (это, кстати, в настоящее время прокомментировано). Основная причина в том, что класс сервера уже определен.

Так как бы вы сделали фактическое исправление?Каким-то образом перезаписать функции или классы?

Возможные решения

  • Использовать более подходящий язык (например, js / node.js), который позволяет перезаписывать (@teresko)
  • Использование classkit (и / или альтернатив) для перезаписи функций / классов PHP (@ MatějZábský)
  • Разгрузка логики сервера для анонимных функций и просто перезапись таких функций *
  • Переименование дублированных классов и загрузка их в обычном режиме(безусловно, утечка памяти) (@chris)

[*] server.php будет выглядеть так:

$server['onData'] = function(){ /* new function body */ };

Примечания

С другой стороны,эта архитектура ставит некоторые проблемы, которые действительно необходимо устранить:

  • постоянное повреждение работающего сервера
  • утечки памяти (некоторые ресурсы будут утекать, вероятновключая функции)
    • (есть ли в PHP GC для функций?)

Ответы [ 4 ]

1 голос
/ 05 декабря 2011

Вы можете сделать это без какого-либо расширения ... но решение не просто грязное ... это антихрист кода!

Если вы используете eval в методе, оцененный код может использовать $ this для доступа к закрытым и защищенным свойствам и методам!

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

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

  1. создать серверный класс
  2. создать дочерний класс, который переопределяет все методы (используя eval)
  3. проверить наличие обновлений.
  4. если файл обновлен, используйте функцию токенизатора для сканирования источника на предмет определения функций (токен функции [-> пробел | комментарии] -> строковый токен с правильным именем функции -> не '{' -> '{ ')
  5. объединяйте все токены, пока у вас не будет столько закрытий, сколько открывающих фигурных скобок.
  6. используйте полученные строковые токены в ваших методах evaling.

И нет, это не Спарта .... это безумие.

Если вы хотите узнать больше о структуре файлов PHP, продолжайте.

Но никогда используйте это в продуктивной среде !!!!!!!

0 голосов
/ 05 декабря 2011

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

Соединение scrpts записывает данные запроса в базу данных или управляемый memcache «пул заданий» и ищет результаты, которые они могут вернуть в «пуле результатов».

Рабочий сценарий принимает заданияиз пула обрабатывает их и помещает результаты в «пул результатов».

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

Iне думайте, что это хороший способ реализовать сервер, но выбор за вами.

0 голосов
/ 04 декабря 2011

Я думаю, что вам нужно использовать расширение вроде runkit, если вы хотите переопределить класс. Но вы также можете просто дать новому классу некоторую форму серийного имени, например MyClassVersion56. Используйте фабрику, чтобы вызвать класс, и фабрика знает имя самого последнего класса, потому что вы где-то устанавливаете переменную. Вы можете даже str_replace исходный код, чтобы сделать автоматическое последовательное именование.

следите за использованием памяти, если вы долго запускаете php-скрипты ...

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

0 голосов
/ 04 декабря 2011

Возможно, вы захотите взглянуть на classkit (его функция classkit_method_redefine и classkit_import в частности).

Но это действительно ужаснопутьВы уверены, что не можете позволить себе время простоя в миллисекундах при перезапуске сценария (как это делают все)?

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

РЕДАКТИРОВАТЬ:

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

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

...