Отключить эхо и печатать в PHP - PullRequest
6 голосов
/ 02 июля 2011

Это может показаться забавным вопросом, но на самом деле это не так, я бы хотел отключить echo, print и другие функции, которые могут выводить в буфер, такие как readfile.

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

Теперь я знаю, что могу установить выходной буфер в начале моего скрипта и выбросить любое содержимое, но это не будет включать такие вещи, как header и set_cookie, поэтому мой вопрос может быть интерпретирован как Как я могу контролировать буфер для заголовка ответа

Есть ли какой-либо возможный способ управления всеми аспектами вывода PHP, например, присваивать обратный вызов основному буферу, а не просто телу ответа?

Ответы [ 4 ]

6 голосов
/ 02 июля 2011

В конце концов, не существует эффективного способа достичь этого, потому что, по крайней мере, echo - это не функция, а языковая конструкция, которую нельзя отключить.Вы можете поэкспериментировать с буферизацией вывода (ob_start() и тому подобное), но это не помешает другому коду снова отключить буферизацию вывода.

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

3 голосов
/ 02 июля 2011

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

Однако вы можете использовать встроенную буферизацию вывода для управления выводом без заголовка. Самая лучшая часть - это возможность вложения:

ob_start();
echo 'The first output!',"\n";

ob_start();
echo 'The second output.';

$output2 = ob_get_clean();

ob_flush();

echo $output2;

выведет:

The first output!
The second output.
1 голос
/ 04 августа 2014

сделайте это так:

    function trace($message) {
      $echo = true;
      if ($echo)
      echo $message . "<br>";
    }

затем просто вызовите трассировку («ваше сообщение»);в любое время, когда вам это нужно, и переключите $ echo в false, чтобы отключить его глобально

1 голос
/ 27 февраля 2012

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

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

    Вы можете использовать token_get_all() для аудита выходных ключевых слов, таких как T_ECHO и T_PRINT ( - список всех возможных токенов ).Не пытайтесь отключить доступ к чему-либо, кроме ключевых слов, здесь слишком много способов обмануть это (eval(), переменные переменные, data: // streams и т. Д.). Здесь вы блокируете только определенные ключевые слова.

    Не забудьте T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE и T_REQUIRE_ONCE.Они могут быть использованы не только для включения неаудированного кода (например, PHP-кода, записанного во временный файл, а затем включены в него), но и с помощью некоторых из более продвинутых оболочек файлов, сами они могут использоваться для вывода.

  2. Используйте расширение PHP ADB , чтобы отключить доступ к определенным методам, переименовав их.Не пытайтесь отключить функции вывода, это просто не сработает, есть слишком много способов для генерации вывода.Выберите специальные, такие как set_cookie() и header(), но для фактического вывода есть бесчисленное множество способов получения результата.Единственный надежный способ заблокировать это - использовать буферизацию вывода, но отключить доступ к методам управления буфером вывода, чтобы они не могли обойти буферизацию.

    class YourApplicationControllingClass {
    
        final protected function callUserCode($pathToUserCodeFile) {
            // We want this to be a local variable so there's no way to get to it
            // with PHP Reflection
            $suspender = new SuspendFunctions();
    
            ob_start();
    
            // You may need to add more here, this is just my superficial pass
            $suspender->suspend("ob_clean", "ob_end_clean", "ob_end_flush", "ob_flush", 
                    "ob_get_clean", "ob_get_contents", "ob_get_flush", "ob_get_length",
                    "ob_get_level", "ob_get_status", "ob_implicit_flush", "ob_list_handlers",
                    "ob_start", "output_add_rewrite_var", "output_reset_rewrite_vars",
                    "set_cookie", "set_raw_cookie", "header_register_callback", "header",
                    "header_remove", "http_response_code", "register_shutdown_function",
                    "register_tick_function", "unregister_tick_function", "set_error_handler",
                    "restore_error_handler", "set_exception_handler", "restore_exception_handler"
                    );
    
            $this->callUserCodeSandbox($pathToUserCodeFile);
            // Restore our original execution environment
            $suspender->resume();
    
            $content = ob_get_clean();
            // If you want to be aggressive, check to see if they produced any output
            // and blacklist them if they even try.
            if ($content !== '') $this->blacklistUserCode($pathToUserCodeFile);
        }
    
        private function callUserCodeSandbox($pathToUserCodeFile) {
            require($pathToUserCodeFile);
        }
    }
    
    final class SuspendFunctions {
        private $suspendedFunctions = array();
    
        /**
        * Suspends certain functions from being executable.
        * @param string $function,... Names of functions to suspend, you may pass multiple
        * @return void
        */
        function suspend($function) {
            $functions = func_get_args();
            foreach($functions as $function) {
                // Make sure we don't double-suspend a function
                if (isset($this->suspendedFunctions[$function])) continue;
    
                // Make new names unguessable, and make it very unlikely that we end up with a collision.
                $newName = '_'.md5($function.microtime(true).mt_random());
    
                // Rename to the unguessable name
                rename_function($function, $newName);
    
                // Keep a record for ourselves what this new name is so we can resume later
                $this->suspendedFunctions[$function] = $newName;
            }
        }
    
        /**
        * Resumes functions for calling
        */
        function resume() {
            foreach($this->suspendedFunctions as $function=>$newName) {
                rename($newName, $function);
                unset($this->suspendedFunctions[$function]);
            }
        }
    }
    

Просто знайте,Независимо от того, насколько вы хороши в этом, почти наверняка найдется способ обойти (например, возможно, их код исправляет содержимое одного из файлов вашего приложения, чтобы снова разрешить вывод).PHP слишком гибок, чтобы полностью его заблокировать.У PHP был похожий проект под названием Безопасный режим, от которого они в конечном итоге отказались, потому что было невозможно полностью надежно заблокировать все.Пока пользователь имеет полную среду выполнения, все, что вы можете сделать, чтобы заблокировать его, он может отменить.В общем случае очень неразумно выполнять введенный пользователем код без ручного аудита каждой строки (и даже тогда опасно).

...