julio 2018

Cannot change save handler when session is active

Introducción

Trabajando en un pequeño proyecto de PHP me topé con la siguiente advertencia: Warning: session_set_save_handler(): Cannot change save handler when session is active. Este mensaje me tuvo dando vueltas por todo internet sin encontrar una solución, pero probando algunas cosas di con ella y vengo a exponerla.

PHP permite implementar nuestro propio manejador de sesiones. En este blog ya hemos hecho alguno anteriormente, justo aquí: implementar session handler en PHP.

Pues  bien, vamos a la solución y explicación.

Explicación de Cannot change save handler when session is active

El mensaje lo dice todo. No se puede cambiar el manejador de sesiones mientras la sesión está activa. Esto tiene bastante sentido.

Es decir, no podemos hacer esto:

<?php
session_start();
session_set_save_handler(/*El handler aquí*/);
$_SESSION["foo"] = "bar";
echo $_SESSION["foo"];
?>

Ya que como vemos, estamos llamando a session_start y después estamos llamando a session_set_save_handler. Por lo que primero debemos llamar a session_set_save_handler y luego utilizar la sesión.

Pero el problema no es así de simple, ya que, ¿Qué pasa cuando leemos dos veces la sesión? por ejemplo, puede que tengamos dos métodos que leen la sesión, y que se ejecuten uno después del otro. La llamada de las sesiones sería así:

<?php

/*
Esto genera un error de que la sesión ya ha sido iniciada pero este no es el
problema ahora, eso se soluciona con session_status y bla bla
*/

#--Esto lo hace el primer método
session_set_save_handler(/*El handler*/);
session_start();
$_SESSION["foo"] = "bar";
#Hasta aquí todo bien

#--Esto lo hace el segundo método

/*
Justo aquí abajo está el error...
la sesión está siendo leída o escrita, porque la iniciamos allá arriba
pero estamos intentando registrar un handler, y por eso
se genera la advertencia, pues no se puede registrar un handler mientras
leemos o escribimos
*/
session_set_save_handler(/*El handler*/);
session_start();
$_SESSION["foo"] = "bar";
#Hasta aquí todo bien

?>

El huevo o la gallina

Y aquí entramos en un dilema. La solución más fácil sería ver si no hay datos de sesión, y si  no los hay, entoncesregistramos el handler. Algo así:

<?php
if(!isset($_SESSION["bla"])) session_set_save_handler(/*Handler*/);
?>

¿Pero cómo leemos datos de sesión de nuestro handler, si no lo hemos registrado? y recordemos que si lo registramos, generará un error. Pero si no lo registramos, no podremos comprobar si ya ha sido registrado.

Analogía del huevo y la gallina con Warning: session_set_save_handler(): Cannot change save handler when session is active

Analogía del huevo y la gallina con Warning: session_set_save_handler(): Cannot change save handler when session is active

Se parece al problema del huevo y la gallina. Ya que si no registramos el handler, estaremos leyendo datos de sesión del manejador propio de PHP, no del nuestro. Pero no podemos registrar el handler porque genera un error. Error que evitamos leyendo datos de la sesión con el handler registrado.

Solución a Cannot change save handler when session is active

La solución es sencilla. Ya que no podemos registrar nuestro handler porque eso genera un error, pero para omitir el error debemos leer datos de la sesión de nuestro handler, podemos mejor comprobar si está definido el arreglo superglobal $_SESSION.

Dicho arreglo superglobal estará indefinido si no hay datos de sesion (independientemente del handler), así que podemos comprobarlo para ver si deberíamos o no registrar el manejador. Así:

<?php
if (!isset($_SESSION)){
  session_set_save_handler(/*El handler aquí*/);
}

De esta manera, no importa cuántas veces se llame a la sesión, pues el handler será registrado sólo una vez.

Variables globales en Python

Introducción

Las variables globales existen en casi todos los lenguajes de programación que conocemos. Hoy veremos cómo es que funcionan las variables globales en Python, un lenguaje muy utilizado.

Dicho funcionamiento es un poco raro si le queremos llamar así, pues la lectura y escritura de las variables globales en Python es distinta a lo que conocemos.

Leer y escribir variables globales en Python

Cómo declarar una variable

Para declarar una variable global en Python, la ponemos hasta arriba de nuestro script, fuera de todas las funciones o clases. Así:

mi_variable_global = 20

def una_funcion():
  print("Soy una función")

Cómo acceder o leer la variable

Siguiendo el ejemplo de arriba, podemos leer nuestra variable dentro de la función, así:

mi_variable_global = 20

def una_funcion():
  print("Soy una función. La variable global es: " + str(mi_variable_global))

Si ejecutamos el script y llamamos a la función, se ve así :

Leyendo valor de variable

Leyendo valor de variable

Y todo bien hasta aquí. Podemos leer cualquier variable desde cualquier lugar.

Cómo modificar una variable global

Modificar una variable global es, normalmente, peligroso. Esto es debido a que en ocasiones pensamos que nos referimos a una variable local, cuando en realidad lo hacemos con una global.

Por otro lado, a veces necesitamos declarar una variable local con el mismo nombre que la global.

Por ello Python provee la palabra reservada “global” que funciona para indicarle que queremos modificar una variable global.

Veamos este ejemplo en donde leemos la variable desde dos funciones:

mi_variable_global = 20

def una_funcion():
    print("Una función, la variable global es: " + str(mi_variable_global))

def otra_funcion():
    print("Otra función, la variable global es: " + str(mi_variable_global))

una_funcion()
otra_funcion()

Todo bien hasta aquí. Si modificamos la variable en una_funcion, nosotros esperamos que al llamar a otra_funcion, el valor sea el que asignamos en una_funcion. Probemos con este código…

mi_variable_global = 20

def una_funcion():
    mi_variable_global = 10 #Ahora la variable es 10, no 20
    print("Una función, la variable global es: " + str(mi_variable_global))

def otra_funcion():
    print("Otra función, la variable global es: " + str(mi_variable_global))

una_funcion()
otra_funcion()

Y al ejecutar:

Intentar modificar variable global en Python

Intentar modificar variable global en Python

He ahí lo raro. ¿Por qué no se está cambiando el valor de la variable global y sólo funciona dentro de la función?

Esto es porque Python no dejará que modifiquemos la variable globalmente al menos que lo indiquemos. Claro que podemos leerla, pero no modificarla sin indicarlo explícitamente usando global.

Si queremos modificarla, hacemos esto:

mi_variable_global = 20

def una_funcion():
    global mi_variable_global #En este caso sí vamos a modificar a la global
    mi_variable_global = 10 #Ahora la variable es 10, no 20
    print("Una función, la variable global es: " + str(mi_variable_global))

def otra_funcion():
    print("Otra función, la variable global es: " + str(mi_variable_global))

una_funcion()
otra_funcion()

La salida ahora sí es la correcta, pues hemos modificado la variable global:

Modificando valor de variables globales en Python

Modificando valor de variables globales en Python

Conclusión

Como vimos, las variables globales en Python son un poco raras pero podemos manejarlas a nuestro antojo.

Si queremos leerlas no hay problema, y si queremos modificarlas sólo usamos la palabra reservada global.

APN Weex México: MCC y MNC

Introducción

Weex es una compañía telefónica que opera en méxico. En su página aparece cómo configurar el APN Weex, en donde nos da el nombre y el APN.

El problema viene cuando no nos brinda la información que va en algunos campos obligatorios como el MCC o el MNC. Sin dichos datos no podremos guardar la configuración.

Busqué mucho tiempo qué valores poner en los siguientes campos:

  • MCC
  • MNC
  • Tipo de autenticación
  • Nombre de usuario
  • Contraseña

No encontré mucha información al respecto o la solución exacta. Tampoco recuerdo ni dónde ni cómo, pero pude encontrar los faltantes y vengo a compartirlo.

Configurar MCC, MNC y todo lo demás en APN Weex

Recordemos que Weex utiliza las antenas de Movistar (o no sé qué cosa exactamente, no soy experto en esa área). Tomando en cuenta eso, intenté llenar los campos faltantes que Weex no proporciona con los de Movistar.

Los datos faltantes son los siguientes:

  • Nombre: Weex
  • APN: internet.weex.mx
  • Nombre de usuario: movistar
  • Contraseña: movistar
  • MCC: 334
  • MNC: 030
  • Tipo de autenticación: PAP
  • Tipo de APN: default

Abajo adjunto las capturas de pantalla que muestran la configuración en Android. Puedes abrirlas para ampliarlas y verlas en tamaño completo.

Captura 1 APN Weex

Captura 1 APN Weex

Configuración Captura 2

Configuración Captura 2

Paso 3 de configuración APN

Paso 3 de configuración APN

 

 

 

 

 

 

strlen y mb_strlen en PHP

Introducción

En muchas ocasiones en PHP necesitaremos leer u obtener la longitud de una cadena o string. PHP provee dos funciones: strlen y mb_strlen.

Dichas funciones funcionan casi igual, pero una ayuda más que la otra. Sigamos leyendo para entender la dferencia.

Continue reading…

JavaScript: comprobar si números están ordenados en orden ascendente

Introducción

Hoy veremos otro ejercicio de codewars muy simple. Se trata de comprobar si todos los números en un arreglo están en orden ascendente.

Recordemos que un arreglo, vector o array es una colección de elementos. Uno de números, ordenado de manera ascendente se vería así:

[1, 4, 5, 80, 100, 500]

En cambio, uno no ordenado de manera ascendente, así:

[1, 3, 2, 50, 80]

Nuestra tarea es escribir una función que compruebe si los elementos están o no ordenados de esa manera.

Solución

La solución que yo propuse es la siguiente:

Si JavaScript ya provee una forma de ordenar arreglos con array.sort, entonces ordenamos el arreglo  (de manera ascendente) y lo guardamos en una variable.

Luego, comparamos el arreglo original con el que ya ordenamos previamente y si son exactamente iguales, entonces la respuesta es que el arreglo original sí estaba ordenado de esa forma.

El código es este:

const inAscOrder = arr => arr.join("") === arr.sort((a, b) => a-b).join("");

Muy corto pero lo que hacemos es ver si al convertir ambos arreglos a cadena son iguales. Esto, en código más expresivo y compatible con versiones anteriores de JS, se vería así:

var inAscOrder = function inAscOrder(arr) {
  return (
    arr.join("") ===
    arr
      .sort(function(a, b) {
        return a - b;
      })
      .join("")
  );
};

Probando

Aquí una prueba llamando a la función:

Otra solución

La solución que obtuvo más votos es esta:

function inAscOrder(arr) {
  return arr.every(function(_, i) {
    return i == 0 || arr[i] > arr[i - 1];
  });
}

Llamamos a la función every de los arreglos. Esta función devuelve true si, al iterar todo el arreglo, se devuelve true dentro de la función.

Con una vez que la condición no se cumpla (es decir, aunque 100  veces sea true pero 1 sea false) entonces se devuelve false.

Para devolver true o false, dentro de la función comparamos  si el valor actual es mayor que el valor anterior.

En la primera iteración no podremos comparar con el valor anterior (porque accederíamos al índice -1) pero por ello está la condición que dice:

Devuelve true si i (o sea, el índice) es 0, o el elemento actual es mayor al anterior.

Error en la solución

No sé si haya un error en esta solución, ya que al probarla con un arreglo así:

[1, 4, 5, 80, 80, 100, 500]

Devuelve false.

Pero si miramos al arreglo, todos los datos están en orden ascendente. El 80 se repite, pero no por ello cambia el orden. Lo que pasa es que se está comparando si es mayor, no mayor o igual.

Para arreglar esto, el código quedaría así:

function inAscOrder(arr) {
  return arr.every(function(_, i) {
    return i == 0 || arr[i] >= arr[i - 1];
  });
}

Con estos resultados:

Python: agregar pie de página a PDF

Introducción

Ya hemos trabajado anteriormente con Python para muchas cosas. Hoy veremos cómo poner un pie de página a cualquier documento PDF.

Veremos cómo poner un texto, número o cualquier mensaje que queramos.

Esto funciona cuando queremos poner nuestro nombre a algún documento, o modificar un documento PDF que no podemos editar.

El proceso es realmente sencillo, sólo necesitamos Python y algunas librerías que instalaremos con Pip. Recuerda que si no tienes instalado pip o Python, aquí hay un excelente tutorial.

Librerías o requisitos

Como siempre, necesitamos Python y pip. Aquí un tutorial.

Una vez que tengamos todo instalado, necesitamos 2 librerías más: PyPDF2 y ReportLab. Más abajo veremos cómo instalarlas.

Instalar PyPDF2

Ejecutamos lo siguiente en la línea de comandos:

pip install PyPDF2

Instalar ReportLab

Ejecutamos lo siguiente:

pip install reportlab

Ahora sí estamos listo

Archivo de ejemplo

Para ejemplificar lo que aquí se verá, se utilizará un PDF normal que es de uno de mis posts:

post

Poner mensaje en pie de página

Para mí, esta opción es la más útil. Podría servir para poner nuestra “marca de agua” en los documentos que generemos. En fin, podemos poner cualquier mensaje deseado.

from PyPDF2 import PdfFileWriter, PdfFileReader
import io
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter


nombre_pdf_original = "post.pdf" # Cambia aquí el nombre de tu documento original
nombre_pdf_salida = "post_con_mensaje.pdf" # Cambia aquí el nombre del PDF de salida
mensaje = "Este es un pie de página :)"
packet = io.BytesIO()

mi_canvas = canvas.Canvas(packet, pagesize=letter)

"""
    También podemos cambiar las coordenadas del mensaje.
    La posición (0, 0) es la esquina inferior izquierda
    por eso es que nuestro mensaje sale tan cerca a 
    dicho lugar
"""
mi_canvas.drawString(5, 5, mensaje)
mi_canvas.save()

packet.seek(0)
pdf_con_pie = PdfFileReader(packet)

pdf_existente = PdfFileReader(open(nombre_pdf_original, "rb"))
output = PdfFileWriter()

# Iterar desde 0 hasta el número de páginas de nuestro documento
numero_de_paginas = pdf_existente.getNumPages()
for numero in range(0, numero_de_paginas):
    page = pdf_existente.getPage(numero)
    page.mergePage(pdf_con_pie.getPage(0))
    print("Agregando mensaje \"{}\" a la página {} de {}".format(mensaje, numero + 1, numero_de_paginas))
    output.addPage(page)

outputStream = open(nombre_pdf_salida, "wb")
output.write(outputStream)
outputStream.close()

El código se explica por sí mismo. Simplemente creamos un documento PDF temporal, con el mensaje que pusimos y luego lo “combinamos” con el original.

Así vamos haciendo por cada página y al final escribimos el contenido del stream en el archivo de salida.

Si ejecutamos el código se verá algo así:

Agregando pie de página a un documento PDF de ejemplo

Agregando pie de página a un documento PDF de ejemplo

El resultado es este:

post_con_mensaje

Sencillo pero muy útil. Tal vez más tarde podríamos agregar una interfaz gráfica para facilitar el uso de este programa. Incluso podríamos empaquetar esto.

Conclusión

Te recomiendo leer la documentación oficial de ReportLab aquí, así como la de PyPDF2 aquí. Igualmente te invito a leer otros tutoriales de Python.

Cambiar zona horaria en Linux Ubuntu

Introducción

Hoy veremos cómo cambiar la zona horaria de Ubuntu para que coincida con nuestra hora local.

La fecha y hora son una cosa muy importante al trabajar con servidores. Por ejemplo, a veces los respaldos que realicemos toman su nombre de los valores del tiempo.

Si estos valores están mal configurados, habrá algunos errores inesperados que es mejor no tener.

Cambiar zona horaria en Ubuntu

Cabe mencionar que esto se hace desde la línea de comandos y que necesitamos permisos de administrador para realizarlo. El comando en cuestión es este:

sudo dpkg-reconfigure tzdata

Con ello, y después de poner nuestra contraseña, aparecerá esta interfaz:

Seleccionar área geográfica

Seleccionar área geográfica

Buscaremos la opción America (o la de nuestra región) y presionaremos Enter. Aparecerá esta otra:

Seleccionar ciudad correspondiente a nuestro timezone

Seleccionar ciudad correspondiente a nuestro timezone

Y ahí seleccionamos la de nuestro país. En mi caso pongo a México pero todo depende del lugar que deseemos configurar. Finalmente, nos mandará un mensaje de confirmación:

Confirmación de cambio de zona horaria

Confirmación de cambio de zona horaria

Ahí podemos comprobar la hora local y veremos que está correcta. Con eso hemos terminado.

Script para respaldar carpeta en Linux

Introducción

Programando un script que automatiza algunas cosas me di cuenta de que necesitaba otro script para respaldar determinada carpeta. Es decir, respaldar o hacer un backup de un directorio usando un script de bash.

Lo que hace este script no es nada nuevo, pero funciona muy bien. Lo escribí hace casi un año pero funciona.

Simplemente empaqueta o comprime todo el contenido de un directorio a un archivo .tar.gz. Podemos, más tarde y con algunas pequeñas modificaciones, programarlo con cron pero eso es otra historia.

Actualización: respaldar carpeta periódicamente

Ya he escrito la continuación de este script sobre cómo utilizar cron para respaldar el directorio cada cierto tiempo.

Script para respaldar

Hablar es de mal gusto, así que aquí el código del script para respaldar un directorio y más abajo lo explicamos:

En la primera línea vemos el Shebang, que es un comentario (muy importante) indicando la ruta de la ejecución, que en este caso es el binario de bash.

Luego obtenemos la fecha y hora para llamar así a nuestro respaldo (por ejemplo, respaldo_28-11-1996_21-06-00.tgz)

Definimos algunas variables como el nombre del archivo, la carpeta en donde serán guardados los respaldos y la carpeta que respaldaremos. Esta variable es la que cambiaremos según lo que deseemos respaldar (valga la redundancia)

Más tarde, con mkdir creamos la carpeta en donde guardaremos los backups. Lo llamamos con la opción p para que, si el directorio no existe, sea creado.

Finalmente llamamos a tar con las opciones cfvz. Me parece que la c es para crear, la v para que sea verboso o imprima mensajes de debug, la z para que los archivos sean comprimidos con gzip y f para que opere sobre el archivo que le estamos pasando como argumento.

Modo de uso

Lo ponemos en algún lugar preferentemente en /home/tu_usuario

No olvidemos darle permisos de ejecución con:

chmod 755 respaldar.sh

Cambiamos el nombre de las variables (dependiendo de la carpeta que vayamos a respaldar, así como el directorio que cambiará los respaldos) y luego lo ejecutamos con:

./respaldar.sh

Ejemplo

Voy a descargar el script y lo pondré en /home/luis

En la carpeta “cosas_importantes” tengo algunos archivos txt. Se supone que toda esa carpeta será respaldada.

Listado de archivos a respaldar

Listado de archivos a respaldar

Damos permiso de ejecución al script y lo ejecutamos. Como vemos, tar nos dice cuáles archivos está comprimiendo:

Ejecución del script

Ejecución del script

Con ello se habrá creado la carpeta “respaldos” y dentro de ella está nuestro backup con el nombre que le pusimos

Comprobación de backup creado con script para respaldar

Comprobación de backup creado con script para respaldar