Llamadas dinámicas a funciones y métodos en PHP
En algunos lenguajes tenemos la opción de utilizar punteros a funciones o soluciones similares, que nos permiten montar funcionalidades que aportan gran flexibilidad a las aplicaciones, por ejemplo, en C disponemos de punteros a funciones puros, que constan de un puntero apuntando a la dirección en memoria de la función, y en .NET disponemos de delegados (delegate), que aportan seguridad en cuanto a que se hace una comprobación de los parámetros y de sus tipos en tiempo de compilación, reduciendo así en gran medida el peligro que tiene utilizar los punteros a funciones de C. Aparte de esto .NET dispone de otros mecanismos como remoting, que nos permiten acceder dinámicamente a los atributos, métodos y propiadedes de las clases y objetos.
En PHP no existen los punteros, pero al ser un lenguaje interpretado es sencillo ofrecer funcionalidades similares. Disponemos de dos opciones, una la más simple, basada en llamar a funciones o métodos a partir de una variable string que contenga el nombre de la función o método, lo cual se denomina en PHP funciones variables, y otra, basada en la utilización de algunas funciones específicas de PHP, que nos ofrecen algo más de pontencia que el caso anterior.
Llamadas con funciones variables
A continuación se muestra un par de ejemplos en los que se realiza la llamada dinámica a una función y al método de una instancia de una clase.
/** * Defino dos funciones */ function uno(){ echo "función uno"; } funcion dos(){ echo "función dos"; } /** * Defino una clase para utilizarla como ejemplo */ class Prueba{ function Prueba(){} function metodo1(){ echo "Prueba.metodo1"; } function metodo2(){ echo "Prueba.metodo2"; } } /** * Ejemplo de llamada dinámica a una función */ $fname = 'uno'; $fname(); /** * Ejemplo de llamada dinámica a un método de un objeto */ $mname = 'metodo1'; $prueba =& new Prueba(); $prueba->$mname(); |
En el primer ejemplo se hace una llamada dinámica a una función, para ello se crea una variable string con el nombre de la función a la que hay que llamar y a continuación se realiza la llamada con $fname()
. Como puede verse, para realizar la llamada utilizamos la variable que contiene el nombre de la función a la que queremos llamar y le añadimos los parétesis. En el caso de que quisiéramos pasarle parámetros, procederíamos igual que con cualquier otra función, poniéndolos simplemente entre los paréntesis.
En el segundo ejemplo, en el que llamamos al método de un objeto, el mecanismo es similar. Tras crear la instancia de la clase Prueba, realizamos la llamada al método tal como la haríamos habitualmente, pero cambiamos el nombre del método por la variable $mname
, que contiene el nombre del método que desemoas llamar.
Llamadas dinámicas con call_user_func
La forma explicada en el punto anterior tiene como principal inconveniente que tenemos que saber, a la hora de escribir el código, el número de parámetros a pasar a la función, lo que en algunos casos no es posible. Además adolece también del inconveniente de que en algunas situaciones no es posible utilizarlo por problemas de sintaxis. PHP dispone de un par de funciones que nos permiten resolver estos inconvenientes, call_user_func y call_user_func_params.
Mediante call_user_func podemos indicar un nombre de función y los parámetros que queremos pasarle, por ejemplo:
function saludo($nombre){ echo "Hola $nombre"; } call_user_func('saludo', 'pepe'); |
Como puede apreciarse, el primer parámetro corresponde al nombre de la función que queremos llamar y el segundo parámetro es el parámetro. Si tuviéramos más parámetros bastaría con ir añadiéndolos tras este segundo.
También es posible llamar a métodos de clases de objetos, para lo cual hay que utilizar como primer parámetro un array cuya posición 0 corresponda a una instancia de objeto y cuya posición 1 sea el nombre del método al que hay que llamar. El resto de parámetros pasados a call_user_func serán los parámetros que se enviarán a dicho método. Es posible llamar también a métodos estáticos indicando en la posición 0 el nombre de la clase en vez de una instancia de objeto. Es importante al realizar llamadas a métodos de objetos, que al indicar la instancia debemos anteponerle el carácter &
, ya que en caso contrario la llamada se realizaría sobre un objeto copia del indicado, debido a como trata PHP4 los objetos.
class Prueba{ function saludar($nombre){ echo "Hola $nombre"; } } // Llamada al método de una instancia $obj =& new Prueba(); call_user_func(array(&$obj, 'saludar'), 'pepe'); // Llamada a un método de forma estática call_user_func(array('Prueba', 'saludar'), 'pepe'); |
Un caso especial es cuando dentro de un método de una clase queremos llamar a otro método de la misma clase, pero para la misma instancia de objeto, no de forma estática. En el primer elemento del array no podremos indicar el nombre de la instancia, pero se puede realizar la llamada utilizando &$this
en su lugar.
Hay que tener en cuenta que los parámetros call_user_func pasa a la función o método, se envían por valor, por lo que pueden darse situaciones, sobre todo cuando trabajamos con objetos, en los que esta solución no nos valga. Para estos casos disponemos de la función call_user_func_array. El tratamiento del primer parámetro en este caso es idéntico a call_user_func, la diferencia radica en la forma de indicar los parámetros. En el caso anterior los parámetros se indicaban como parámetros independientes de la función, y en este caso se indican mediante un segundo parámetro de tipo array que los contiene a todos ellos.
$params = array('pepe'); call_user_func_array(array(&$obj, 'saludar'), $params); |
quiero un codigo en php que me diga la direccion ip de cada uno de mis pc (red lan) si lo tienes enviamelo a mi correo geiner_13@hotmail.com se lo agradesco
Hola Patxi! buenos dias. Amigo, queria preguntarte cómo podría hacer para manejar punteros a través de las funciones que expones para establecer un árbol, porque básicamente en todos los libros que tengo de estructuras de datos, los arboles se manejan con punteros, y estoy claro en que los punteros no existen en PHP. Espero tu respuesta, gracias!!!
Hola Jean Piero. Como bien dices, lo más habitual es utilizar punteros para implementar las estructuras de árbol, pero existen otros mecanismos como por ejemplo implementarlos en un array. Seguramente tendrás información de los algoritmos concretos y sus ventajas/inconvenientes en los libros habituales de estructuras de datos.
Por otra parte en PHP quizás lo más sencillo sea utilizar arrays anidados. Dado que no hay comprobación de tipos y que los arrays no necesitan ser creados con un tamaño inicial, puedes partir de un array con dos elementos y de cada uno de los elementos colgar otro array, y así sucesivamente para crear un árbol binario. Quizás dentro de cada posición del array podrías almacenar otro array con dos valores, uno con el dato del nodo y otro con el array que contenga las subramas. Modificar los algoritmos habituales para el manejo de esta estructura debería ser trivial.
Por último, si tienes soltura con orientación a objetos y sus peculiaridades en PHP (http://www.eslomas.com/index.php/archives/2006/01/09/programacion-orientada-a-objetos-en-php4/), podrías plantearte algo más estructurado creando un clase nodo con los atributos de cada nodo y los métodos de acceso a las subramas.
Hola Patxi, necesito un poco de ayuda…
He encontrado este programa que pasa un .pdf a texto llano (justo lo que yo necesito). He creado un proyecto y un archivo php en eclipse y he copiado lo siguiente:
$fname = ‘pdf2txt’;
$fname();
function pdf2txt($filename){
$data = getFileData($filename);
$s=strpos($data,»%»)+1;
$version=substr($data,$s,strpos($data,»%»,$s)-1);
if(substr_count($version,»PDF-1.2″)==0)
return handleV3($data);
else
return handleV2($data);
}
// handles the verson 1.2
function handleV2($data){
// grab objects and then grab their contents (chunks)
$a_obj = getDataArray($data,»obj»,»endobj»);
foreach($a_obj as $obj){
$a_filter = getDataArray($obj,»>»);
$j = 0;
if (is_array($a_filter)){
$j++;
$a_chunks[$j][«filter»] = $a_filter[0];
$a_data = getDataArray($obj,»stream\r\n»,»endstream»);
if (is_array($a_data)){
$a_chunks[$j][«data»] = substr($a_data[0],
strlen(«stream\r\n»),
strlen($a_data[0])-strlen(«stream\r\n»)-strlen(«endstream»));
}
}
}
// decode the chunks
foreach($a_chunks as $chunk){
// look at each chunk and decide how to decode it – by looking at the contents of the filter
$a_filter = split(«/»,$chunk[«filter»]);
if ($chunk[«data»]!=»»){
// look at the filter to find out which encoding has been used
if (substr($chunk[«filter»],»FlateDecode»)!==false){
$data =@ gzuncompress($chunk[«data»]);
if (trim($data)!=»»){
$result_data .= ps2txt($data);
} else {
//$result_data .= «x»;
}
}
}
}
return $result_data;
}
//handles versions >1.2
function handleV3($data){
// grab objects and then grab their contents (chunks)
$a_obj = getDataArray($data,»obj»,»endobj»);
$result_data=»»;
foreach($a_obj as $obj){
//check if it a string
if(substr_count($obj,»/GS1″)>0){
//the strings are between ( and )
preg_match_all(«|\((.*?)\)|»,$obj,$field,PREG_SET_ORDER);
if(is_array($field))
foreach($field as $data)
$result_data.=$data[1];
}
}
return $result_data;
}
function ps2txt($ps_data){
$result = «»;
$a_data = getDataArray($ps_data,»[«,»]»);
if (is_array($a_data)){
foreach ($a_data as $ps_text){
$a_text = getDataArray($ps_text,»(«,»)»);
if (is_array($a_text)){
foreach ($a_text as $text){
$result .= substr($text,1,strlen($text)-2);
}
}
}
} else {
// the data may just be in raw format (outside of [] tags)
$a_text = getDataArray($ps_data,»(«,»)»);
if (is_array($a_text)){
foreach ($a_text as $text){
$result .= substr($text,1,strlen($text)-2);
}
}
}
return $result;
}
function getFileData($filename){
$handle = fopen($filename,»rb»);
$data = fread($handle, filesize($filename));
fclose($handle);
return $data;
}
function getDataArray($data,$start_word,$end_word){
$start = 0;
$end = 0;
$a_result = 0;
unset($a_result);
while ($start!==false && $end!==false){
$start = strpos($data,$start_word,$end);
if ($start!==false){
$end = strpos($data,$end_word,$start);
if ($end!==false){
// data is between start and end
$a_result[] = substr($data,$start,$end-$start+strlen($end_word));
}
}
}
return $a_result;
}
?>
Ejecuto y me aparece una IOException:
CreateProcess: c:\apache\php\php -l -f D:/Practicas/practicaPDM/pdf2txt.php error=3
Sabrias decirme que es lo que pasa?? me falta algo en la configuración o es en el código??
No he programado nunca en php y no se como se introduce el pdf que quiero que analice, yo programo en java y sería el equivalente a un main con una llamada a la función function pdf2txt($filename) pasandole el archivo en cuestión…¿como se hace eso?
Espero q puedas ayudarme, estoy perdida!!
Muchas gracias.
Las dos primeras lineas:
$fname = ‘pdf2txt’;
$fname();
no venian el el codigo, las he puesto yo para probar…seguro que no valen.
saludos
creo que llego 4 años tarde a este post
parece que falta el tag <php? al principio del fichero
Wow! este ejemplo está genial! Saludos Patxi!