Usa parámetros variables en tus funciones con wp_parse_args (WordPress Tips)

wp_parse_args() es una función de WordPress que nos permite contar con tres interesantes beneficios al escribir una función:

  • poder aceptar una cantidad variable de argumentos, sin importar el orden en que estén declarados y además con la posibilidad de aumentar los argumentos que la función recibe sin tener problemas de compatibilidad
  • aceptar argumentos como una cadena de consulta(query string)o como array
  • poder dotar de opciones predeterminadas para cada uno de los argumentos de la función, de modo que la mayoría de las veces sólo debamos indicar un par de variaciones para utilizarla efectivamente

Según el Codex: se usa a través de WordPress para evitar tener que preocuparse sobre la lógica de las opciones predeterminadas y los inputs, y produce un patrón estable para pasar argumentos.

Aprovecharlo en tus funciones es muy sencillo. Veamos algunos ejemplos.

Supongamos que queremos tener una función muy sencilla para mostrar la fecha actual, pero a la que queremos dar algo de flexibilidad para, por ejemplo, poder mostrar otras fechas que le podamos indicar como parámetro.

function show_date( $args = null ){
	$s = wp_parse_args( $args, array(
		'format'    => 'j F Y',
		'date'      => time(),
		'echo'      => false,
		'class'     => 'date-now',
		'translate' => true
	) );
	$out = '<span class="'. esc_attr( $s['class'] ) .'">';
	if ( $s['translate'] ) {
		// si queremos obtener la fecha en el idioma de la instalación de
		// WordPress, utilizaremos la función date_i18n()
		$out .= date_i18n( $s['format'], $s['date'] );
	} else {
		$out .= date( $s['format'], $s['date'] );
	}
	$out .= '</span>';
	if ( $s['echo'] ) echo $out;
	else return $out;
}

En este caso, la mayoría de las veces podríamos utilizar la función llamando simplemente a show_date( ), sin ningún parámetro (ni siquiera $args es requerido), pero en otros casos contamos con una buena cantidad de opciones para poder reutilizar la función en distintos contextos, por ejemplo: show_date('echo=0&class=otra-clase').

En este caso, dado que podríamos utilizar la función sin indicar ningún parámetro, en la definición de la función $args es igual a null, ya que wp_parse_args() espera recibir exactamente dos parámetros: los argumentos que la función está recibiendo ($args) en forma de array o query string, y un array de valores predeterminados.

Por supuesto, también se puede dar el caso que tu función necesite recibir argumentos para hacer lo suyo; en ese caso podrías declarar esos argumentos de forma separada a los que trabajará wp_parse_args.

Por ejemplo, una función que suelo utilizar para construir select:

/**
 * Create an HTML select
 * @param array $els Array with the select options, the array key is the "value" attribute that will be sent with the form and the array value is the option text
 * @param array|string $args Select attribute options, selected items and other arguments
 * @return string Select
 * @author Felipe Lavín <felipe@yukei.net>
 **/
function create_select($els, $args){
	$s = wp_parse_args($args, array(
		'id'               => null,
		'echo'             => false,
		'size'             => null,
		'class'            => null,
		'style'            => null,
		'multiple'         => false,
		'selected'         => null,
		'__checkboxes'     => true,
		'show_option_all'  => false,
		'show_option_none' => false,		
	));
	$multiple_values = ( is_array( $s['selected'] ) ) ? true : false;
	$do_cb = ( $s['__checkboxes'] && ( $multiple_values || $s['multiple'] ) ) ? true : false;
	$atts = ( $do_cb ) ? array('id', 'class', 'style') : array('id', 'name', 'class', 'size', 'multiple', 'style');
	$selected = $do_cb ? ' checked="checked"' : ' selected="selected"';	
	if ( !empty($els) ) {
		$out = ( $do_cb ) ? '<ul' : '<select';
		foreach($atts as $k){
			if ( !is_null( $s[$k] ) && $s[$k] !== false ) {
				switch($k):
					case 'multiple':
						$out .= ' multiple="multiple"';
						break;
					case 'name':
						$out .= $s['multiple'] ? ' name="'. $s['name'].'[]"' : ' name="'. $s['name'] .'"';
						break;
					default:
						$out .= ' '. $k .'="'. esc_attr( $s[$k] ) .'"';
				endswitch;
			}
		}
		$out .= '>';
		if (!$do_cb ) {
			// "none" could be empty, but "all" should be a string
			if ( $s['show_option_none'] !== false ) {
				if ( is_array( $s['show_option_none'] ) ) {
					$out .= '<option value="'. key( $s['show_option_none'] ) .'">'. current( $s['show_option_none'] ) .'</option>';
				} else {
					$out .=	'<option value="-1">'. $s['show_option_none'] .'</option>';
				}
			}
			if ( $s['show_option_all'] != false ) $out .= '<option value="_all">'. $s['show_option_all'] .'</option>';
		}
		foreach ( $els as $key => $val ) {
			if ( !$multiple_values ) $active = $s['selected'] == $key ? $selected : null;			
			else $active = in_array( $key, $s['selected'] ) ? $selected : null;
			
			// So we can directly pass query results
			if ( is_object( $val ) && isset( $val->post_title ) ) $val = $val->post_title;
			
			if ( $do_cb ) 
				$out .= '<li><label for="cb_'. $s['id'] .'_'. $key .'"><input'. $active .' type="checkbox" value="'. $key .'" name="'. $s['name'] .'[]" id="cb_'. $s['id'] .'_'. $key .'" /> '. $val .'</label></li>';
			else
				$out .= '<option value="'. $key .'"'. $active .'>'. $val .'</option>';
		}
		$out .= ( $do_cb ) ? '</ul>' : '</select>';
		if ( $s['echo'] ) echo $out;
		else return $out;
	} else {
		return false;
	}

En este caso los dos parámetros son requeridos: el primero porque… porque necesitas elementos para poder construir el select; y el segundo porque determina el output que produce la función.

Algunas notas para finalizar:

  • Al mezclar los argumentos recibidos con los predeterminados los primeros no se filtran, por lo que podrías pasar argumentos que no estén considerados en la misma función a la que se los estás pasando…
  • Por lo mismo, si dentro de tu función invocarás otras como query_posts, get_posts o crearás objetos de WP_Query es buena idea filtrar qué argumentos pasarán de un lado a otro.
  • Por otra parte esto te puede dar una gran flexibilidad si, por ejemplo, usas este patrón como parte de un constructor de clase

Ah, y por cierto, en jQuery puedes lograr algo bastante similar con jQuery.extend()