¿Son los días sábado hábiles para el SENIAT?

Legalmente hablando, claro está y no, no es una pregunta retórica aunque vamos a revisar la historia. Si os parece una cuestión baladí mejor no sigáis leyendo pero en fin, aquí vamos.

Historia.

Ya bien lo dice la Biblia: el primer día de la semana es el domingo porque al séptimo día Dios descansó, es decir el día sábado, «sábado universal» que es lo que estamos viviendo hoy día. De hecho la religión judía aún sigue observando ese día como de descanso, en el país Israel es prácticamente sagrado desde el viernes en la noche. Por esa razón es que domingo es cero en el mundo de los ordenadores (en computación todo arranca desde cero, no vamos a perder ese valioso espacio de memoria). Continue reading

free as in freedom

Feliz Día del Software Libre 2017

De antemano os decimos que libre no quiere decir necesariamente que es gratis: una cosa es compartir y enseñar y otra muy distinta es que trabajemos gratis. Manejamos el Software Libre (así, con mayúsculas, porque es un ente) desde el punto de vista del servicio, de horas trabajadas, de que podemos automatizar las tareas, sí pero que la instalación, monitorización y mantenimiento (en ese orden) ocasiona “horas hombre” u “horas mujer” de trabajo duro y que conlleva una gran responsabilidad, éso es por lo que cobramos. Ahora bien, allá aquellos que piensan que trabajamos encerrados en “neveras” con nuestros ordenadores y que nos las pasamos “tuiteando”, bueno tratamos siempre de convencer, que es más importante que vencer; tercer sábado de septiembre ¡FELIZ DÍA DEL SOFTWARE LIBRE!

free as in freedom
free as in freedom

Continue reading

Feliz Día del Programador

Feliz Día del Programador 2017

«Feliz Día del Programador» desde el 2015 tenemos conocimiento que celebramos este día, sin distingo del lenguaje que usemos, tengamos que usar o que desarrollemos (los más avezados en la materia). Sin duda que los ordenadores es uno de los más grandes inventos de la humanidad, con la labor destacada de Alan Turing (con quien en vida se cometió una gran injusticia en su vida personal).

Feliz Día del Programador
Feliz Día del Programador

Modestía aparte: sobre nosotros recae un gran poder que conlleva una gran responsabilidad y aprovechamos el día para recordarnos nuestra dosis diaria de humildad, paciencia y empeño no solo en este día sino en todo el año para que los usuarios a quienes nos debemos (y nos dan el sustento económico en este, por ahora, sistema capitalista) sean los máximos beneficiados de nuestros frutos mentales ¡Y SI PROGRAMAMOS EN SOFTWARE LIBRE CONTRIBUIMOS AL PATRIMONIO TECNOLÓGICO DE LA HUMANIDAD! Continue reading

enlaces web

Enlaces de interés en el mundo de la programación en castellano

Pues en uno de esos momentos que uno dedica a “holgazanear” nos encontramos con el grato hallazgo de una página donde pone los enlaces web de sitios en castellano, los así llamados «blog técnicos». En cada pie de página de nuestro sitio hallaréis los sitios amigos a nosotros, pero en esta oportunidad os presentamos ¿a los futuros amigos? ¡Pues quien sabe, a lo mejor! Mejor anotarlos para luego irlos visitando uno por uno y poco a poco ¡Nuestra primera entrada que consiste de solo copiar y pegar! 😉

Enlaces relacionados con la programación.

Primero el blog de donde copiamos los enlaces: SYSADMIT.

Y ahora sí, copiar y pegar:

NombreSitio webCuenta en Twitter
José Luis Sánchez Borquehttps://mytcpip.com/
Juan Carlos Martínezhttp://www.martinezmartinez.eu/
Juan José Leónhttp://www.mellamansysadmin.es/
Luis Arandeshttp://www.navegandosistemas.es/
Josep Roshttp://www.josepros.com/
Ncorahttp://blog.ncora.com/
Jorge de la Cruzhttp://www.jorgedelacruz.es/
José Luis Gómezhttp://blog.e2h.net/
Roberto Tejerohttp://blogs.itpro.es/rtejero/
Blog de MegaCrackhttp://www.megacrack.es/
Juan Carlos Gilaranzhttp://www.mundosql.es/
Héctor Herrerohttp://www.bujarra.com/
Xavier Caballéhttp://www.pantallazos.es/
Ricard Ibáñezhttp://www.cenabit.com/
Gonz Gómezhttp://gonzgomez.blogspot.com/
Federico Cinallihttp://federicocinalli.com/
Leandro Ariel Leonhardthttp://www.blogvmware.com
Jordi Pratshttp://systemadmin.es/
Patricio Cerdahttp://patriciocerda.com
Luis Aitor Gonzálezhttp://luisaitorgonzalez.com
Gorka Izquierdohttp://www.aprendiendoavirtualizar.com/
Kyle Murleyhttps://kylemurley.wordpress.com/
Radek Strycharzhttp://greckzero.com/
Oscar Nogaleshttp://www.elblogtic.com/
Daniel Garciahttp://www.elinformatiku.es/
José Ramón Ramos Gatahttp://blog.ragasys.es/
Emiliano Marinihttp://www.linuxito.com/
Agustín Camposhttp://www.acampos.net/
David Hernándezhttp://www.daboblog.com/
Frikilinuxhttp://frikinux.blogspot.com.es/
Félix Merahttp://www.ingenieroinformatico.org
Roberto Garcíahttp://1gbdeinformacion.blogspot.com.es/
Santiago Buitragohttp://blog.asirsl.com/
Miguel Ángel Usechehttp://skatox.com/Skatox
Guillermo Sanchezhttp://www.itsanchez.com.ar/
Gaspar Fernándezhttp://totaki.com/poesiabinaria/
Blog Bitixhttps://picodotdev.github.io/blog-bitix/
Blog davidochobitshttp://www.ochobitshacenunbyte.com/
Toni de la Fuentehttp://www.blyx.com/
Ariel Antiguahttp://arielantigua.com/weblog/
Itxaso Larrinagahttps://mundoitblog.wordpress.com/
Jose Miguel Bernalhttp://jmbernal.com/
Rigoberto Sanchezhttp://www.elblogderigo.info/
Javier Peralhttps://www.sputniklab.net/
Alfonso Lópezhttp://www.zonademarrones.com/
Fernando Boladohttp://fernandoboladogarcia.blogspot.com.es/
José Domingohttp://www.josedomingo.org/
Aprendiz de sysadminhttp://aprendizdesysadmin.com
Pedro Gonzálezhttp://goigon.blogspot.com.es
Javier Carrascohttp://www.javiercarrasco.es/
Juan Antonio Izquierdohttp://www.administracion-linux.com/
La fatiga del geekhttp://nauj27.com/blog/
Oscar Navarretehttp://www.aratech.es/
La senda del adminhttp://www.lasendadeladmin.com
El blog del roothttp://www.elblogdelroot.com
La mar de todohttps://lamardetodo.com
Deividhttps://deividsdocs.wordpress.com/
Miguel Carretashttps://elblogdeladministrador.com
Yudel Cartahttp://lawebdelyuyo.eu/
Diego Maciashttp://dmaciasblog.com
David Romero Trejohttp://www.davidromerotrejo.com/
Gurú de la informáticahttp://www.gurudelainformatica.es/
Hackerdecabecerahttp://www.hackerdecabecera.com/
Cambiadesohttps://www.cambiadeso.es
Chema Alonsohttp://www.elladodelmal.com/@ChemaAlonso
Instituto de Enseñanza Secundaria "José María Pereda"http://materialestic.es/
I.E.S. Las Galletashttp://www.asirlasgalletas.com
Blog de Nadiahttps://soynadiablog.wordpress.com/@SoyNadia_
Luigino Bracci Roahttp://softwarelibresocialista.blogspot.com/@Lubrio
Pandora FMS Opensource monitoringhttp://blog.pandorafms.org/es@PandoraFMS
Xanadu distro GNU/linuxhttps://xombra.com/@Xombra
Linux gnu bloghttp://linuxgnublog.org/@LinuxGnuBlog
ComputerNewAgehttps://computernewage.com/
El Baúl del Programadorhttps://elbauldelprogramador.com/
Pijus Magnificushttps://www.pijusmagnificus.com/
SysAdmithttp://www.sysadmit.com/
José María Morales Vásquezhttps://blog.unlugarenelmundo.es/@JoseMaria
Daniel Rodríguez Martínezhttps://www.makigas.es/@Makigas
@danirod93
Ondiz Zarragahttps://ondahostil.wordpress.com/lo-que-he-aprendido/@Makigas
@OndaHostil
Fran (@Cyberhadesblog)
Rafael (@Tuxotron)
http://www.cyberhades.com/@Cyberhadesblog
Zagurhttps://portallinux.es/@T3rr0rZ0n3
Ignacio Brihuega Rodriguezhttps://www.fwhibbit.es/@fwhibbit_blog
Damián Amoedohttps://entreunosyceros.net/@EntreUnosYceros
José Antonio Dongil Sánchezhttps://elabismodenull.wordpress.com/@jdonsan
Raúl Caro Pastorinohttp://www.laguialinux.es/@LaGuiaLinux
Diego Córdobahttps://juncotic.com/blog/@d1cor
@juncotic
Agustín Montero Blascohttps://israelmgo.es/@israelmgo

Blog técnico en portugués:

Homelaber Brasil: http://homelaber.com.br/

Enlaces relacionados con el mundo forense.

Este es un paso más avanzado, para llegar acá debemos tener sólidos conocimientos aprendidos en los enlaces anteriores, acá tenemos un enlace cortesía de Manuel ” @CiberPoliES “Guerra, aquí la tenemos:

https://glider.es/webs-amigas-20-anos-despues/

Nota: allí también utilizan software privativo, el cual nosotros desaconsejamos ahora su uso, pero para combatir al crímen debemos echar mano de todos los recursos habidos y por haber; los cibercriminales siempre está al día con la tecnología.

Como véis ésa si que es una profesión muy ruda: saber programar en ambos mundos, libre y privativo, y lidiar con las fabricantes de hardware ¡menuda tarea! 😮


<Eso es todo, por ahora>.

 

eSpeak

eSpeak en Ubuntu 16 (y otros ambientes)

Yo renombraría esta utilería como “e-Speak”, un apócope de “Electronic Speak” porque para nosotros los castellanohablantes nos suena como un “espanglish“, pero bueno, así se llamaeSpeak” (así nos confunda) ¿Y qué hace este software? Pues simplemente nos “habla” el texto que querramos y es especialmente útil para añadir ayuda auditiva para los discapacitados visuales en nuestros programas. La imagen de los labios la he sacado de su propio sitio web, el programa es de los años 90 y las gráficas se necesitaban así, ligeras y toscas (por extensión a la licencia imagino la imagen será de dominio público, en todo caso siempre enlazamos dando crédito de autoría). Acá haremos unas simples líneas de comando (nosotros siempre usando solo la venta terminal ¡cuándo no es pascua en diciembre!) así que disfrutad de nuestra primera entrada de agosto de 2017.

eSpeak
eSpeak (página web)

Instalación.

Como reza el título estamos en un ambiente Ubuntu 16 así que las instrucciones serán para esta popular distribución (para Fedora seguid el enlace de las fuentes consultadas al final de este artículo). Abrimos una ventana terminal CTRL+ALT+T y tipeamos lo siguiente:

sudo apt-get install espeak
apt-get install espeak
apt-get install espeak

Una vez hayáis introducido vuestra contraseña y esperar que se descarguen los archivos de internet pues tendremos instalada nuestra nueva utilidad de deletreo. Probad, como todos los programas que por defecto vienen en idioma inglés, con el parámetro ” –help” para visualizar la ayuda disponible; leed y analizad antes de pasar al siguiente punto, las voces instaladas.

Otros sistemas operativos.

Lo bueno del software libre es que como tiene acceso al código fuente, podréis instalarlo vosotros mismos (con los ajustes y correcciones necesarios) en varios ambientes. En su repositorio en GitHub hay instrucciones detalladas al respecto para Windows, Mac, BSD y Android.

Voces instaladas.

Si nosotros hablaramos inglés, pues al diablo, todo sería más fácil, pero como hablamos “spanish” (¿habrá alguna aplicación que se llame “eSpanish“? 😉 ) procedemos a buscar las voces en nuestro idioma, mirad la figura:

espeak --voices | grep "spanish"
espeak --voices | grep "spanish"
espeak –voices | grep “spanish”

Acá vemos las dos letras claves son las del principio “es”, investigando más a fondo listamos las voces correspondientes al castellano:

espeak --voices="es"
espeak –voices=”es”

Ya vemos las voces disponibles para nuestro idioma pero en la documentación advierten que muchos idiomas son experimentales, así que solo esperad la perfección de Dios solamente, nosotros pobres mortales somos imperfectos (pero buscando siempre la excelencia).

Usando “eSpeak”.

La voz que vamos a utilizar entonces es “venezuala-mbrola-1” (para los angloparlantes el nombre de nuestro país es difícil de pronunciar, por eso lo escriben mal allí arriba en la opción, para ellos es “vene -pausa- zuuuuu-Ela”) así que podemos, por ejemplo, dar la siguiente orden en un guion para que nos avise que finalizó el proceso solicitado:

echo "Respaldo completado" | espeak -v es-vz

Así mismo podemos grabarlo en un archivo con la extensión .wav con el parámetro nemotécnico “-w“:

echo "Respaldo completado" | espeak -v es-vz -w respaldo_completado.wav

Otro uso es leer un archivo de texto que tengamos para ayudar a nuestros usuarios, pues “eSpeak” lo lee por nostros con el parámetro “-f” seguido del nombre del fichero:

espeak -f archivo.txt -v es-vz

Por último, si queremos que cada minuto nos de la hora de nuestro sistema, en castellano, lanzad lo siguiente:

while true; do date +%S | grep '00' && date +%H:%M | espeak -v es-vz; sleep 1; done

Si la voz “venezuala” está instalada, oiréis la hora en castellano y si es demasiado robotizada podemos jugar con los parámetros de amplitud, tono, etcétera, acá un ejemplo, experimentad vosotros mismos con otras cifras:

while true; do date +%S | grep '00' && date +%H:%M | espeak -v es-vz -s 160 -a 100 -g 4; sleep 1; done

Uso práctico.

Se nos ocurre que podemos agregar un trabajo por fecha “cron-job” para que revise un registro .log (archivos de texto con la extension .log) para que revise las últimas 10 líneas (valor por defecto de tail) y si encuentra un error nos advierta de manera auditiva:

tail -1F /tu/fichero/log | grep --line-buffered 'ERROR' | espeak  -v es-vz

Conclusión.

Ya no hay excusas para no ayudar a nuestros compatriotas discapacitados visualmente e incluso nos ayudamos nosotros mismos con recordatorios, podemos agregarlo a una aplicación o simplemente agregar una tarea programada para que ejecute un guion de un fichero donde agreguemos las cosas que queremos recordar ¿Qué os parece?

Fuentes consultadas:

En idioma inglés:

Chromium Web Browser Logo

Chromium extension tutorial

Por supuesto que no estamos hablando del elemento químico cromo (“chromium” en inglés) sino del popular navegador web de código abierto para todos. Lo que desarrollaremos es una “extensión”, una herramienta integrada al navegador y que tiene muy diferentes propósitos. Con fines didácticos haremos una extensión muy sencilla que nos redirige hacia una página web que nos devuelve simplemente nuestra propia dirección IPv4, ¡manos al teclado!

¿Qué es una extensión para Chromium?

Chromium Web Browser Logo

Como ya lo definimos al principio, una herramienta puede tener fines diversos, uno o más:

  • Pueden ser usadas para notificarnos si tenemos nuevos mensajes de correo electrónico por medio del pequeño icono que se coloca al lado de la barra de direcciones.
  • Si no hemos iniciado sesión en nuestra cuenta de correo-e basado en web, podemos hacer click en el icono para ir a la página donde introduciremos nuestras credenciales (¡Cuidado con las páginas falsas o “phishing”!).
  • Podría alertarnos si una palabra clave en particular aparece en la página que estemos visitando.
  • Podríamos programarla para NO cargar las imágenes de un sitio determinado -o todos los que visitemos-.
  • Tal vez sería útil para alertarnos de la modificación de una página (aunque para ellos hay lectores RSS específicos para ese trabajo) o su cambio de estado.
  • ¡Incluso, si somos aventureros, nos permitiría programar una calculadora!

Como ven los usos son muchos y variados, nuestra imaginación no tiene límites más sin embargo el entorno donde va a funcionar nuestra novel extensión sí que está supeditada al navegador Chromium. Esencialmente una extensión es una pequeñita página web que está alojada en un navegador web – en este caso Chromium – y que además puede acceder a ciertas funciones API (“Application Programming Interface“: conjunto de clases, métodos y funciones normalizadas).

La que nosotros vamos a realizar es bastante modesta y a medida que avanzemos la iremos profundizando más, pero la idea es introduciros en esta área de programación en un entorno de desarrollo bastante peculiar.

Creando el proyecto.

Pues que para comenzar crearemos en nuestro disco duro, donde gustemos, una carpeta con el nombre “KS7000_ConoceTuIPv4” – o el nombre que vosotros querais, sed de libre albedrío, lo importante es que aprendamos los conceptos y procedimientos-.

Primer componente de nuestra extensión en Chromium.

Como somos inclinados al dibujo técnico más que al dibujo artístico (¡Ea, que por allí viene nuestro tutorial sobre Inkscape!) lo que hicimos fue redimensionar un logotipo en formato PNG (que ya teníamos rodando por internet) a un tamaño de 16 por 16 píxeles ya que necesitados una pequeña interfaz gráfica para el botón donde nuestros usuarios harán click y abrirán nuestro trabajo. Cuando “subamos” – y especifiquemos- este icono a Chromium no tendremos que programar línea alguna: ya el navegador sabrá qué hacer al evento de hacer click sobre nuestra pequeña obra de arte microscópica (935 bytes “pesa” el fichero):

KS7000_ConoceTuIPv4 (icono)

Precisamente queremos hacer hincapié en este punto: esa imagen pequeñita que vamos a especificar ya Chromium le añade el evento click para cargar y ejecutar los siguientes componentes que vamos a crear, eso es, en sí, una Interfaz de Programación de Aplicaciones “API” -normalizadas en un ambiente determinado (Chromium, en este caso) -.

Creando un archivo de manifiesto.

Un manifiesto es simplemente una declaración de propósitos o programas. Para este caso es un fichero llamado manifest.json y por su extensión deducireis que está en lenguaje JSON: JavaScript Object Notation, el cual permite el intercambio de información entre cliente y servidor. Aunque está basado en la norma ECMA-262 3ª edición de 1999 nosotros consideramos que es una norma de facto. En todo caso no representa mayor misterio, tal como lo es XML, el JSON (a menos que guarde datos binarios) es plenamente legible tanto a humanos como a máquinas, pasemos a analizar su estructura mínima necesaria:

{
  "manifest_version": 2,
  "name": "KS7000_ConoceTuIPv4",
  "version": "1.0",
}

Explicado línea por línea, los tres primeros elementos son obligatorios:

  1. “manifest_version”: toma el valor 2 -sin comillas- para indicar que es a partir de la versión 18 de Chromium que funcionará la extensión. Al momento de escribir este reportaje vamos por la versión 59 así que ya sabeis que la versión 1 está obsoleta e inútil, de hecho. Notad cada coma para indicar separación de componentes, lo colocamos antes del fin de línea y retorno de carro.
  2. “name”: con hasta un máximo de 45 caracteres, es como identificaremos nuestra extensión y tratad que sea bien original y único si es que quereis algún día hacerlo famoso por internet y evitar malentendidos con otros programadores o incluso poderosas corporaciones.
  3. “version”: pues la numeración que utilizemos para identificar los cambios que le hagamos, y aunque está entrecomillado no os equivoquéis: son cuatro enteros de ntreo cero y 65535 separados por puntos y sirven para identificar – y actualizar a nuestros usuarios si hacemos una mejora -. En nuestro ejemplo colocamos “1.0” ¿Dónde diablos están los otros dos números faltantes? Pues en realidad nuestra versión es “1.0.0.0”, tened mucho cuidado al numerar porque como dijimos se usa para comparar y actualizar, de ser necesario, a nuestros usuarios.

Más adelante ahondaremos en otras propiedades que deberíamos agregar y que implican otros ficheros en nuestra mini aplicación, per antes de finalizar esta sección os contamos que hay otras propiedades interesantes:

  • “description”: la descripción de lo que realiza nuestra extensión, para lo que sirve, sed breve y conciso; entrecomillad.
  • “short_name“: que soporta hasta 12 caracteres y es especialmente útil para titular las pestañas y el lanzador de aplicaciones (en teoría).
  • “version_name”: un nombre no necesariamente numérica, si queremos colocarle un nombre afectuoso, deberiamos tratar de que sea único.

Así las cosas nuestro archivo de manifiesto va quedando como sigue:

{
  "manifest_version": 2,
  "name": "KS7000_ConoceTuIPv4",
  "short_name": "KS7000_IP",
  "description": "Esta extensión os permitirá saber vuestra dirección IPv4.",
  "version": "1.0",
  "version_name": "TuIPv4",
}

¿Acciones de página o acciones de navegador?

Aunque dijimos que Chromium sabría qué hacer con nuestra extensión, esto no es totalmente cierto ya que en esta API desarrollaron dos aciones principales de la cual debemos especificar que nuestra extensión realize alguna de las dos (o ninguna: si es ninguna pues no obtenemos ningún resultado, es una extensión inútil).

  • Acción de navegador: se ejecutará en cualquier pestaña y paǵina que abramos y permite que nuestros usuarios interactuen con nuestra extensión (ejecutar una orden o cambiar las preferencias, por ejemplo).
  • Acción de página: es un icono que indica un estado o condición a la página que estamos visitando (¿recuerdan que propusimos, al principio, una extensión que buesque y notifique la existencia de una palabra clave? pues eso, se puede activar -o no- si esa palabra clave está presente). Esta acción NO permite interactuar con los usuarios, es más bien informativa.

Nosotros usaremos la acción de navegador para lo cual debemos escribir:

"browser_action": {}

y colocamos entre los corchetes los elementos que queremos especificar y que a continuación especificamos:

 "default_icon": "KS7000_ConoceTuIPv4.png",
 "default_popup": "aviso.html"

La primera línea indicamos que utilice el ícono que os mostramos al principo, pero !ah,caramba! ¿habiais dicho que Chromium sabrá que hacer por nosotros? pues aquí el detalle no es culpa de Chromium ya que debido a los múltiples dispositivos (hardware) que pueden ejecutar este navegador web son muy diversos, los tamaños de pantalla son muy diversos también así que debemos -en teoría- hacer íconos de diferentes tamaños Y CHROMIUM SE ENCARGARÁ DE MOSTRAR EL QUE SEA DEL TAMAÑO ADECUADO AL DISPOSITIVO y tal como prometimos nos “harán” el trabajo a nosotros los programadores.

Acá nos quedaremos con un solo ícono, queda para vosotros estudies cómo especificar más iconos. La segunda línea establece cuál archivo .html queremos mostrar cuando el usuario haga click, y de nuevo es cuestión de tener variedad de opciones ya que podemos hacer varios ficheros .html (según la complejidad de nuestra extensión) pero debemos decirle al navegador cual queremos que muestre primero, así solo sea una la que “subamos” ¡NO ESTAMOS PROGRAMANDO EN PYTHON, TAMPOCO ESPERÉIS MILAGROS!

Archivo de manifiesto, versión final, debidamente indentado.

{
  "manifest_version": 2,
  "name": "KS7000_ConoceTuIPv4",
  "short_name": "KS7000_IP",
  "description": "Esta extensión os permitirá saber vuestra dirección IPv4.",
  "version": "1.0",
  "version_name": "TuIPv4",

  "browser_action": {
    "default_icon": "KS7000_ConoceTuIPv4.png",
    "default_popup": "aviso.html"
  } 
}

Elemento emergente de interfaz.

Ya sabemos que nos falta ahora un elemento, el “default_popup” al cual le asignamos el nombre “aviso.html” y como este tema no es un tutorial de HTML5 pues de una os colocamos el código del fichero de marras:

<!doctype html system "about:legacy-compat">
<html lang="es-419">
  <head>
    <meta charset="UTF-8">
    <title>KS7000</title>
  </head>
  <body>
    <h1>KS7000_ConoceTuIPv4</h1>
    <a href="http://www.soporteweb.com/" target="_blank">Haz click para saber saber vuestra dirección IPv4</a>
  </body>
</html>

“Subiendo” nuestra extensión.

Pues tanto como “subir” no será, no vamos a publicar nada por internet (aún no, por lo menos). Para nuestro ejemplo estamos ejecutando Ubuntu 16 64 bits al cual le instalamos Chromium 59 y en la barra de direcciones escribimos lo siguiente:

chrome://extensions/

Luego presionamos Intro o Enter y veremos algo muy parecido a esto:

Chromium instalar y configurar extensiones
Chromium instalar y configurar extensiones

En letras verdes os indicamos dónde “clickar” para activar el modo de programador, se os habilitan tres botones pero el primero de izquierda a derecha es el que nos interesa, el que pone “CARGAR EXTENSIÓN SIN EMPAQUETAR” ya que le vamos simplemente a indicar la carpeta que a tal efecto creamos al comenzar este tutorial. Dice que es “sin empaquetar” porque para publicar nuestra extensión en internet debemos comprimirla en un formato específico y con una herramienta específica que no solamente ahorra tamaño sino que también agrega seguridad, pero no es tema de este tutorial, allí teneis los enlaces si quereis estudiarlos a fondo.

KS7000_ConoceTuIPv4 instalado en Chromium
KS7000_ConoceTuIPv4 instalado en Chromium

Así veremos cuando está instalada nuestra extensión, notad el icono al finalizar la barra de direcciones al cual al hacerle click nos muestra lo siguiente:

KS7000_ConoceTuIPv4 ventana emergente
KS7000_ConoceTuIPv4 ventana emergente

En esa ventana pequeñita es que se “ejecuta” nuestra página web con el enlace deseado, pues bueno, click y listo, se abre una pestaña nueva (ya que usamos el target “_blank”) y vamos a un dominio cuyo único trabajo es devolver la dirección pública IPv4 de nosotros los internautas. ¡Felicidades, hemos creado nuestra primera extensión!

Posibles problemas.

Seguimos insistiendo con las API‘s: si tenemos algún error en nuestro código, “una metida de pata”, el Chromium nos indicará y animará a seguir intentando; en este ejemplo a propósitos omitimos unas comillas de apertura, es decir, no la pusimos por pares y al cargar nos indica esto:

KS7000_ConoceTuIPv4 mensaje de error en comillas de apertura
KS7000_ConoceTuIPv4 mensaje de error en comillas de apertura

El mensaje indica que es un token desconocido, pues claro, no está bien delimitado el elemento y asume que queremos declarar un dichoso token: ¡todo esto por una simple comilla doble que omitimos!

Usando guiones JavaScript.

Recordemos que JavaScript lo podemos ubicar tanto dentro de un archivo html como en un archivo aparte js que sea llamado, desde luego, el fichero html. Ambos métodos son soportados por las extensiones en Chromium y vamos a revisar las dos opciones.

Guiones JavaScript internos.

Primero eliminamos la extensión que creamos en Chromium y vamos a agregar el siguiente guión JavaScript justo en la cabecera <head>:

 <script>alert("Aviso de JavaScript");</script>

A continuación reinstalamos desde nuestra consabida carpeta y hacemos click en el icono de la extensión instalada (si no os funciona, revisad de nuevo bien desde el principio de este artículo) y lograremos ver el siguiente aviso que os lo resaltamos en color verde:

KS7000_ConoceTuIPv4 JavaScript error
KS7000_ConoceTuIPv4 JavaScript error

Cuando hacemos click en diho botón veremos algo parecido a esto:

KS7000_ConoceTuIPv4 hash error
KS7000_ConoceTuIPv4 hash error

¿Qué ha sucedido aquí? Pues simple y llanamente que Chromium tiene ciertos requisitos de seguridad basados en recomendaciones de la W3C a fin de proteger la integridad de nuestro software. Para que se pueda ejecutar algún guión JavaScript insertado en un fichero HTML debemos calcular el hash (tenemos un tutorial que explica este tema) del fichero en cuestión en insertarlo en el manifiesto mediante un comando específico.

Para ello ya tenemos la mitad del trabajo hecho: el cálculo del hash lo hace Chromium sin siquiera pedirselo, así que lo aprovechamos para agregarlo en manifest.json y la idea es que al declararlo se obliga a compararlo con el generado al archivo HTML y si ambos coinciden se considera que no ha habido alteraciones a nuestro trabajo por parte de terceras personas con buenos o malos propósitos.

Debemos entonces incluir estas lineas en el manifiesto:

"content_security_policy": "default-src 'self' 'sha256-jLjrqnmi4O/pyY5/8e4eXAPbFQ6Vq8j76usKTBjmtb4='),

Recargamos con CTRL+R y repetimos el proceso, y ahora logramos ejecutar con éxito:

KS7000-ConoceTuIPv4 mensaje de JavaScript
KS7000-ConoceTuIPv4 mensaje de JavaScript

Para que tengamos presente la seguridad, si cambiamos un solo caracter de “aviso.html” inmediatamente Chromium dejará de ejecutar cualquier script que haya dentro del archivo. Para poder ejecutarlo de nuevo, debemos volver a actualizar el hash, pero surge la pregunta ¿y si el atacante también modifica el hash dentro del manifiesto? Si estamos en nuesstros propios ordenadores pues esto es más que dificil que suceda, un atacante no va a invertir tiempo y esfuerzo para malograr un solo ordenador, es lógico que busque atacar cientos o miles de computadoras así que el obejtivo sería un repositorio en internet y allí interviene lo que comentamos: para “subir” nuestra aplicación para se repartido por internet no solamente la debemos comprimir, SINO QUE TAMBIÉN LE TENEMOS QUE AGREGAR UN HASH AL ARCHIVO COMPRIMIDO (recordad la herramienta crxmake).

Para comprobar lo que afirmamos, solo basta modificar un solo caracter en el fichero aviso.html, nosotros le agregamos par de símbolos de admiración en el mensaje y aunque no afecte para absolutamente nada la seguridad de nuestro equipo de igual manera se muestra la línea que ha sido modificada (o, en la vida real “hackeada”), mirad:

KS7000_ConoceTuIPv4 modificado y el hash es distinto del registrado
KS7000_ConoceTuIPv4 modificado y el hash es distinto del registrado

Como nota curiosa, ya logramos que el guión se ejecute pero eso es solo lo que hará Chromium, es decir, el archivo aviso.html no será mostrado pero poco importa para nosotros porque simplemente estamos aprendiendo a programar, por ello nos olvidaremos de incluir una instrucción de este tipo que llamamos modal. Una ventana modal es aquella que se posesiona del enfoque de una aplicación y la única manera de continuar es obedecer la instrucción que ordena. Para que aprendamos los guiones que se guardan en un fichero aparte, externo a un fichero html, debemos presentar los conceptos de ejecuciones síncronas y asíncronas.

Guiones síncronos y asíncronos.

La principal diferencia entre ambos métodos estriba que mientras el método síncrono detiene su ejecución y espera el resultado o liberación de recursos, el proceso asíncrono se ejecuta por sí solo y va de manera independiente (interactue o no con el proceso que le dio origen). Se pudiera decir que ambos procesos “padre” e “hijo” deberían de alguna forma comunicarse y esto se debe hacer por medio del entorno de desarrollo o IDE, por sus iniciales en inglés de “Integrated Development Environment” o Ambiente de Desarrollo integrado, lo cual simplifica enormemente nuestro trabajo de programación ya que nos permite abstraernos en programar solamente sin importar el hardware o sistema operativo utilizado (si se quiere lo podríamos ver como “aislado por capas” como lo es el modelo OSIpara las redes de ordenadores).

Generando un proceso asíncrono.

Vamos entonces a modificar nuestro fichero “aviso.html” de la siguiente manera, resaltado en verde los cambios:

<!doctype html system "about:legacy-compat">
<html lang="es-419">
  <head>
    <meta charset="UTF-8">
    <title>KS7000</title>
  </head>
  <body>
    <h1>KS7000_ConoceTuIPv4</h1>
    <button id="conoceIPv4">¡Conoce tu dirección IPv4!</button>
    <p id="IPv4"></p>
    <script src="emergente.js"></script>  </body>
</html>

¡ IMPORTANTE ! : declarad vuestro guion externo en JavaScript DESPUÉS de los elementos, de lo contrario arrojará un error “null” indicando que los elementos no existen. Esto es así debido a que el analizador “parser” va de arriba hacia abajo, línea por línea, y el proceso asíncrono que lanzamos debe tener con qué trabajar. En este caso hay dos elementos que “pasaremos”: el botón identificado como “conoceIPv4” y el párrafo sin texto o “en blanco” identificado como”IPv4″.

Ahora vamos a crear nuestro elemento escrito en JavaScript. un guión que, por ahora, simplemente colocará un mensajito de texto en el párrafo identificado como “IPv4”:

document.getElementById("conoceIPv4").addEventListener("click", buscaIPv4);
  function buscaIPv4() {
    document.getElementById("IPv4").innerHTML = "vuestra IPv4 es:";
  }

Como veis es algo bien sencillo:

  • En la primera línea declaramos que al documento identificado como “conoceIPv4” le agregue un evento que esté en función de si el usuario hace click en el botón y que ejecute la función “buscaIPv4”.
  • A su vez la función buscaIPv4 POR AHORA lo único que hace es sustituir al elemento (párrafo) con el identificador “IPv4” y lo sustituya con el mensaje “Vuestra IPv4 es:”.

Al guardar y recargar con CTRL+R las extensiones instaladas va a volver a mostrarnos el mensaje de que los hash NO coinciden, copiamos el hash actualizado y lo pegamos en el fichero manifiest.json para volver a recargar y ejecutar, podréis observar algo muy parecido a esto:

KS7000_ConoceTuIPv4 mensaje de encabezado
KS7000_ConoceTuIPv4 mensaje de encabezado

¡LISTO, HEMOS CREADO UN PROCESO ASÍNCRONO! MUY FÁCIL, ¿CIERTO? 🤓

Creando

Comprobando compatibilidad en Mozilla Firefox.

Por medio de la cuenta Twitter de un desarrollador web de Mauritania tuvimos la excelente oportunidad de revisar cómo se desarrolla una extensión en Mozilla Firefox (versión 45 o superior) y además cómo preparse para presentarla al público, todo muy detallado y bien pensado.

Como Chromium y Firefox ambos comparten el entorno de programación chrome hace que compartan la misma estructura de datos pero se “llega” de diferente manera. Primero debemos introducir en la barra de direcciones el siguiente comando:

about:debugging

Presionamos la tecla intro y seleccionamos complementos y hacemos click en el botón “Cargar complementario temporario” (sí, suena extraño en castellano pero si queremos colaborar con las traducciones, pues las puertas están abiertas). Seleccionamos la carpeta con nuestro mini proyecto y listo ¡a comprobar!

KS7000_ConoceTuIPv4 ejecutado en Mozilla Firefox
KS7000_ConoceTuIPv4 ejecutado en Mozilla Firefox

Fuentes consultadas.

En idioma francés.

En idioma italiano.

En idioma inglés.

Calculando factoriales de números grandes

El día de ayer por medio del perfil de línea de tiempo del Doctor Juan González observamos el interesante artículo tutorial sobre Python publicado por Juan J. Merelo Guervós el cual incluye retos de programación. Ni tardo ni perezoso, a pesar de llegar cansado a mi hogar, y tras una vigorizante taza de café negro con leche de avena sin azúcar nos dimos a la tarea de practicar algunas cositas para mantener el cerebro en forma y al día con las novedades en programación.

Calculando el factorial de un número.

Definición.

No vamos a hacer esta entrada muy profunda, ya que debemos ir a trabajar para ganarnos el pan de cada día, directo y rápido: el factorial de un número viene expresado por la multiplicación sucesiva de sus antecesores, uno a uno, hasta llegar a uno y se denota por ” n! ” ; así, por ejemplo 5!= 5 x 4 x 3 x 2 x 1 = 120 (las equis indican multiplicación).

El reto.

Pues arroja el guante el sr. Merelo con una función recursiva escrita, por supuesto, en lenguaje Python para calcular un factorial y tras ensayo y error llegamos a una satisfactoria y muy parecida a la ya famosa y escrita por todos lados en internet (para ser honestos en algún momento vimos esta función pero no recordabamos… hasta que el buscador DuckDuckGo acudió en nuestra ayuda, un asistente tan capaz -en memoria- como todas las asistentes que tuvo Albert Einstein -que en paz descansen, Valentine Bargmann y el Doctor Einstein-).

Para ilustraros cómo funciona, aprovechando nuestro mundo tecnológico –y añorando el gis y el pizarrón- os colocamos un gif animado tomado de penjee.com, un sitio web dedicado también a la enseñanza de Python:

Función factorial animada tomada de penjee.com
Función factorial animada tomada de penjee.com
def factorial(num):
  if num == 1:
    return 1
  else:
    return num * factorial(num - 1)

Calculando el factorial de un número grande.

Factorial, fórmula y definición
Factorial, fórmula y definición

Hasta acá todo muy bien, pero el reto no finalizaba allí, una segunda pregunta era ¿qué tal calcula los números grandes? Como bien podéis imaginar, el multiplicar y multiplicar en ordenador pues en algún momento se nos agota la memoria suponíamos nosotros… pues que aquí está la pega del asunto, tras calcular el factorial 120! de manera fácil (en Wikipedia en inglés denotan que normalmente el máximo factorial que se puede calcular con un procesador de 64 bits en una variable de tipo entero es 20! –ya nosotros hemos comentado el poder de cómputo de los procesadores y sistemas operativos de 64 bits-) pues nos envalentonamos a ir “al infinito y más allá”.

python factorial.py con n = 120 (ciento veinte)
python factorial.py con n = 120 (ciento veinte)

Siendo así la cosa le establecemos a 1200 (un mil doscientos) a la función de marras y nos arrojó este lindísimo mensaje de error que os mostramos:

python factorial.py con n = 1200 (mil doscientos)
python factorial.py con n = 1200 (mil doscientos)

Pero leyendo nos percatamos que el problema no es el desbordamiento de memoria, no,  el desbordamiento es un error en la profundidad de recursión:

RecursionError: maximum recursion depth exceeded in comparison

El contrareto.

Ya esto se salía del reto impuesto, es un contrareto que nos presenta Python pues claro, en un entorno de programación uno debe siempre imponer límites considerando que aún no ha llegado la computación cuántica (y aún así ésta tiene límites en este universo). Indagando, y hay bastante material sobre el tema, llegamos a la librería sys encargada de manejar tales asuntos y de hecho el manual en línea de Pyton nos advierte:

Set the maximum depth of the Python interpreter stack to limit. This limit prevents infinite recursion from causing an overflow of the C stack and crashing Python. The highest possible limit is platform-dependent. A user may need to set the limit higher when she has a program that requires deep recursion and a platform that supports a higher limit. This should be done with care, because a too-high limit can lead to a crash.

Que traducimos al castellano:

Establece el límite de la profundidad máxima de la pila del interprete Python. Este límite previene una recursión infinita ocasionando un desbordamiento en la pila (del lenguaje) C y la consecuente falla de Python (Dios nos ampare que alguien crea que Python es falible†). El límite más alto posible es dependiente de la plataforma empleada. Un usuario puede necesitar ajustar el límite superior cuando tenga un programa que requiera una mayor recursión y la plataforma que lo soporta lo permita. Esto debería hacerse con cuidado porque un límite muy alto puede conducir a un fallo (Dios nos ampare que alguien crea que Python es falible†).

†(Nota del traductor en tono de sarcasmo).

Python recursion limit get and set
Python recursion limit get and set

O inventamos o erramos.

Don Simón Rodríguez: "Inventamos o erramos".
Don Simón Rodríguez: “Inventamos o erramos”.

Como nuestro insigne Don Simón Rordríguez nos legó, debemos inventar y a lo sumo fallar (y reintentar y así sucesivamente hasta lograr inventar) nosotros pensamos ¿Y si establecemos el límite superior de la pila de procedimientos (límite de recursión) precisamente de manera recursiva? 🤔

Pues acá vamos entonces:

import sys
def factorial(num):
  '''Return large factorial by Jimmy Olano Sayago
               (GNU General Public License v3.0) '''
  if num<=1:
    return 1
  else:
    sys.setrecursionlimit(sys.getrecursionlimit()+1)
    return num * factorial(num - 1)

file = open('large_factorial_result.txt', 'w')
cad = factorial.__doc__
file.write( cad )
file.write( ' factorial(21700): ' )
file.write( str( factorial( 21700) ) )
file.close

Al tener nosotros establecida la función, por tanteo y utilizando un procesador AMD A8-6600K APU (64 bits, 4 núcleos y 8 gigabytes en RAM) llegamos un valor de 21700! el cual ocupa 86400 digitos. Esencialmente el guion que hicimos basados en la clásica función recursiva de cálculo factorial es aumentar el límite de pila de Python de manera recursiva para luego abrir un archivo de texto y guardar allí el resultado.

El delicado tema de los typos de variables y la memoria disponible.

Consultamos muy diversos reportajes sobre el tema del manejo de la memoria y las variables pero el que fue revelador para nosotros es uno escrito hace más de once años pero que goza de plena vigencia: una comparación sobre cómo trabajan las variables los lenguajes C y Python.

Tiene su teoría y abunda su práctica y probamos los diferentes ejemplos y nos resultó en valore comprobatorios contra lo que se afirma en dicha entrada pero la conclusión es tan excelente que se las traemos en idioma inglés y la traducimos al castellano:

Python removes this confusion, there is only the integer object. Does it have any limits? Very early versions of Python had a limit that was later removed. The limits now are set by the amount of memory you have in your computer. If you want to create an astronomical integer 5,000 digits long, go ahead. Typing it or reading it will be the only problem! How does Python do all of this? It automatically manages the integer object, which is initially set to 32 bits for speed. If it exceeds 32 bits, then Python increases its size as needed up to the RAM limit.

La traducción hecha por nosotros:

Python elimina esta confusión (de variables), hay solo el objeto entero. ¿Tiene algún límite? Muy tempranas versiones de Python tuevieron un límite que luego más tarde fue removido. Los límites ahora son establecidos por el monto de memoria que usted tenga en su computadora. Si quiere crear un entero astronómico de 5 mil dígitos, ¡adelante! ¡Escribirlo o leerlo será el único problema! ¿Cómo Python hace todo esto posible? Pues automáticamente dirige el objeto entero, el cual inicialmente estaba configurada a 32 bits por motivos de velocidad. Si excede los 32 bits, entonces Python incrementa su tamaño tanto como se necesite hasta llegar al límite de la memoria RAM.

Así como lo podéis leer, en el año 2006 se consideraba “astronómico” una cifra de 5000 dígitos, ¡PUES HE AQUÍ QUE CALCULAMOS UN FACTORIAL CON 86400 DÍGITOS! Para nosotros es todo un logro digno de dedicarle una entrada y una pequeña nota en las Wikipedia(s).

Sometido a consideración por la comunidad de programación y comunidad matemática.

De inmediato subimos el guion y su resultado (utilizando el idioma inglés) a Gist mantenido en línea por GitHub.com (¡gracias!) y “tuiteado” con la etiqueta #Python y #1linepy tal como recomienda el sr. Merelo:

Experimento social.

Como experimeto social hemos cometido una torpeza en la fórmula cuando establecemos:

if num<=1:
  return 1

El objetivo es detectar cuántas personas realmente analizan nuestra propuesta y nos hacen llegar sus observaciones (se especula que el mismísimo Leonardo DaVinci codificaba mal sus planos a propósito por si caían en manos enemigas) ya que el cálculo de factoriales está supeditado al campo de los números positivos, ya sean reales o imaginarios (esto es así porque el cálculo factorial es clave en la determinación de combinaciones y permutas, todas ellas en el campo de los números positivos).

También hemos publicado en Wikipedia en inglés nuestra experiencia (y próximamente en castellano, francés, italiano, portugués, alemán y ruso en la medida de lo posible y de nuestro tiempo libre) para determinar cuales sociedades unidas por un idioma común aceptan o rechazan nuestra propuesta (cada idioma Wiki tiene sus wikipedistas que algunas veces nos han “echado para atrás” nuestras ediciones por diversos motivos variopintos).

 

Conclusión: no hemos llegado aún al infinito, menos más allá.

Una inquietud que tenemos es la gran cantidad de ceros al final del resultado y una cosa que nos tranquiliza es la serie de resultados hasta 20! que va aumentado la cantidad de ceros a la derecha. Lo lógico es que si estamos multiplicando pues “corremos la coma hacia la derecha” y se debe, desde luego, rellenar con ceros para ello.

Aún falta por determinar hasta qué punto alcanza nuestro hardware su límite, podremos hacer un ciclo “for” que arranque con este último valor de 21700! e irlo incrementando y guardando en archivos cuyos nombres tengan el valor calculado apara identificarlos hasta que simplemente falle Python, ¡PERO ESPEREN, HAY MÁS!

Pensamos que arrancando nuestro sistema operativo Ubuntu 16.04 LTS en modo terminal, sin cargar interfaz gráfica y “tumbando” los servicios diversos -Apache server, MySQL, etc.- y los más que se puedan de systemd ganaremos más memoria y compararemos resultados a ver si influye y si es así, cuánto influye, ¡SOMOS TODO OÍDOS A VUESTRAS IDEAS AL RESPECTO POR MEDIO DE NUESTRA CUENTA EN TWITTER @KS7000!

Fuentes consultadas.

En idioma castellano.

En idioma italiano.

En idioma inglés.

PHP Simple HTML DOM Parser

Para abrir el mes de junio seguimos el hilo en nuestros artículos que buscan difundir el conocimiento libre del Patrimonio Tecnológico de la Humanidad. En anterior oportunidad estudiamos PHP curl, una herramienta basada en cURL y por ende en libcurl (¡ea, esto último no lo mencionamos allá!). Como en ese caso obtuvimos un método para descargar páginas web enteras, incluso si hay que pasarle datos con el método POST y/o hay que introducir usuario y contraseña. Esta entrada busca extraer, analizar e incluso modificar dichos datos ¡vente con nosotros!

PHP Simple HTML DOM Parser.

Presentación.

Buscábamos una “parser” o analizador código HTML y aunque PHP tiene su modelo establecido nos decantamos por algo más fácil pero con el inconveniente que debemos apoyar la licencia de uso del Instituto Tecnológico de Massachusetts (“MIT license”) y que NO viene integrado al lenguaje PHP. Lo bueno es que apenas son 65037 bytes en un archivo con un guion escrito en lenguaje PHP para que funcione (la licencia nos obliga a distribuir completo los archivos de ejemplos y manuales, todos escritos en idioma inglés).

El analizador de HTML (y XML) que veremos y aprenderemos a usar tiene el pomposo nombre de “PHP Simple HTML DOM Parsercon todas las implicaciones que derivan de llamar “simple” al D.O.M. (“Document Object Model”).

Document Object Model.

El Modelo de Objeto Documento es una norma propuesta por el Consorcio World Wide Web o W3C que son quienes hacen las recomendaciones y sientan las bases, de facto, del Web creado por el Doctor Tim Berners-Lee. En dicho papel de trabajo, a nuestro modo de ver las cosas, se propone que una página web es un documento y todo lo que está contenido en ella está representada por nodos. Si nos ponemos a ver tiene sentido porque la norma HTML5 establece unas sub divisiones que a su vez contienen títulos, sub títulos, párrafos, listas, etc.

Este concepto es mucho más amplio porque contempla, aparte de propiedades, también métodos que por definición HTML5 no contempla y para respetar la neutralidad hacen de la vista gorda del JavaScript (¿definirán algún día al CSS para suplantarlo? Sí, los sabemos, nuestro pensamiento siempre es profano).

El DOM está estructurado sobre tres pilares básicos:

  • “Core DOM”: modelo normativo para todos los tipos de documentos.
  • “XML DOM”: para documentos XML (febrero, 1998).
  • “HTML DOM”: no menos importante, aunque aparezca en tercer lugar, para documentos HTML.

Autores.

La idea original proviene de José Solorzano quien comenzó un proyecto bautizado como “HTML Parser for PHP 4” (dale con esto de los nombres largos ¿será que estoy leyendo muchos artículos en inglés y me estoy contagiando? Porque el idioma castellano es bastante prolijo…) que como podemos ver funciona con PHP 4 y pues va a ser que ya está descontinuado (incluso tienen un avisito recomendando el proyecto que vemos hoy acá).

Basado en ello S.C. Chen (me578022@gmail.com) programó una versión actualizada y basada en DOM con ayuda de Yousuke Kumakura y publicada en SourceForge, un alojador de contenido de software de código abierto que alberga más de 430.000 proyectos (3,7 millones de usuarios y 42 millones de clientes). Solo sabemos que S.C Chen es famoso a nivel mundial por su ópera prima que trajimos en este momento a colación.

Core DOM.

(pronto desarrollaremos este tema).

XML DOM.

Para poder entender el HTML DOM debemos primero estudiar y aprender el XML DOM y a continuación haremos nuestro racionamiento y sustento.

Concepto de XML.

“eXtensible Markup Language” o Lenguaje de Marcado Extensible (lo siento por el castellano pero lo seguiremos identificando como XML dado su impacto de facto en el mundo entero) es un lenguaje que “describe” como hacer las cosas y que además se puede ampliar en cualquier momento conservando una retrocompatibilidad. Fue diseñado para almacenar y transportar datos y que, además, pudiera ser leído por ordenadores y humanos por igual.

Si queréis leer sobre nuestra disertación acerca de lo que es un lenguaje de marcado, os invitamos a leer nuestro tutorial en línea sobre HTML. XML es guardado en ficheros codificados, generalmente, en UTF-8 para lograr compatibilidad con los diversos idiomas del mundo, nuestro tutorial sobre HTML5 también habla  y describe al respecto en forma detallada.

Normas XML.

Hay unas normas destacadas que mencionaremos y que a futuro, en la medida que tengamos tiempo para ello, le publicaremos sus diferentes entradas en nuestro humilde blog.

  • XML AJAX
  • XML DOM
  • XML XPath
  • XML XSLT
  • XML XQuery
  • XML DTD
  • XML Schema
  • XML Services

¿Qué es y qué NO es XML?

Aparte de lo que ya definimos, hay otros detalles que decir acerca del XML: fue diseñado para ser autodescriptivo, ya que a diferencia del HTML tiene muy pocos “comandos” o etiquetas y el XML como tal no hace absolutamente nada por sí solo. Esa es entonces la principal diferencia con HTML, que se enfoca en presentar los datos mientras que XML se enfoca en transportar datos.

Decimos que se puede extender ya que nosotros mismo “inventamos” nuestros propias etiquetas, así que no tenemos limitación pero una vez hallamos agregado más etiquetas a una información preexistente ésta seguirá siendo completamente leída y manipulada por distintas aplicaciones sin ningún tipo de problema.

Por supuesto, hacemos la advertencia que hablamos de extender, no de contraer: al eliminar algún dato por supuesto que las aplicaciones ya no podrán realizar su trabajo completo, y en muchos casos se negarán a realizarlo de plano.

Elementos de un documento XML.

Un documento XML está constituido por un elemento raíz el cual tiene elemento ramas o nodos que a su vez pueden contener subelementos y así sucesivamente. Cada elemento a su vez puede contener atributos.

De manera obligatoria un documento XML debe tener un elemento raíz, y aunque un prólogo no es obligatorio ni forma parte del documento es muy recomendable agregarlo; siempre va al principio del documento.

La función del prólogo es denotar la versión XML del documento y, de manera adicional, la codificación de caracteres el cual ya dijimos UTF-8 es lo más recomendable. La sintaxis del prólogo comienza con un símbolo “menor que” o mejor dicho, un corchete angular de apertura, ya que no estamos hablando de matemáticas en este caso (pero ayuda muchísimo a entender el concepto de aperura y cierre). Debe cerrar con un símbolo “mayor que” o corchete angular de cierre. Además de los dos símbolos anteriores se necesitan dos signos de interrogación de cierre y los contenidos deben estar entrecomillados para lo cual recomendamos las comillas simples, esto nos ahorra problemas para cuando vayamos a programar. Veamos un ejemplo de prólogo:

<?xml version='1.0' encoding='UTF-8'?>

El prólogo es un caso especial, y ya dijimos que no forma parte del documento, por lo tanto tiene distintas reglas que las aplicadas a los elementos, los cuales pasamos a describir en la siguiente sección.

Sintaxis de los elementos XML.

  • Cada uno de los elementos debe tener su correspondiente cierre pero con una barra invertida, por ejemplo:
    • <elemento> texto </elemento>
  • Los nombres de los elementos distinguen mayúsculas de minúsculas por lo tanto <elemento> no es igual a <Elemento>.
  • Los nombres de los elementos deben comenzar con una letra o en su defecto con un guion bajo ” _ “.
  • Los nombres de los elementos no pueden comenzar con “XML” o sus combinaciones de mayúsculas y/o minúsculas.
  • Los nombres de los elementos no puden contener espacios (de hecho un espacio denota que comienza un atributo, por eso no pueden contener espacios).
  • Los subelementos deben estar correctamente anidados dentro de los elementos, ejemplo:
    • <elemento><subelemento>valor</subelemento><elemento>
  • Los atributos de los elementos deben estar entrecomillados (lo mismo que dijimo para el prólogo se aplica en este caso), ejemplo:
    • <elemento principal=’si’> dato </elemento>
  • Como tal vez hayan captado, hay unos caracteres especiales en los documentos XML que no podremos usar directamente porque se presta a confusión para los ordenadores a la hora de leer el documento XML por lo tanto debemos sustituirlos por otros caracteres si queremos usarlos como datos:
    • Corchete angular de apertura “<” lo sustituimos por “&lt;” (recordad “lt”= “less than”, menos que).
    • Corchete angular de apertura “>” lo sustituimos por “&gt;“(recordad “gt”= “greater than”, mayor que).
    • Ampersand o “et” (proviene del latín, como en “etcétera”): “&” lo sustituimos por “&amp;“.
    • Apostrofo ” ” lo susituimos por “&apos;“.
    • Comillas dobles ” ” las sustituimos por “&quot;” (“quotation mark”).
  • Si queremos o necesitamos dejar un comentario para nosotros los seres humanos y que sea ignorado por el ordenador hacemos lo mismo que en el lenguaje HTML, lo encerramos entre los siguientes signos:
    • <!– comentario –>
    • Nota: dentro de un comentario no podemos escribir dos guiones juntos ya que dos guiones juntos indican inicio y fin de comentario, así que se prestaría a confusión para los ordenadores.
  • En los documentos XML debemos tener en cuenta que todo espacio es considerado como tal, es decir, los espacios se conservan y son un dato en sí mismo y así será leído ya quedará de parte de la aplicación el cortar o comprimir espacios “innecesarios”. Este comentario lo hacemos porque los documentos XML pueden ser leídos -y editados- por nosotros los humanos y un espacio en blanco mal colocado de nuestra parte hará que la aplicación que va  a leer los datos le suceda una excepción pero nosotros no veamos el error al abrir el archivo nosotros mismos.
  • Parecerá una tontería pero el caracter que indica el final de una línea es el caracter LF o “Line Feed” OSEA al caracter ASCII 10 y debemos tener siempre presente que en el sistema operativo Windows se utiliza LF más CR (ASCII 10 y 13 respectivamente) y en los viejos sistemas operativos Mac solo CR.

Características de los elementos de un documento XML.

  • Un elemento comienza desde el primer corchete angular de apertura hasta el último corchete angualr de cierre.
  • Un elemento puede contener:
    • Nada (pues eso, vacío, ni siquiera un espacio -si tuviera un espacio no sería vacio-).
    • Ya que introducimos el concepto de elemento vacio -y esto debería estar en la sintaxis- un elemento tácitamente vacio lo podremos denotar de la siguiente manera y ambos son iguales y válidos:
      • <elemento />
      • <elemento></elemento>
    • Texto -datos, para los ordenadores-.
    • Otros elementos -recordad anidar correctamente-.
    • Cualquier combinación de los tres anteriores, las combinaciones son infinitas y por ende decimos que son extensibles -y retrocompatibles-.

Una breve aclaratoria sobre los atributos.

Hay que tener especial cuidado con los atributos que le coloquemos a los elementos. Para no caer en abstracciones les haremos un caso práctico: consideremos un elemento llamado “persona” y lo que eso significa para nosotros los castellanohablantes.

<?xml version='1.0' encoding='UTF-8'?>
<persona></persona>

Ahora consideremos colocarle un atributo de género femenino o masculino:

<?xml version='1.0' encoding='UTF-8'?>
<persona sexo='masculino'>
    <nombre>Pedro</nombre>
</persona>
<persona sexo='femenino'>
   <nombre>María</nombre>
</persona>

Como vemos podemos “cargar” en memoria de ordenador solamente los hombres -o las mujeres- pero la aplicación que lee dichoa rchivo debe estar programada, de manera previa, a buscar esos dos géneros solamente ¿Qué sucedería si surgiera un tercer tipo de sexo, como por ejemplo hermafrodita?

<?xml version='1.0' encoding='UTF-8'?>
<persona sexo='masculino'>
    <nombre>Pedro</nombre>
</persona>
<persona sexo='hermafrodita'>
 <nombre>Michelle</nombre>
</persona>
<persona sexo='femenino'>
   <nombre>María</nombre>
</persona>

No habría forma ni manera que la aplicación pudiera “ampliarse” para que leyera datos adicionales ampliados, así que es una mejor idea plantearlo de la siguiente manera:

<?xml version='1.0' encoding='UTF-8'?>
<persona>
    <sexo>masculino</sexo>
    <nombre>Pedro</nombre>
</persona>
<persona>
    <sexo>hermafrodita</sexo>
    <nombre>Michelle</nombre>
</persona>
<persona>
    <sexo>femenino</sexo>
    <nombre>María</nombre>
</persona>

Como veís, colocado de esta manera la aplicación puede ir generando una matriz con los distintos nuevos tipos de valores tal como lo hace con los valores en sí mismos. Aparte de esta ventaja debemos recordar también que los atributos NO pueden contener múltiples valores y tampoco pueden tener estructuras tipo árbol, entonces ¿para que diantres sirven los atributos? Antes de responder esta pregunta queremos dejar en claro algo más sobre los atributos: los atributos son elemento fuertemente tipados -como decimos en los lenguajes de programación-, son estructura rígidas que son difíciles de cambiar a futuro pero que debido a esto podremos tomar ventaja; veamos la siguiente sección.

Eliminando ambigüedades en el nombrado de los elementos.

Como ya bien sabemos los documentos XML nos sirven para almacenar datos y debido a que nosotros los seres humanos asignamos un nombre para todo en nuestro universo -y a medida que lo seguimos descubriendo- no siempre escogemos nombres únicos. Tomemos por caso la palabra “banco”: tal vez primero pensemos en una institución financiera pero es que también un “banco” es un objeto sobre el cual podemos sentarnos.

Si por alguna idea peregrina nos damos a la tarea de guardar datos de ambos conceptos en un mismo documento XML debemos hallar una manera de diferenciarlos para que no haya lugar a dudas. Para ello podemos -y debemos- asignar un prefijo para eliminar cualquier duda, pero ¿qué prefijo usaremos? Pongamos por caso que los bancos -instituciones financieras- le colocamos el prefijo “i:” y los bancos -objetos- los prefijamos con “o:” (notemos que no violan la sintaxis de nombres de elementos ya que comienzan con una letra, no comienzan con “xml” ni contiene espacios):

<?xml version='1.0' encoding='UTF-8'?>
<raíz>
    <i:bancos>
        <i:nombre>Banco de Venezuela</i:nombre>
        <i:nombre>Banco Mercantil</i:nombre>
        <i:nombre>Banco Bicentenario</i:nombre>
    </i:bancos>
    <o:bancos>
        <o:nombre>banco de madera</o:nombre>
        <o:nombre>banco de metal</o:nombre>
        <o:nombre>taburete</o:nombre>
    </o:bancos>
</raíz>

Rapidamente notamos, si fueramos un lector ajeno que recibiera este documento XML, ¿qué diablos significa ambos prefijos, aparte de separarlos sintacticamente? Bien podríamos colocar un comentario para hacer alusión a qué nos referimos (“i:”institución financiera, “o:”obejto), pero eso funciona solo para nosotros los humanos, los ordenadores obvian estas observaciones ¿qué otra solución podemos echar mano?

Espacios de nombres en XML: XMLNS.

Los espacios de nombres (“Name Spaces”) en XML (juntos serían XMLNS) es el concepto para definir los prefijos que querramos usar en cualquier documento XML. Este “Name Spaces” son unos atributos y como tales debemos colocarlos en el elemento “padre” con un valor bajo la forma de URI (Uniform Resource Identifier) que bien puede ser a su vez un URL (Uniform Resource Locator) o un URN (Uniform Resource Name).

Para este nuestro caso vamos a usar el modelo de comentario y el modelo de URL todo esto apuntando siempre a que sea un ser humano el que va a leer la información (y luego veremos el caso de si es un ordenador el que “procesará” la información):

<?xml version='1.0' encoding='UTF-8'?>
<!--
  prefijo 'i:' se refiere a instituciones financieras.
  prefijo 'o:' se refiere a muebles, objetos.
-->
<raíz>
    <i:bancos xmlns:i='https://es.wikipedia.org/wiki/Banco'>
        <i:nombre>Banco de Venezuela</i:nombre>
        <i:nombre>Banco Mercantil</i:nombre>
        <i:nombre>Banco Bicentenario</i:nombre>
    </i:bancos>
    <o:bancos xmlns='https://es.wikipedia.org/wiki/Banco_(mueble)'>
        <o:nombre>banco de madera</o:nombre>
        <o:nombre>banco de metal</o:nombre>
        <o:nombre>taburete</o:nombre>
    </o:bancos>
</raíz>

También podemos declarar dichos atributos en el elemento raíz para darle mayor claridad a nuestro código y es igualmente válido:

<raíz
    xmlns:i='https://es.wikipedia.org/wiki/Banco'
    xmlns='https://es.wikipedia.org/wiki/Banco_(mueble)'
>

Recordemos que los documentos XML permiten los espacios en blanco para precisamente indentar para clarificar, por ello este formato de elemento raíz.

HTML DOM

Fuentes consultadas.

En idioma castellano.

En idioma inglés.

PHP y cURL

Suena incoherente: usamos curl en nuestra línea de comandos frecuentemente para diversidad de tareas pero no sabíamos que el lenguaje PHP tiene su propia versión llamada, cómo no, curl. El asunto es cómo se implementa y qué podemos hacer, nosotros en la línea de comandos lo acompañamos en el trabajo con grep pero en PHP la cosa es un poco diferente, ¡vamos a averiguar cómo!

Introducción.

Exactamente cómo implementaron cURL en PHP no lo sabemos, tal vez PHP haga llamado directo al curl y recoja sus resultados, sirviendo entonces como intermediario solamente… No tiene sentido pero si analizamos que muchos sitios web compartidos con otros dominios tienen PHP pero a uno no lo dejan ingresar por la línea de comandos entonces ¡vaya que si vale la pena el trabajo intermedio!

Por otra parte puede ser que especulamos demasiado y los buenos desarrolladores de PHP tienen su propia versión… ¡pero recordad que trabajamos con Software Libre que permite la modificación y reutilización del código! Pero echemos una leve ojeada al comando curl.

Comando curl.

Ya sabemos como va es esto del software libre: hay más de 1400 colaboradores en curl y el proyecto está alojado en GitHub desde el año 2010; aceptan contribuciones bajo la forma de “pull requests”: en resumen uno copia y trabaja con esa copia privada modificandola -y mejorandola, por supuesto- y devolviendo el trabajo al original para su aprobación. No se garantiza que se acepte toda la propuesta, e incluso ellos podrán tomar ciertas cosas propuestas y otras no, o en el mejor de los casos todo vaya bien y nos convirtamos en el colaborador 1401… y así haremos nuestra contribución al Patrimonio Tecnológico de la Humanidad.

Pero ¿quiénes desarrollan curl? Daniel Stenberg -sueco, habitante de Estocolmo- es el principal desarrollador y comenzó el proyecto por allá en 1996 basado en el trabajo de un programador brasileño llamado Rafael Sagula. Esta sencilla herramienta herramienta carioca fue bautizada como httpget con unas cuantas centenares de líneas y Stenberg las amplió para liberarla en 1997 con el nuevo nombre de “HTTP proxy support”. Pero con el advenimiento de “nuevos” protocolos al proyecto tales como GOPHER y FTP pronto dejó de tomar sentido llamarlo solamente “HTTP”… así que ese mismo año vio la luz la versión número dos.

Para 1998 más protocolos y capacidades fueron agregados así que volvió a cambiar de nombre para la versión 4.0 -manteniendo la numeración de versiones- así que el 20 de marzo de ese año marca el nacimiento formal de curl. El nuevo nombre hace alusión al programa del lado del cliente, de allí la letra “c” inicial. Las otras tres letras os lo podéis imaginar ya: Uniform Resource Locator -URL- o Localizador de Recursos Normalizados.

El mismo Stenberg reconoce que hay muchos proyectos con el mismo nombre curl pero para la época que ellos lanzaron la versión cuatro no había (o conocían) de otros proyectos con el mismo nombre. Es por ello que nosotros al principio especulamos del origen de curl en el lenguaje PHP, pero es que ¡Incluso existe un curl desarrollado en software privativo! (no le haremos publicidad pues no nos pagan por ello, buscadlo vosotros mismos con DuckDuckGo). Tan famoso es que ya se considera como verbo en idioma inglés y muchos creen que es un protocolo pues lo tratan como tal pero ya sabemos que en realidad es una navaja suiza con muchísimas funciones.

curl para la línea de comandos.

En principio el comando curl fue desarrollado para scripts ya que sus entradas y salidas utilizan las ya famosas stdin y stdout. No ampliaremos más porque en este blog hallaréis información sobre su uso con la línea de comandos. El artículo que motivó la publicación de esta entrada está en el siguiente “tuit” y precisamente es para la línea de comandos -y pretendemos que corra en PHP curl-:

También hemos recopilado con el paso del tiempo otros usos notables del curl en la línea de comandos:

 

curl para su uso como librería.

A partir del año 2000 fueron desarrolladas, o mejor dicho, el código existente fue migrado completamente como librerías y a partir de allí se le hizo una interfaz de usuario para la línea de comandos. Está escrito en lenguaje C y aún hoy en día dichas bases siguen sustentando el proyecto con la ventaja que se puede reutilizar en otros proyectos y compilarlos todos juntos. Seguimos sospechando que precisamente eso hicieron en PHP pero esa es nuestra humilde opinión que como reafirmamos: en software libre este comportamiento es un honor a su filosofía de desarrollo: ejecutar, estudiar, distribuir, mejorar y redistribuir.

Versiones actuales y desarrolladores estrellas.

Nosotros en nuestro GNU/Linux Ubuntu 16 tenemos instalada la versión 7.40.00 pero en realidad la última versión estable es la 7.54.0.

Si bien son muchísimos los colaboradores, es justo mencionar los que más han contribuido en los últimos años de una manera marcada y constante:

  • Daniel Stenberg.
  • Steve Holme.
  • Jay Satiro.
  • Dan Fandrich.
  • Marc Hörsken.
  • Kamil Dudka.
  • Alessandro Ghedini.
  • Yang Tse.
  • Günter Knauf.
  • Tatsuhiro Tsujikawa.
  • Patrick Monnerat.
  • Nick Zitzmann.

Disipación de toda duda.

En este punto de nuestra investigación nuestras sospechas se hacen realidad: uno de los primero lenguajes en adoptar la librería curl es precisamente PHP el cual motoriza un 25% de las páginas web a nivel mundial. El extracto , en inglés, reposa en el libro electrónico -liberado con licencia “Creative Commons”- “Everything about curl“:

The libcurl binding for PHP was one of, if not the, first bindings for libcurl to really catch on and get used widely. It quickly got adopted as a default way for PHP users to transfer data and as it has now been in that position for over a decade and PHP has turned out to be a fairly popular technology on the Internet (recent numbers indicated that something like a quarter of all sites on the Internet uses PHP).
A few really high-demand sites are using PHP and are using libcurl in the backend. Facebook and Yahoo are two such sites.

La traducción hecha por nosotros al idioma castellano:

El software de enlace (librerías) de curl para PHP fue uno, cuidado sino, el primero de los enlaces que realmente captura y lo usa ampliamente. Rápidamente fue adoptado como una manera por defecto para transferir datos y se ha mantenido en esa posición por más de una década mientras tque PHP se ha convertido en una tecnología bastante popular en internet (cifras recientes indican que algo asó como una cuarta parte de todos los sitios web en internet utilizan PHP).

Realmente unos pocos sitios web de alta demanda están usando PHP acompañado de las librerías de enlace de curl corriendo entre bamabalinas. Facebook y Yahoo son dos de tales sitios.

Protocolos soportados por curl.

HTTP, HTTPS, FTP, FTPS, GOPHER, TFTP, SCP, SFTP, SMB, TELNET, DICT, LDAP, LDAPS, FILE, IMAP, SMTP, POP3, RTSP y RTMP (esos son todos, por ahora -y vienen más, no se detiene el proyecto-).

PHP curl.

Ahora si que pasamos a hablar del software que nos ocupa en este vuestro sitio web de compartición del conocimiento. Como ya hemos hablado bastante de historia y teoría pasamos directamente a describir cómo trabajar con PHP curl:

  1. Inicializar curl con curl_init().
  2. Pasarle los parámetros – esencial es la URL – con curl_setopt().
  3. Retribuir y mostrar -o guardar- con curl_exec().
  4. Cerrar y liberar recursos con curl_close().

Nuestro primer ejemplo práctico.

Lo siguiente que haremos es abrir una ventana terminal, tomar nuestro editor de texto favorito y escribir el siguiente código para ser guardado en un archivo que llamaremos php_curl.php:

<?php
    $objCurl = curl_init();
    curl_setopt ($objCurl, CURLOPT_URL, "http://www.soporteweb.com");
    curl_exec ($objCurl);
    curl_close ($objCurl);
?>

Por supuesto, llamad vuestro archivo como queráis, guardadlo en vuestra carpeta donde ejecute vuestro servidor PHP y dadle los permisos de lectura y ejecución necesarios. En la primera línea inicializamos, en la segunda línea le pasamos la dirección web deseada -un dominio que devuelve nuestra dirección IP asignada por nuestro ISP-, en la tercera línea lo ejecutamos y en la cuarta línea cerramos y liberamos recursos. Una explicación más detallada a continuación.

Inicialización.

Debemos crear una instancia y guardarla para futuras referencias, este objeto basado en curl nosotros lo llamamos $objCurl y este nombre es el que debemos pasar a los otros comandos. El comando curl_init() solamente acepta un parámetro, la URL que es un dato imprescindible, tal vez debido a ser tan importante fue el único que establecieron en este comando. Aunque en el ejemplo no lo colocamos por razones didácticas, de ahora en adelante “para que no se nos olvide” lo estableceremos siempre en la primera línea del script o guion del programa.

Configuración.

Este es el comando más denso, notad que le damos el nombre de configuración porque su nombre así lo sugiere: curl_setopt(), osea “setopt” -> “set options”. Acepta tres parámetros, separados por comas:

  • Primero debemos indicarle el objeto que contiene la inicialización de curl, en nuestro caso la variable $objCurl.
  • Segundo le pasaremos el tipo de valor que le pasaremos en el tercer parámetro, en nuestro caso la URL y la constante que lo define -nombrada de manera nemotécnica- es CURLOPT_URL.
  • El tercer parámetro es el valor en sí mismo de lo que indicamos en el segundo parámetro.

Debemos indicaros que usamos una sola línea para configurar, pero pronto veremos que esta parte es la más abultada por la infinidad de datos y tipos de datos que podemos pasar. Es mejor que vayaís preparando para aprender que esta sección siempre será multilínea y que debemos indentarla y escribirla lo más explícito posible para corregir a futuro de forma fácil nuestro código.

Ejecución.

Simplemente le decimos a PHP que hemos terminado de establecer lo que queremos obtener –o lo que queremos enviar, ya veremos más adelante- y acepta un parámetro que sigue siendo el objeto que hemos creado y configurado. En este punto ya os recordaremos que estamos trabajando con funciones y este comando devuelve verdadero o falso para indicar si tuvo éxito (para comparar la respuesta usaremos siempre “==” para estar seguros del tipo de variable y el resultado obtenido). En el ejemplo no colocamos ninguna variable que reciba el resultado del que hablamos como función ¿dónde está lo que nos interesa? En realidad el valor numérico (verdadero o falso) es lo que podemos guardar en una variable, el resto del resultados se va al stdout y eso será lo que enviaremos al navegador web que solicitó nuestro primer guión PHP sobre curl. Debido a esto recibiremos el mismo código HTML de la página que estamos solicitando (pero con los enlaces web absolutos cambiados).

Si probáis con descargar diferentes páginas, unas funcionarán, otras no, todo depende de si la página es estática, dinámica, si tiene JavaScript, en fin, cantidad de cosas que pueden NO ser compatibles con PHP curl: por eso debemos aprender las muchísimas opciones de configuración que nos sean útil en cada trabajo que se nos presente para ganarnos así el pan nuestro de cada día como Dios manda.

Liberación de recursos.

Os podrá sonar que nos contradecimos con lo siguiente: la función curl_close() se encarga de cerrar, como su nombre indica pero no devuelve resultado acerca del trabajo que le mandamos a realizar. Consideramos que esto es un “bug” porque a la hora de detectar dónde fugan recursos en nuestro servidor deberemos recurrir a otras herramientas apartes de PHP, pudiendo ser evitado esto con una sencilla rutina de nuestra parte e indicar que está sucediendo y qué está mal. Ah, perdón, casi lo olvidamos: el parámetro único que acepta es el objeto que creamos -y queremos destruir- con curl_init().

Nuestro primer ejemplo práctico pero mejorado.

Un sencillísima rutina de control de errores nos puede ahorrar muchísimos dolores de cabeza.

Nota: hay mejores métodos para el manejo de excepciones, pero aprendamos poco a poco. Ah, y nosotros lo llamamos “control de errores” y eso tampoco es el nombre correcto (excepciones) pero así nos abstraemos.

Por ello reescribiremos nuestro ejemplo pero con condicionales if~else:

<?php
  $objCurl = curl_init("http://www.soporteweb.com/");
  if ($objCurl == true) {
     $resul = curl_exec ($objCurl);
     if ($resul == true){
       curl_close ($objCurl);
     } else {
       echo "No se pudo ejecutar PHP curl<br>";
     }
  } else {
    echo "No se pudo inicializar PHP curl.<br>";
  }
?>

Notad que pasamos de una buena vez el URL en en el inicio con curl_init(). Además guardamos el valor del resultado en una variable, evaluamos el valor que tiene y ejecutamos o mostramos un mensaje apropiado según la respuesta obtenida. Muchos programadores y programadoras piensan que hacer nuestro software de esta manera, aparte de ayudarnos a nosotros mismo, abre las puertas a los hackers cuando se presentan excepciones -o errores, como les llamamos- porque “develan mucho”. Ese razonamiento es válido en el software privativo pero en el software libre no tiene asidero alguno porque cualquier hacker tiene acceso al código fuente, así que hagamos la depuración fácil para nosotros mismos.

Guardando el resultado en una “variable aparte”.

Como explicamos PHP curl envía al stdout la respuesta y asu vez eso lo pasamos al navegador, ¿qué tal si analizamos primero lo que recibimos y luego lo reenviamos? Por ejemplo, podríamos “acomodar” los enlaces absolutos de las imágenes que contiene la página web que llamamos (URL). Para ello vamos a emplear culr_setopt() con CURLOPT_RETURNTRANSFER establecido en el valor 1, mirad:

<?php
    $objCurl = curl_init("http://www.soporteweb.com");
    if ($objCurl == true) {
        curl_setopt ($objCurl, CURLOPT_RETURNTRANSFER, 1);
        $resul = curl_exec ($objCurl);
        if ($resul == true){
            curl_close ($objCurl);
            print "Nuestra direccion IP es: ";
            print $resul;
        } else {
            echo "No se pudo ejecutar PHP curl<br>";
        }
    } else {
        echo "No se pudo inicializar PHP curl.<br>";
    }
?>

Y así “manipulamos” el resultado con el texto que coloreamos en verde; ahora vamos un paso más allá. 😎

Guardando el resultado en un archivo.

Aunque ya tengamos el resultado deseado en una “variable” supongamos que estamos creando un robot (o bot como los mientan ahora en este siglo) y queremos guardar el resultado en un archivo ¿qué tiene que ver PHP curl con esto si ya sabemos el lenguaje PHP y sabemos como hacerlo aparte?

Pues resulta que con CURLOPT_FILE en curl_setopt() permite guardarlo en un archivo, pero antes tenemos que abrir el archivo y pasarle la “referencia” a PHP curl:

<?php
  $objCurl = curl_init("http://www.soporteweb.com/");
  if ($objCurl == false) {
    print "No se pudo inicializar <b>PHP curl</b>.<br>";
  } else {
    $nom_arch = 'PHP_curl.html';
    $arch = fopen( $nom_arch , "w");
    if ($arch == false ) {
      print "No se pudo abrir el archivo '".$nom_arch."'<br>";
      curl_close ($objCurl);
    } else {
      $resp = curl_setopt ($objCurl, CURLOPT_RETURNTRANSFER, 1);
      if ( $resp == false ) {
        print "No se pudo establecer CURLOPT_RETURNTRANSFER.<br>";
        curl_close ($objCurl);
      } else {
        $resp = curl_setopt ($objCurl, CURLOPT_FILE, $arch);
        if ( $resp == false ) {
          print "No se pudo establecer CURLOPT_FILE.<br>";
          curl_close ($objCurl);
      } else {
          $resul = curl_exec ($objCurl);
          if ($resul == false) {
            echo "No se pudo ejecutar PHP curl.<br>";
          } else {
            print "El archivo fue guardado con el nombre 'PHP_curl.html'.<br>";
            // curl_close NO devuelve resultado:
            curl_close ($objCurl);
          }
        }
      }
    }
  }
?>

Si tenéis problemas para escribir el archivo en disco REVISAD vuestros derechos de escritura para PHP especialmente para el grupo “www-data”.

Así de sencillo, con las correspondientes rutinas de depuración para nosotros. Como tal vez se vea un poco largo hemos coloreado las partes importantes para que sigáis el hilo. ¿Que por qué nos hemos ido por la negación siempre de primero? Pues revisando la función fopen() verificamos que si es exitosa devuelve una referencia al archivo más no un valor de tipo booleno -verdadero-. En cambio si falla la función siempre devuelve un valor boleano falso. Lo de seguir usando la negación por defecto es para llevar el mismo estilo parejo al resto del código.

Mejorando la claridad de la sintaxis con ayuda de exit().

Una función “vieja” en PHP es la función die() que es totalmente equivalente a exit() que es más “elegante”. El chiste del asunto es emplear la conjunción or (operador lógico “o”) para unir la acción que queremos realizar con la función exit(). Dicha función permite como parámetro una cadena de texto o un entero entre cero y 254 (el 255 está reservado para uso exclusivo de PHP). Nos gusta mejor la opción de pasarle una cadena de texto con el mensaje que queremos presentar al usuario ¡o a nosotros mismos!

Otro punto importante es que la función exit() aparte de mostrar mensaje y cerrar adecuadamente los objetos en memoria es que sale inmediatamente y no ejecuta el resto de las líneas que quedan a partir de su llamada.

Veamos entonces cómo emplearlo en nuestra labor que hoy nos ocupa (para no alargar tanto cada línea fijaos el uso del punto y coma para separar bloques de código multilíneas):

<?php
  $objCurl = curl_init("http://www.soporteweb.com/")
    or exit("No se pudo inicializar <b>PHP curl</b>.<br>");

  $nom_arch = 'PHP_curl.html';
  $arch = fopen( $nom_arch , "w")
    or exit("No se pudo abrir el archivo '".$nom_arch."'<br>");

  curl_setopt ($objCurl, CURLOPT_RETURNTRANSFER, 1)
    or exit("No se pudo establecer CURLOPT_RETURNTRANSFER.<br>");

  curl_setopt ($objCurl, CURLOPT_FILE, $arch)
    or exit("No se pudo establecer CURLOPT_FILE.<br>");

  curl_exec ($objCurl)
    or exit("No se pudo ejecutar PHP curl.<br>");

  print "El archivo fue guardado con el nombre 'PHP_curl.html'.<br>";

  // curl_close NO devuelve resultado:
  curl_close ($objCurl);
?>

¡Qué gran mejora en la legibilidad! Debemos aclarar que exit() de manera implícita llama a los procedimientos de cierre y liberación de recursos de memoria (destructor lo nombran en lenguaje C++) así que como ven es una vía muy pragmática para nuestros propósitos.

Si queréis probar como funciona la rutina con control de excepciones, colocad una dirección web que no exista y mirad que sucede, ¡experimentad! También os recomendamos probar otros protocolos, por ejemplo FTP en un servidor público como por ejemplo “ftp://ftp.cesca.es/” donde veremos un listado de archivos disponibles para ser descargados.

Probando otros protocolos: FTP.

En el párrafo anterior os sugerimos probar el protocolo FTP y no hubo nada que cambiar en el código que teníamos -aparte de la URL, por supuesto-. Pero PHP curl tiene sus opciones y a continuación pasamos a revisar alguna de ellas. La primera que revisaremos limita el listado obtenido de archivos y directorios, osea, no muestra los permisos de cada uno de ellos. La sintaxis en el comando curl_setopt() -y todas las opciones utilizan esta función- es la siguiente:


1
2
curl_setopt($objCurl, <span style="color: #008000;">CURLOPT_FTPLISTONLY</span>, 1)
  or exit("No se pudo establecer CURLOPT_FTPLISTONLY");

Si el servidor FTP no fuera público o exigiera una conexión tipo anónimo usaremos esto:

 curl_setopt($objCurl, CURLOPT_USERPWD, "anonimo:su@correo-e.com")
    or exit("No se pudo establecer CURLOPT_USERPWD");

Con esta novedad de introducir credenciales de conexión nos encontramos con otro tipo de manejo de excepciones: dado el caso las credenciales no funcionen (mala escritura, contraseña vencida, usuario inexistente, etc) dentro del obejto PHP curl se guardará en una instancia aparte el resultado del intento de conexión, para ellos contamos con:

 echo curl_error($objCurl);

Pasando valores por medio del método POST.

Finalmente llegamos al meollo del asunto: poder pasar a un servidor web una o más variables por medio del método POST a un servidor web. Para propósitos de aprendizaje diremos que es uno de los métodos más populares debido a su cierta privacidad ya que el usuario no puede ver el enlace como en el método GET y tampoco es “cacheable” por los navegadores web.

Primero haremos una rchivo que nos muestre todas las variables que reciba y las liste por pantalla, de manera increíble solo necesita una sola líneas de código (guardar con el nombre de “curl_post.php“):

<?php
  var_dump($_POST);
?>

Una vez tengamos este archivo ya sea en nuestro servidor local o en nuestro servidor remoto podremos comenzar a escribir el siguiente script:

<?php
 $objCurl = curl_init("http://localhost/curl_post.php")
 or exit("No se pudo inicializar PHP curl<br>");

 curl_setopt($objCurl, CURLOPT_POST, 1)
 or exit("No se pudo establecer CURLOPT_POST");

 //Pasamos las variables que nos interesan al servidor
 curl_setopt($objCurl, CURLOPT_POSTFIELDS, "var1=uno&var2=dos&var3=tres")
 or exit("No se pudieron establecer las variables en CURLOPT_POSTFIELDS");

 curl_exec ($objCurl)
 or exit("No se pudo ejecutar PHP curl.");

 curl_close ($curl);
?>

Debido a los mensaje que incluimos en cada exit() y con el coloreado del código no hay nada que explicar… excepto unas cuantas acotaciones:

  • Las variables que pasamos en el método POST deben estar por pares unidas con el ampersand y separadas por medio de un signo de igualdad.
  • Los espacios no se permiten, debemos pasar “%20” o mejor dicho debemos darle un formato especial antes de enviarlo. El comando que nos ayudará será urlencode() y para nosotros castellanohablantes es importante representar bien los acentos, etc.
 //Pasamos las variables que nos interesan al servidor
 $var_post = urlencode("var1=Venezuela&var2=América del Sur&var3=Güigüe");
 curl_setopt($objCurl, CURLOPT_POSTFIELDS, $var_post)
   or exit("No se pudieron establecer las variables en CURLOPT_POSTFIELDS");

Fuera de tema: “Tamper data for Mozilla Firefox”.

Adam Judson es un desarrollador que tiene -nos tiene- como usuarios a más de 80 mil personas en su trabajo de programación desde el año 2007. Él desarrolló “Tamper Data” para este popular navegador, una herramienta para, según sus propias palabras “pruebas de seguridad en aplicaciones web por medio de la modificación de los parámetros POST”.

Dicha herramienta la podemos instalar en nuestro navegador (requiere reinicio del navegador y no es compatible con “Google Web Accelerator”) y con ella podremos visitar cualquier sitio web que utilize envio de datos POST y al activarlo en una ventana aparte mostrará sin molestar ni influir en nada el tráfico saliente de nuestro ordenador por medio de nuestro navegador web Firefox. ¿Por qué anuncia pruebas de seguridad? Porque nosotros muy bien sabemos como programadores que el código fuente de nuestras aplicaciones web están a la vista de los usuarios ¿pero que tal lo que no se ve? Pues acá entra “Tamper Data”: por más que coloquemos validación HTML5 y JavaScript del lado del cliente, si no utilizamos HTTPS el tráfico entre el usuario y nuestro servidor puede ser modificado por terceros. Y aunque si usaramos HTTPS tampoco podemos confiarnos de nuestros usuarios, verbigracia la misma herramienta que estamos recomendando puede modificar los datos si en la venta que se nos abre hacemos click en “modificar”. Al activar este botón, cuando sale algo de nuestro ordenador nos pregunta si deseamos modificar algún dato, ¡y allí podremos echar por tierra toda la hermosa programación hecha en HTML5 y javaScript!

Este comentario es “fuera de tema”: para combatir esto último lo que debemos hacer es, en lenguaje PHP -o vuestro lenguaje utilizado- volver a validar los datos recibidos desde el usuario, ¡PERO HAY MÁS! Es obligatorio agregar “disparadores” y “restricciones” incrustados en nuestra base de datos (recomendamos PostgreSQL por su capacidad de incluso aceptar guiones en lenguaje Python) para volver a validar datos al agregar los datos a nuestro motor informático. Más detalles escapan de esta entrada, pero este resumen es el mejor que podemos expresar al respecto.

Ejecutando PHP curl en modo de depuración.

“Verbose” en idioma inglés describe un modo de hablar más de lo necesario. Pero en nuestro mundo del software libre nuestra búsqueda del conocimiento es insaciable, “solo sabemos que no sabemos nada” por ello le dedicamos esta sección empinada a ahondar en PHP curl. Aparte de aprender más y mantener nuestro cerebro haciendo ejercicios, debido a la gran cantidad de protocolos y opciones es sumamente fácil para nosotros el cometer errores solicitando opciones inexistentes o incompatibles entre ellas, si son varios los parámetros que les pasemos.

Para activar el modo de depuración -vamos que la traducción al castellano de “verboso” no cuela- en PHP curl echamos mano de la alternativa CURLOPT_VERBOSE en la ya consabida función curl_opt() ¡pero esperen -como dice el “infomercial” de T.V.- hay más! PHP curl, dijimos, lo tenemos claro, emplea stdin y stdout que son los más conocidos pero también utiliza stderr ¡para algo que no es error, sino DEPURACIÓN! Teoricamente no debería ser pero en la práctica, el mundo real esto es de facto que lo hacen -y haremos-: capturar el stderr para llevar los mensajes de depuración hacia un archivo de escritura y de adición. Este archivo si no está creado, lo hace, y lo abre para agregar dichos datos y si existe le adiciona al final, de tal manera que llevamos una especie de bitácora para nuestro estudio y control.

<?php
  $ObjCurl = curl_init()
    or exit("No se pudo inicializar PHP curl.<br>");

  curl_setopt ($ObjCurl, CURLOPT_URL, "http://www.soporteweb.com")
    or exit("No se pudo establecer la URL.<br>");

  curl_setopt($ObjCurl, CURLOPT_VERBOSE, 1)
     or exit("No se pudo establecer el modo de depuración.<br>");

  $verbose_arch = fopen('verbose.txt', 'a');
  curl_setopt($ObjCurl, CURLOPT_STDERR, $verbose_arch)
    or exit("No se pudo establecer CURLOPT_STDERR<br>.");

  curl_exec ($ObjCurl)
    or exit("No se pudo ejecutar PHP curl.<br>");

  curl_close ($ObjCurl);
?>

Y veremos algo parecido a esto:

CURLOPT_VERBOSE
CURLOPT_VERBOSE

Explicamos que:

  • Las líneas que comienza con un asterisco son mensajes informativos de PHP curl.
  • Las líneas que comienzan con “>” es información que se le envia al servidor web, ftp, etc.
  • Las líneas que comienzan con “<” es la información que recibimos de tal servidor.
  • Los servidores web basados en ASP.NET devolverán un error “HTTP/1.1 500 Internal Server Error” y no podremos hacer nada ya que no se rigen por las normas comunes basadas en RFC y recomendaciones de WWW.

Otros usos para PHP curl.

Por nuestros experimentos vimos que PHP curl permite grabar en archivos “flujos de datos” de una manera básica, sin estar amarrado a protocolos (si lo usamos con FTP veremos que nos hacen falta los comandos populares y que solo podremos usar si tenemos un cliente FTP como tal). Eso deja abierta la posiblidad a darle infinidad de usos sin siquiera saber -ni preocuparnos- “con quien estamos hablando”. Un ejemplo de ello es con el acceso a servidores con tecnología ASP.NET o la mismisa consulta de nuestra dirección IP que devuelve un resultado no normalizado ni con metadato alguno (solo nosotros, seres humanos, estamos con la plena certeza que es una dirección IPv4).

Por ejemplo, si en vez de usar soporteweb.com para retribuir nuestra dirección IPv4 usamos el servicio que presta icanhazip.com en el modo de depuración veremos algo como esto:

icanhazip.com Get the facts
icanhazip.com Get the facts

Y al seguir el enlace indicado abrimos el blog de Major Hayden (sí, ese es el nombre, no no es mayor de ningún ejército ni alcalde de ninguna ciudad) un administrador de sistemas GNU/Linux con 6 años de experiencia quien ha tenido excelentes maestros y está dispuesto siempre aprender… como nosotros mismos, quienes escribimos y quienes leemos este humilde sitio web.

Auguramos que curl y PH curl seguirá creciendo y mejorando para nuestro beneplácito y que nosotros, a la larga, algún día aportaremos nuestro granito de arena a la causa del software libre, de una u otra manera, de manera inexorable.

curl_setopt(): parámetros mas socorridos.

La lista es larga en verdad pero mencionaremos, al menos, los que avizoramos de alguna utilidad dada la experiencia que hemos logrado a lo largo de los años:

CURLOPT_USERAGENT

Con esta opción podremos indicarle al servidor con el cual le estamos requiriendo información acerca de nuestra identidad. Curiosamente PHP curl por defecto no envía indentificación alguna acerca del nombre del software pero es que también el servidor que atiende la consulta tampoco la requiere como obligatoria. En la práctica les mostramos (las direcciones IP las ocultamos -junto con otros detalles- de las visitas a esta vuestra página web):

PHP curl no se identifica -por defecto-
PHP curl no se identifica -por defecto-

Así que le aplicamos código y así quedaría la visita de nuestro bot en cierne:

Usando PHP curl CURLOPT_USERAGENT
Usando PHP curl CURLOPT_USERAGENT

CURLOPT_CONNECTTIMEOUT

A veces estimamos en cuánto tiempo debería responder determinado proceso y si no se realiza pues no está bien afinado nuestro servidor no sin antes descartar fallas de internet, etc. Para ello CURLOPT_CONNECTTIMEOUT es el idóneo para que nuestro script no se cuelgue indefinidamente, al cabo de cierto tiempo que le establezcamos y si no logra conectar con el servidor la tarea finalizará. El tercer parámetro que pasaríamos a continuación es el número de segundos, lo cual es una eternidad en tiempo de computadores, por ello también contamos con CURLOPT_CONNECTIONTIMEOUT_MS que viene expresado en milisegundos si estamos realmente apurados.

Es una excelente opción para desarrollar herramientas de diagnóstico a equipos remotos y podremos llevar un ergistro de eventos en nuestra base de datos (sí, que ya hay mucho software para eso pero también hay tareas tan pequeñas que no necesitamos “una mandarria sino un simple martillito”)

CURLOPT_COOKIE y CURLOPT_COOKIEFILE

Una “cookie” en inglés o galletita en castellano, es una pieza de información que plantan los servidores web en nuestros navegadores para identificarnos de manera absoluta. Al igual que el método POST debemos saber el nombre de la galletita y por ende su contenido, así que no la generamos nosotros -obvio- pero si debemos tener una para poder obtener respuesta de algunos servidores, quienes comparan si tienen la galletita en su lista de memoria (las galletitas tienen vencimiento o validez en el tiempo) y así podrán:

  • bien sea aceptar nuestra petición ó
  • incluso devolvernos unos valores personalizados.

De allí viene el gran temor a la violación de nuestra privacidad, una delgada línea demarca las buenas de las malas intenciones.

De esto último viene la necesidad de CURLOPT_COOKIEFILE, muchas veces son tantas las galletitas usadas por nosotros los programadores que mejor es enviar un archivo completo con ellas incluídas. Aquí debemos colocar el nombre del archivo en la opción anterior debemos enviar los valores en sí.

Otra manera de pasar una gran cantidad de galletitas es pasar el jarrón completo, un fichero, y para que no se nos olvide la sintaxis le pusieron el curioso nombre CURLOPT_COOKIEJAR. Un ejemplo sería:

curl_setopt($objCurl, CURLOPT_COOKIEJAR, 'jarron_de_galletitas.txt');

1
CURLOPT_SSL_VERIFYPEER

Con esta opción podemos deshabilitar que verifique certificados en una conexión https (o mejor dicho TLS). Esto normalmente está habilitado pero uno a veces necesita, por economía, un certificado autofirmado el cual no está validado ante ningún tercer ente. Con esta opción cambiada a false en el tercer parámetro evitaremos este problemita.

1
CURLOPT_FOLLOWLOCATION

Actualmente las redirecciones de página se ha vuelto el pan nuestro de cada día motivado a la popularidad de las páginas web. Una página de cualquier gobierno del mundo de muy seguro que tendrá miles y hasta millones de visitantes y como comprenderán un solo servidor web jamás ni nunca se daría abasto. Lo que se ha inventado es poner granjas de servidores en distintas partes del mundo con copias constantes de los dominios deseados y redireccionado el tráfico con ayuda de potentes DNS: todo esto se conoce como CDN o “Content Delivery Network”.

La idea del asunto es que cuando uno solicita una página web los DNS encargados redireccionan nuestra solicitud a la granja de servidores más cerca a nosotros de manera geográfica donde habrá una copia de la web que queremos. ¿A qué viene todo este cuento con PHP curl? Que es muy probable que la respuesta que obtengamos será una cabecera tipo 300 indicandonos que la direcciíon adecuadad para nosotros y entonces volvemos a hacer la petición -la misma petición- pero a otro servidor más cercano (o mas desocupado, o que tenga nuestra idioma, etc. -el que nos convenga-).

PHP curl ya está preparado en ese escenario y estableceremos CURLOPT_FOLLOWLOCATION a verdadero y por defecto PHP curl redireccionará hasta un máximo de 5 oportunidades nuestra solicitud (más adelante veremos otra función que nos dirá cuántas redirecciones siguió con la variable CURLINFO_REDIRECT_COUNT establecida).

Un problema se presenta al hacer una consulta POST: después del primer redireccionamiento PHP curl NO HACE una consulta POST sino una consulta GET (por razones históricas y de costumbres en los navegadores) y esto es un problema porque son métodos distintos y no obtendremos respuesta (aparte de que nos volveremos “locos” buscando dónde está el error). Para prevenir este inconveniente en vez de fijar CURLOPT_POST a “true” tomaremos otra opción, CURLOPT_CUSTOMREQUEST fijado a “POST”:

curl_setopt($ObjCurl, CURLOPT_CUSTOMREQUEST, "POST");

Otra advertencia más: cuando uno sube archivos por medio de CURLOPT_POSTFIELDS en la segunda redirección no será enviado dicho fichero. También para prevenir esto urge emplear CURLOPT_POSTREDIR fijado el valor a 3 para que maneje los redireccionamientos 301 y 302 reenviando el archivo en cada oportunidad (es más tráfico de datos pero ¿cómo hacemos con los CDN’s que llegaron para quedarse?).

Panorama general del resto de las opciones de curl_setopt()

Ya en este punto os habreis dado cuenta de la importancia de esta función, recordad que solo hemos visto apenas cuatro que van en este orden: curl_init(), curl_setopt(), curl_exec() y curl_close(); pues bien hay 23 funciones más de las cuales más adelante estudiaremos una de ellas (por ahora).

He aquí un listado clasificado por el tipo de contenido que hay que pasar en el tercer parámetro de curl_setopt(), de un vistazo (en marrón las que hemos visto y analizado):

Aquellas cuyo valor debe ser booleano (verdadero<>0 ó falso=0):
  • CURLOPT_AUTOREFERER
  • CURLOPT_BINARYTRANSFER
  • CURLOPT_COOKIESESSION
  • CURLOPT_CERTINFO
  • CURLOPT_CONNECT_ONLY
  • CURLOPT_CRLF
  • CURLOPT_DNS_USE_GLOBAL_CACHE
  • CURLOPT_FAILONERROR
  • CURLOPT_SSL_FALSESTART
  • CURLOPT_FILETIME
  • CURLOPT_FOLLOWLOCATION
  • CURLOPT_FORBID_REUSE
  • CURLOPT_FRESH_CONNECT
  • CURLOPT_FTP_USE_EPRT
  • CURLOPT_FTP_USE_EPSV
  • CURLOPT_FTP_CREATE_MISSING_DIRS
  • CURLOPT_FTPAPPEND
  • CURLOPT_TCP_NODELAY
  • CURLOPT_FTPASCII
  • CURLOPT_FTPLISTONLY
  • CURLOPT_HEADER
  • CURLINFO_HEADER_OUT
  • CURLOPT_HTTPGET
  • CURLOPT_HTTPPROXYTUNNEL
  • CURLOPT_MUTE
  • CURLOPT_NETRC
  • CURLOPT_NOBODY
  • CURLOPT_NOPROGRESS
  • CURLOPT_NOSIGNAL
  • CURLOPT_PATH_AS_IS
  • CURLOPT_PIPEWAIT
  • CURLOPT_POST
  • CURLOPT_PUT
  • CURLOPT_RETURNTRANSFER
  • CURLOPT_SAFE_UPLOAD
  • CURLOPT_SASL_IR
  • CURLOPT_SSL_ENABLE_ALPN
  • CURLOPT_SSL_ENABLE_NPN
  • CURLOPT_SSL_VERIFYPEER
  • CURLOPT_SSL_VERIFYSTATUS
  • CURLOPT_TCP_FASTOPEN
  • CURLOPT_TFTP_NO_OPTIONS
  • CURLOPT_TRANSFERTEXT
  • CURLOPT_UNRESTRICTED_AUTH
  • CURLOPT_UPLOAD
  • CURLOPT_VERBOSE
Aquellas cuyo valor debe ser numérico entero:
  • CURLOPT_BUFFERSIZE
  • CURLOPT_CLOSEPOLICY
  • CURLOPT_CONNECTTIMEOUT
  • CURLOPT_CONNECTTIMEOUT_MS
  • CURLOPT_DNS_CACHE_TIMEOUT
  • CURLOPT_EXPECT_100_TIMEOUT_MS
  • CURLOPT_FTPSSLAUTH
  • CURLOPT_HEADEROPT
  • CURLOPT_HTTP_VERSION
  • CURLOPT_HTTPAUTH
  • CURLOPT_INFILESIZE
  • CURLOPT_LOW_SPEED_LIMIT
  • CURLOPT_LOW_SPEED_TIME
  • CURLOPT_MAXCONNECTS
  • CURLOPT_MAXREDIRS
  • CURLOPT_PORT
  • CURLOPT_POSTREDIR
  • CURLOPT_PROTOCOLS
    • CURLPROTO_HTTP, CURLPROTO_HTTPS, CURLPROTO_FTP, CURLPROTO_FTPS, CURLPROTO_SCP, CURLPROTO_SFTP, CURLPROTO_TELNET, CURLPROTO_LDAP, CURLPROTO_LDAPS, CURLPROTO_DICT, CURLPROTO_FILE, CURLPROTO_TFTP, CURLPROTO_ALL
  • CURLOPT_PROXYAUTH
  • CURLOPT_PROXYPORT
  • CURLOPT_PROXYTYPE
  • CURLOPT_REDIR_PROTOCOLS
  • CURLOPT_RESUME_FROM
  • CURLOPT_SSL_OPTIONS
  • CURLOPT_SSL_VERIFYHOST
  • CURLOPT_SSLVERSION
  • CURLOPT_STREAM_WEIGHT
  • CURLOPT_TIMECONDITION
  • CURLOPT_TIMEOUT
  • CURLOPT_TIMEOUT_MS
  • CURLOPT_TIMEVALUE
  • CURLOPT_MAX_RECV_SPEED_LARGE
  • CURLOPT_MAX_SEND_SPEED_LARGE
  • CURLOPT_SSH_AUTH_TYPES
  • CURLOPT_IPRESOLVE
    • CURL_IPRESOLVE_WHATEVER (por defecto), CURL_IPRESOLVE_V4, CURL_IPRESOLVE_V6
  • CURLOPT_FTP_FILEMETHOD
    • CURLFTPMETHOD_MULTICWD, CURLFTPMETHOD_NOCWD, CURLFTPMETHOD_SINGLECWD.
Aquellas cuyo valor debe ser una cadena de texto:
  • CURLOPT_CAINFO
  • CURLOPT_CAPATH
  • CURLOPT_COOKIE
  • CURLOPT_COOKIEFILE
  • CURLOPT_COOKIEJAR
  • CURLOPT_CUSTOMREQUEST
  • CURLOPT_DEFAULT_PROTOCOL
  • CURLOPT_DNS_INTERFACE
  • CURLOPT_DNS_LOCAL_IP4
  • CURLOPT_DNS_LOCAL_IP6
  • CURLOPT_EGDSOCKET
  • CURLOPT_ENCODING
  • CURLOPT_FTPPORT
  • CURLOPT_INTERFACE
  • CURLOPT_KEYPASSWD
  • CURLOPT_KRB4LEVEL
  • CURLOPT_LOGIN_OPTIONS
  • CURLOPT_PINNEDPUBLICKEY
  • CURLOPT_POSTFIELDS
  • CURLOPT_PRIVATE
  • CURLOPT_PROXY
  • CURLOPT_PROXY_SERVICE_NAME
  • CURLOPT_PROXYUSERPWD
  • CURLOPT_RANDOM_FILE
  • CURLOPT_RANGE
  • CURLOPT_REFERER
  • CURLOPT_SERVICE_NAME
  • CURLOPT_SSH_HOST_PUBLIC_KEY_MD5
  • CURLOPT_SSH_PUBLIC_KEYFILE
  • CURLOPT_SSH_PRIVATE_KEYFILE
  • CURLOPT_SSL_CIPHER_LIST
  • CURLOPT_SSLCERT
  • CURLOPT_SSLCERTPASSWD
  • CURLOPT_SSLCERTTYPE
  • CURLOPT_SSLENGINE
  • CURLOPT_SSLENGINE_DEFAULT
  • CURLOPT_SSLKEY
  • CURLOPT_SSLKEYPASSWD
  • CURLOPT_SSLKEYTYPE
  • CURLOPT_UNIX_SOCKET_PATH
  • CURLOPT_URL
  • CURLOPT_USERAGENT
  • CURLOPT_USERNAME
  • CURLOPT_USERPWD
  • CURLOPT_XOAUTH2_BEARER
Aquellas cuyo valor debe ser una matriz:
  • CURLOPT_CONNECT_TO
  • CURLOPT_HTTP200ALIASES
  • CURLOPT_HTTPHEADER
  • CURLOPT_POSTQUOTE
  • CURLOPT_PROXYHEADER
  • CURLOPT_QUOTE
Aquellas cuyo valor debe ser un recurso de flujo de datos:
  • CURLOPT_FILE
  • CURLOPT_INFILE
  • CURLOPT_STDERR
  • CURLOPT_WRITEHEADER
Aquellas cuyo valor debe ser una función o una función de cierre:
  • CURLOPT_HEADERFUNCTION
  • CURLOPT_PASSWDFUNCTION
  • CURLOPT_PROGRESSFUNCTION
  • CURLOPT_READFUNCTION
  • CURLOPT_WRITEFUNCTION
Aquellas cuyo valor debe ser una muy específica:
  • CURLOPT_SHARE

Función curl_setopt_array.

Nos dimos cuenta como la función exit() mejoró muy bien la legilibilidad de nuestro código y además proporciona información a nuestros usuarios en caso de tratamiento de excepciones. Pero hay una manera aún mejor de configurar las opciones justo antes de ejecutar curl_exec() con el detalle que es como la famosa película de vaqueros: tiene lo bueno, lo malo y lo feo pero sin lo feo. Lo bueno -y muy bueno- es que como dijimos mejora la apariencia de nuestro código y abre nuevas posibilidades con el uso de una matriz: podemos realizar una función que consulte una base de datos y llene dicha matriz. Lo malo es que al pasar todo de un solo golpe a la unficón ésta acepta todo o no acepta nada y tendremos nuestra bella salida diciendo “no se pudo establecer culr_setopt_arraypero no nos indicará dónde, cuál es el elemento que falla.

<?php
  $objCurl = curl_init()
    or exit("No se pudo inicializar PHP curl.<br>");

  $verbose = fopen('verbose.txt', 'a')
    or exit("No se pudo abrir el archivo verbose.txt<br>");
  $opciones = array(
                    CURLOPT_URL     => 'https://icanhazip.com',
                    CURLOPT_VERBOSE => 1,
                    CURLOPT_STDERR  => $verbose
                    );

  curl_setopt_array($objCurl, $opciones)
    or exit("No se pudo establecer curl_setopt_array()");

  curl_exec ($objCurl)
    or exit("No se pudo ejecutar PHP curl.<br>");

  curl_close ($objCurl);
?>

Percataos que colocamos el URL en la matriz para abultar y activamos el modo de depuración de resto todo lo demás es el mismo ejemplo que colocamos anteriormente. Lo curioso del asunto es que curl_setopt() y curl_setopt_array() pueden trabajar juntos perfectamente, lo anterior lo podemos reescribir como lo próximo y funciona plenamente:

<?php
  $objCurl = curl_init()
    or exit("No se pudo inicializar PHP curl.<br>");

  curl_setopt($objCurl, CURLOPT_URL ,'https://icanhazip.com')
    or exit("No se pudo establecer el CULROPT_URL");


  $verbose = fopen('verbose.txt', 'a')
    or exit("No se pudo abrir el archivo verbose.txt<br>");
  $opciones = array(
                    CURLOPT_VERBOSE => 1,
                    CURLOPT_STDERR  => $verbose
                    );

  curl_setopt_array($objCurl, $opciones)
    or exit("No se pudo establecer curl_setopt_array()");

  curl_exec ($objCurl)
    or exit("No se pudo ejecutar PHP curl.<br>");

  curl_close ($objCurl);
?>

Analizando los resultados de la última conexión.

Comentamos que PHP curl puede utilizarse para fabricar una sencilla herramienta de monitoreo de servidores. Pensando en esto indagamos en ciertas opciones que trae la librería y de la cual podemos obtener gran cantidad de información, siempre pensando en guardarlo en una base de datos para su posterior análisis de manera masiva. Pero debemos comenzar por lo básico y no irnos precipitadamente.

Aparte de las opciones de depuración, que ya logramos vertirlas en un archivo que llamamos simplemente “verbose.txt” (y cuyo nombre podemos cambiar a la fecha y hora actual para que siempre sea un archivo distinto y almacenar -o borrar, o procesar, etc.- cuando queramos) podemos echar mano de otra función para obtener los datos de la conexión. Tal función es denominada curl_getinfo() a la que introducimos nuestro manejador creado por curl_init().

Esta función devuelve falso si no tiene información o una matriz con elementos predefinidos -no todos-. Para poder escribir esta matriz en un archivo agarramos a la función serialize() para convertirla en una cadena de texto antes de guardarla.

De una vez vamos a la práctica con el primer ejemplo que presentamos, el más “refinado” con las llamadas exit() ¿lo recordais? Pero vamos un paso adelante y lo modificamos, primero para que tome la URL por el método GET: escribimos en la barra de direcciones la ubicación de nuestro guion y al final agregamos un signo de cierre de interrogante con la palabra “url”, luego un signo de igualdad y a continuación el dominio o página que queremos descargar y guardar -para luego analizar según el propósito que tengamos, que para cada persona es muy variopinto. Supongamos que nuestro guion está en nuestro servidor local y se llama “curl.php”, el llamado sería el siguiente (obvien el prefijo “http://” porque lo agregamos en el script):

http://localhost/curl.php?url=www.ks7000.net.ve

También agregaremos dos mensajes adicionales: si no se pasa una URL entonces alerta que debe pasar una y otro mensaje para indicar que ha sido guardada en un archivo (recordad que usamos la opcion CURLOPT_RETURNTRANSFER al valor de 1). Coloreamos el código para su mayor comprensión:

<?php
if ($_GET["url"])
{
 $objCurl = curl_init("http://".$_GET["url"])
 or exit("No se pudo inicializar PHP curl.<br />");

 $nom_arch = 'servidor.html';
 $arch = fopen( $nom_arch , "a")
 or exit("No se pudo abrir el archivo '".$nom_arch."'<br />");

 curl_setopt ($objCurl, CURLOPT_RETURNTRANSFER, 1)
 or exit("No se pudo establecer CURLOPT_RETURNTRANSFER.<br />");

 curl_setopt ($objCurl, CURLOPT_FILE, $arch)
   or exit("No se pudo establecer CURLOPT_FILE.<br />");

 curl_setopt ($objCurl, CURLOPT_CONNECTTIMEOUT, 7)
   or exit("No se pudo establecer CURLOPT_CONNECTTIMEOUT.<br />");
 curl_exec ($objCurl)
 or exit("No se pudo ejecutar PHP curl.<br />");

 $nom_arch = "servidor_respuesta.txt";
 $archivo = fopen($nom_arch, "a")
 or exit("No se pudo abrir el archivo'".$nom_arch."'<br />");

 $resp = curl_getinfo($objCurl)
 or exit("No se pudo obtener información de la ultima conexion.<br />");

 fwrite($archivo, serialize($resp))
 or exit("No se pudo escribir en el archivo '".$nom_arch."'<br />");

 fclose($archivo)
 or exit("No se pudo cerrar el archivo '".$nom_arch."'<br />");

 // curl_close NO devuelve resultado:
 curl_close ($objCurl);
 echo "URL descargada y guardada.<br />";
} else {
 echo "Debe pasar una URL para ser descargada y analizada.<br />";
}
?>

Es crucial llamar esta función ANTES de llamar la función curl_close() que es cuando aún existe el objeto en memoria.

Matrix con campos predefinidos.

Esos datos que guardamos en el anterior software que inventamos ya están seleccionados por los desarrolladores de PHP curl y son los siguientes (repetimos, NO son todos los disponibles):

  • “url”
  • “content_type”
  • “http_code”
  • “header_size”
  • “request_size”
  • “filetime”
  • “ssl_verify_result”
  • “redirect_count”
  • “total_time”
  • “namelookup_time”
  • “connect_time”
  • “pretransfer_time”
  • “size_upload”
  • “size_download”
  • “speed_download”
  • “speed_upload”
  • “download_content_length”
  • “upload_content_length”
  • “starttransfer_time”
  • “redirect_time”
  • “certinfo”
  • “primary_ip”
  • “primary_port”
  • “local_ip”
  • “local_port”
  • “redirect_url”
  • “request_header” (solamente está definido si CURLINFO_HEADER_OUT está establecido por una llamada previa a curl_setopt())

Totalidad de los campos de get_info()

Podemos solicitar cualquiera de estos campos pasando como parámetro los siguientes valores a la función curl_getinfo() -con una breve descripción y ordenados alfabéticamente-:

  • CURLINFO_APPCONNECT_TIME – Tiempo, en segundos, desde el inicio hasta la conexión/apretón de manos SSL/SSH del host remoto.
  • CURLINFO_CERTINFO – Serie de certificados TLS.
  • CURLINFO_CONDITION_UNMET – Información del condicional de timepo unmet.
  • CURLINFO_CONNECT_TIME – Tiempo, en segundos, que tomó el establecimiento de la conexión.
  • CURLINFO_CONTENT_LENGTH_DOWNLOAD – Logitud del contenido de la descarga, leída desde el campo “Content-Length:”.
  • CURLINFO_CONTENT_LENGTH_UPLOAD – Tamaño especificado de subida .
  • CURLINFO_CONTENT_TYPE – “Content-Type:” del documento solicitado. NULL indica que el servidor no envío un encabezado “Content-Type:” válido.
  • CURLINFO_COOKIELIST – Todas las cookies conocidas.
  • CURLINFO_EFFECTIVE_URL – Último URL efectivo.
  • CURLINFO_FILETIME – Hora del documento remoto obtenido; si devuelve -1, la hora del documento es desconocida.
  • CURLINFO_FTP_ENTRY_PATH – Ruta de entrada en el servidor FTP.
  • CURLINFO_HEADER_OUT – El string de la petición enviada. Para que funcione, se ha de añadir la opción CURLINFO_HEADER_OUT al manejador, llamando a curl_setopt()
  • CURLINFO_HEADER_SIZE – Tamaño total de los encabezados recibidos.
  • CURLINFO_HTTP_CODE – Último código HTTP recibido.
  • CURLINFO_HTTP_CONNECTCODE – El código de respuesta de CONNECT.
  • CURLINFO_HTTPAUTH_AVAIL – Máscara de bits que indica el/los método/s de autenticación disponible/s de acuerdo a la respuesta anterior.
  • CURLINFO_LOCAL_IP – Dirección IP local (fuente) de la conexión más reciente.
  • CURLINFO_LOCAL_PORT – Puerto local (fuente) de la conexión más reciente.
  • CURLINFO_NAMELOOKUP_TIME – Tiempo, en segundos, en resolver el nombre.
  • CURLINFO_NUM_CONNECTS – Número de conexiones que curl ha tenido que crear para lograr la transferencia anterior.
  • CURLINFO_OS_ERRNO – Número de error (errno) de un fallo de conexion. Es número es específico de cada SO y sistema.
  • CURLINFO_PRETRANSFER_TIME – Tiempo, en segundos, desde el inicio hasta justo antes de que comience la transferencia del fichero.
  • CURLINFO_PRIMARY_IP – Dirección IP de la conexión más reciente.
  • CURLINFO_PRIMARY_PORT – Puerto de destino de la conexión más reciente.
  • CURLINFO_PRIVATE – Datos privados asociados a este manejador de cURL, previamente establecidos con la opción CURLOPT_PRIVATE de curl_setopt()
  • CURLINFO_PROXYAUTH_AVAIL – Máscara de bits que indica el/los método/s de autenticaición proxy disponible/s de acuerdo a la respuesta anterior.
  • CURLINFO_REDIRECT_COUNT – Número de redireccionamientos, con la opción CURLOPT_FOLLOWLOCATION habilitada.
  • CURLINFO_REDIRECT_TIME – Tiempo, en segundos, de todos los pasos de redireción antes de que la última transación haya empezado, con la opción CURLOPT_FOLLOWLOCATION habilitada.
  • CURLINFO_REDIRECT_URL – Con la opción CURLOPT_FOLLOWLOCATION inhabilitada: URL de redirección encontrado en la última transacción, que debería ser solicitado manualmente luego. Con la opción CURLOPT_FOLLOWLOCATION habilitada: está vacío. El URL de redirección en este caso está disponible en CURLINFO_EFFECTIVE_URL
  • CURLINFO_REQUEST_SIZE – Tamaño total de las peticiones realizadas, actualmente solo para peticiones HTTP.
  • CURLINFO_RESPONSE_CODE – El último código de respuesta.
  • CURLINFO_RTSP_CLIENT_CSEQ – Siguiente CSeq cliente de RTSP.
  • CURLINFO_RTSP_CSEQ_RECV – CSeq recivido recientemente.
  • CURLINFO_RTSP_SERVER_CSEQ – Siguiente CSeq servidor de RTSP.
  • CURLINFO_RTSP_SESSION_ID – ID de sesión de RTSP.
  • CURLINFO_SIZE_DOWNLOAD – Número total de bytes descargados.
  • CURLINFO_SIZE_UPLOAD – Número total de bytes subidos.
  • CURLINFO_SPEED_DOWNLOAD – Velocidad media de descarga.
  • CURLINFO_SPEED_UPLOAD – Velocidad media de subida.
  • CURLINFO_SSL_ENGINES – Criptomotores OpenSSL admitidos.
  • CURLINFO_SSL_VERIFYRESULT – Resultado de la verificación del certificado SSL solicitado por la opción CURLOPT_SSL_VERIFYPEER.
  • CURLINFO_STARTTRANSFER_TIME – Tiempo, en sengudos, hasta que el primer byte está a punto de transferirse.
  • CURLINFO_TOTAL_TIME – Duración total, en segundos, de última transferencia.

Resumen.

Recuerden lo que siempre dijo en vida nuestro Gran Maestro: “un punto, un punto y una línea recta“; aprovechen estos conocimientos para desarrollar programas y utilidades para fortalecer el Patrimonio Tecnológico de nuestra Humanidad no para escribir virus y gusanos que marguen las vidas de los demás.

<Eso es todo, por ahora>.

Fuentes consultadas.

En idioma castellano.

En idioma inglés.