Удаление функции во время выполнения в PHP
Я знаю, что этот вопрос кажется взломанным и странным, но есть ли способ удалить функцию во время выполнения на PHP?
У меня есть рекурсивная функция, объявленная в блоке "if", и хочу, чтобы эта функция была "действительной" только в этом блоке "if". Я не хочу, чтобы эта функция вызывалась вне этого блока.
Я узнал runkit_function_remove, но runkit isn 't включен на моем веб-хосте. Есть ли другой способ сделать это?
BTW Я поддерживаю только PHP 5.1.0.
Изменить: Я знал, что мой вопрос был взломан, но вот то, что я хочу сделать:
if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc())
{
function stripslashes_deep($value)
{
return is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
}
$_POST = array_map('stripslashes_deep', $_POST);
$_GET = array_map('stripslashes_deep', $_GET);
$_COOKIE = array_map('stripslashes_deep', $_COOKIE);
$_REQUEST = array_map('stripslashes_deep', $_REQUEST);
//runkit_function_remove('stripslashes_deep');
}
Так как "stripslashes_deep" будет жить только тогда, когда Magic Quotes включены, я хотел бы избавиться от него, когда покончу с этим. Я не хочу, чтобы люди полагались на функцию, которая не всегда существует. Надеюсь, теперь это станет яснее. Предложения, не связанные с хаккой, тоже приветствуются!
Ответы
Ответ 1
Из Руководство по функциям PHP:
Все функции и классы в PHP имеют глобальную область действия - их можно вызывать вне функции, даже если они были определены внутри и наоборот. [...] PHP не поддерживает перегрузку функций, а также невозможно определить или переопределить ранее объявленные функции.
Исключение - через runkit. Однако вы можете определить свою функцию как анонимную функцию и unset
после запуска, например
if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc())
$fn = create_function('&$v, $k', '$v = stripslashes($v);');
array_walk_recursive(array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST), $fn);
unset($fn);
}
Некоторые комментаторы правильно указали (но уже не проблема в PHP в настоящее время), вы не можете вызвать анонимную функцию внутри себя. Используя array_walk_recursive
, вы можете обойти это ограничение. Лично я бы просто создал регулярную функцию и не беспокоился об ее удалении. Это никому не повредит. Просто дайте ему собственное имя, например stripslashes_gpc_callback
.
Примечание: отредактировано и сжато после комментариев
Ответ 2
Начиная с PHP 5.3 вы можете использовать анонимную функцию:
$anonymous_function = function($value){
// do stuff
};
Назовите его следующим образом:
$returned_value = $anonymous_function('some parameter');
Чтобы удалить функцию, просто отключите переменную:
unset($anonymous_function);
Вот пример того, как реализовать вашу функцию:
$stripslashes_deep = function (&$object){
global $stripslashes_deep;
if(is_array($object))
foreach($object as &$value){
if(is_array($value))
$stripslashes_deep($value);
else
$value = stripslashes($value);
}
};
анонимные функции не имеют имени. Таким образом, вы не можете использовать array_map, так как он будет использовать только имя функции в качестве параметра.
анонимные функции имеют переменную область. Таким образом, вы должны объявить переменную, содержащую функцию глобальную, для ее получения из рекурсивной функции.
К сожалению, если вы определите регулярную функцию внутри анонимной функции, она будет доступна в глобальном масштабе, даже после того, как вы отмените переменную. (Я не вижу в этом ничего хорошего. Надеюсь, что ошибка будет исправлена позже:-)
Ответ 3
Простой ответ: нет.
Однако вы можете попробовать разместить функцию внутри пространства имен, класса или даже внутри другой функции, но я думаю, что не то, что вы ищете.
Еще одна опция, которую вы используете, - это использовать debug_backtrace()
внутри указанной функции, чтобы проверить, какой файл/строка/etc... вызывает ее - это хакерское я знаю, но это также runkit_function_remove()
.
Изменить - Слишком плохо, что вы не запускаете PHP 5.3+, иначе вы могли бы просто сделать:
if (get_magic_quotes_gpc())
{
$_GET = json_decode(stripslashes(json_encode($_GET, JSON_HEX_APOS)), true);
$_POST = json_decode(stripslashes(json_encode($_POST, JSON_HEX_APOS)), true);
$_COOKIE = json_decode(stripslashes(json_encode($_COOKIE, JSON_HEX_APOS)), true);
$_REQUEST = json_decode(stripslashes(json_encode($_REQUEST, JSON_HEX_APOS)), true);
}
Для более старых версий PHP у вас все еще есть эта опция:
if (get_magic_quotes_gpc()) {
$process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
while (list($key, $val) = each($process)) {
foreach ($val as $k => $v) {
unset($process[$key][$k]);
if (is_array($v)) {
$process[$key][stripslashes($k)] = $v;
$process[] = &$process[$key][stripslashes($k)];
} else {
$process[$key][stripslashes($k)] = stripslashes($v);
}
}
}
unset($process);
}
Никаких функций нет, и код не так длинный. =)
Ответ 4
Определение начальной функции выглядит следующим образом:
// includes/std_functions.php
if (! function_exists('the_function')) {
function the_function() {
global $thefunction;
return call_user_func_array($thefunction, func_get_args());
}
$GLOBALS['thefunction'] = function() {
echo 'foo';
};
}
если у вас есть условие проверки, которое позволяет вам переписать код:
// somewhere in your code
if (<replacethefunctioncondition>) {
$GLOBALS['thefunction'] = function() {
echo 'bar';
};
}
если вы перегружаете другой, включите, который будет загружен до или после исходного файла:
// includes/custom_functions.php
if (! function_exists('the_function')) {
function the_function() {
global $thefunction;
return call_user_func_array($thefunction, func_get_args());
}
}
$GLOBALS['thefunction'] = function() {
echo 'bar';
};
Ответ 5
Нет. Но удаление определения функции глупо.
Либо вы определяете функцию по-разному в блоке else
, и необходимо, чтобы определение изменялось в зависимости от состояния программы, что делало ваш проект ближе и ближе к невозможному для отладки, или код, вызывающий эту функцию, сбой и сжигание, если бы это было сделано во время выполнения определить его невозможно.
Вы должны поместить эту функцию в класс:
class foo {
public function bar() {
if( /* some condition */ ) {
$this->baz();
} else {
$this->bazzer();
}
}
private function baz() {
/* if the if condition was met */
}
private function bazzer() {
/* if the if condition failed */
}
}
или, если вы хотите, чтобы одно только условие было проверено,
class foo {
private $bar_function = NULL;
public function __construct() {
if( /* some condition */ ) {
$this->bar_function = baz;
} else {
$this->bar_function = bazzer;
}
}
public function bar() {
$this->$bar_function();
}
...
Я не знаю, что вы пытаетесь сделать или почему вы хотите удалить определение функции, но, надеюсь, это может помочь вам сделать это более чистым способом.
Ответ 6
Я тоже вижу очень мало причин, чтобы функция "живая" или "не живая" в зависимости от состояния, но чтобы ответить на вопрос, это вроде возможно использование анонимных функций. @Gordon уже рассказал, как это делается. Начиная с PHP 5.3.0, вы также можете использовать анонимные функции следующим образом. (Функциональная разница с подходом create_function()
отсутствует.)
if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc())
{
$stripslashes_deep = function($value)
{
return is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
}
$_POST = array_map($stripslashes_deep, $_POST);
$_GET = array_map($stripslashes_deep, $_GET);
$_COOKIE = array_map($stripslashes_deep, $_COOKIE);
$_REQUEST = array_map($stripslashes_deep, $_REQUEST);
unset ($stripslashes_deep);
}
Ответ 7
Это невозможно без runkit_function_remove. Runkit - это расширение, предназначенное для выполнения таких действий.
Вы уверены, что функция сохраняется после завершения if-блока? Я бы подумал, что если бы он был определен в if-блоке, то он стал бы недоступен впоследствии.
Ответ 8
Я согласен с Аликс. Однако вы можете избежать вызова функции из любого места, избавляясь от функции. Перепишите рекурсивную функцию как итеративный цикл. У вас будет дополнительное преимущество сокращения использования памяти.
UPDATE: с вашим примером кода, я не уверен, почему вы чувствуете необходимость предотвратить повторное вызов этой функции. Он просто форматирует некоторые строки в массиве рекурсивно... опять же, вы можете сделать это итеративно и не иметь функции в первую очередь. В противном случае просто оставьте его и не вызывайте его снова.
Ответ 9
Может быть другое решение для замены функций путем изменения их использования следующим образом:
function function1(){
echo "1";
}
function function2(){
echo "2";
}
if(FUNCTION_CHOOSER_CONDITION) $the_function = "function2";
else $the_function="function1";
$the_function(); // displays 2 if condition is TRUE;
Возможно, это не для вас в этой ситуации, так как вы хотели уничтожить функцию, но я, хотя это подходящее место, чтобы говорить о замене функций.