Передача параметров в php-функцию
05-01-2008Время чтения ~ 2 мин.PHP 44549
PHP-функция может принимать параметры, например так:
function f1( $arg1, $arg2 )
Предположим мы определили эту функцию и использовали её в своих скриптах. Но через какое-то время, решили добавить еще пару параметров, чтобы расширить возможности. Теперь, для того, чтобы обеспечить совместимость с предыдущими скриптами, для новых параметров нужно добавить значения по-умолчанию:
function f1( $arg1, $arg2, $arg3 = '', $arg4 = '' )
То есть теперь, если при вызове функции не указывать последние два аргумента, то им будут присвоены дефолтные значения: в нашем случае - пустая строка.
Проходит еще какое-то время и мы решаем добавить в нашу функцию еще и параметры для форматирования. Например html-элементы «до» и «после», class, шаблон форматирования и т.д. Получается, что параметров у нашей функии становится довольно много и пользоваться ей уже не совсем удобно. Тем более может возникнуть ситуация, когда указать нужно не все параметры, а скажем только 5-й и 8-й.
В таких случаях на помощь может прийти функция парсинга строки URL. То есть в функцию мы передаем только одну строчку, состоящую из нужных нам параметров, но оформленную по правилам URL:
function f1( $args = '' ) { parse_str($args, $r); ...
Пример вызова:
f1('b=555&a=777');
В результате в массиве $r окажется структура:
Array ( [a] => 777 [b] => 555 )
Казалось бы, что проблема решена: мы можем не изменяя объявления функции, менять её функционал. Но есть один нюанс. Связан он с тем, что строчка параметров должна передаваться по правилам URL-строки, где необходимо заменять служебные символы (кавычки, пробелы и т.д.) на их коды. Таким образом, перед тем, как выполнить parse_str, нужно выполнить парсинг строки.
Всё это не совсем удобно, но мы видим, что в результате нам всё равно нужно получить массив параметров. Так что нам мешает сразу передавать в параметре готовый массив?
Вот пример:
function f1($args = array()) { if ( is_array($args) ) $r = $args; else parse_str($args, $r); # контроль echo '<pre>'; print_r( $r ); echo '</pre>'; } # примеры вызова f1( array ('a' => '888') ); f1( 'b=555&a=777' );
Обратите внимание, что функция сама проверяет тип параметра (is_array). Если это не массив, то выполняется parse_str. Получается универсальный подход.
Однако это еще не всё. Как быть в случае, если в параметрах переданы не все значения? Тут есть два подхода.
Первый - это явно прописать проверку на существование значения и если его нет, то взять дефолтное:
if ( !isset($r['a']) ) $r['a'] = '1'; if ( !isset($r['b']) ) $r['b'] = '2';
Второй способ - это задать массив дефолтных значений, который мы просто объединяем с входящим:
$default = array( 'a' => '1', 'b' => '2', 'c' => '3', ); $r = array_merge($default, $r);
Теперь, при вызове:
f1( array ('a' => '888', 'c' => '999') );
Получим в $r:
Array ( [a] => 888 [b] => 2 [c] => 999 )
Как видите, все довольно просто и элегантно. :)
все эти кода мучают :smile:
По-моему, если возникают такие функции в программе - пора заняться рефакторингом... Это то же самое, что делать в базе данных текстовые поля типа string (text, напринципиально) и записывать туда xml - типа офигенно расширяемо.
зачем xml, просто сериализированный массив ,)
на самом деле тоже нравится такой подход и использую повсеместно (я о конечной функции f1 в посте) - порой даже не догадываешься какие еще могут пригодиться входные переменные, особенно если функция делает нечто глобальное...
А нахрена тогда у функции несколько параметров было делать. :) Если есть один универсальный. :)
Так оно всегда вначале кажется, что хватит пары параметров, а уже после вдруг понимаешь, что нужно еще парочка. Я приведу на примере WordPress'а. Есть функции вывода рубрик/ссылок/т.п. Если бы в них можно было бы передать параметры через массив, но не было бы ни проблемы совместимости, ни сложностей с оформлением. А сейчас же классы, элементы до/после жестко вбиты в функции. ИМХО - неудобно - первое же обновление и все заново приходится менять. Многие на этом накололись. :sad:
Ну, если честно - по проектированию Wordpress отнюдь не идеал. ;)
А вот появление таких функций - это просто плохо с точки зрения проектирования.
Кстати, если вывод ссылок оформить ввиде обьектов/классов - получилось бы гораздо удобнее.
В целом полезно, но позабавил вот это текст:
Учитывая, что parse_str можно перевести как парсинг_строки )))
Просто каламбур, так сказать)
Предпочитаю всё-таки передавать тогда либо ассоциативный массив, либо объект в функцию - кто знает как эта parse_str работает и как данные в ней теряются (и что делать когда в данных амперсанд) :lol:
Такой подход действительно полезен и удобен для всяких "настроечных" методов. В остальных случаях - надо думать. Что проще запоминать: порядок передачи аргументов (если больше 3 - уже фигово), ключи массива (+что там по умолчанию), или несколько разных методов (хорошо, если получается ясный логичный интерфейс, хотя часто найти таковой не удается).
Макс, а чем тебя не устраивает func_get_args() и родственные ей функции?
2cryonyx: Тем, что в таких случаях нужно перебирать входящие параметры по номерам. Как определить, что переданный параметр это «элемент "до"», а не «элемент "после"»?
Огромный пардон, что не очень по месту задаю вопрос - но уж очень припекло! Несколько часов мучался, искал способ, как убрать из сайдбара вордпресса какой-либо блок (скажем, dtree), на основании юзер-левела читающего. Ничего не нашёл((. Т.к. если в dtree указать, что пустые категории должны скрываться, и при этом применить disclose-secret плагин, то dtree сходит с ума, и показывает или только те папки, где нет скрываемых disclose-ом постов, или показывает сразу все, вне зависимости от уровня юзера. Так что выход один - просто убирать dtree целиком из сайдбара, если уровень пользователя меньше, скажем, второго. Но только как это сделать?((( Подозреваю, что нужно добавить в код что-то типа "иф юзер левел блаблабла...", но не кодер я((. Так что в целом типа прошу помощи, граждане посетители!!)
if (current_user_can('level_?')) { .... }
вместо level_?:
level_0 == subscriber
level_1 == contributor
level_2-level_4 == author
level_5-level_7 == editor
level_8-level_10 == administrator
ну или
if (current_user_can('%role%')) { .... }
где %role% что нибудь из: subscriber, contributor, author, editor или administrator
function f1( $arg1, $arg2, $arg3 = '', $arg4 = '')
Укажем четвертый, неуказывая третий:
f1('arg1', 'arg2', null, 'arg4')
Все просто и не стоит заморачиваться ;)
Ну в итоге может подобная практика вылиться в идею работы с методами как с некоторыми функциональными компонентами. На вход приходит большрой массив с данными, а результат такой же массив для шаблонизатора.
Сенк за статью .. как раз то что искал ... правда ответ н свой вопрос нашел в коментах: сенкс Dmitry Yashin.
null-то всё равно надо писать. а если он там не один?
в общем, если бы всё было так просто, Максим не стал бы заморачиваться ;)
В узко-специализированных местах выглядит вполне красиво, но как панацея от неё больше вреда, чем пользы.
Пятёрка за нестандартность мышления.
В MaxSite CMS это где-то применяется/будет применяться?