<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>JBorbon blog</title>
    <link>http://jborbon.me</link>
    <description>I rant and rave until I have no sense of time.</description>
    <atom:link xmlns:atom="http://www.w3.org/2005/Atom" href="https://jborbon.me/feed" type="application/rss+xml" rel="self"/>
    <pubDate>Tue, 13 Mar 2018 19:34:16 -0600</pubDate>
    <item>
      <title><![CDATA[Reseña de "By Bizarre Hands"]]></title>
      <link>https://jborbon.me/resena-de-by-bizarre-hands</link>
      <description><![CDATA[<p><img src="https://i.imgur.com/s7zCscv.jpg" class="img-responsive img-thumbnail center-block" /></p>
<p>Desde hace bastante tiempo me considero fan de las antologías de terror. En muchos casos las prefiero a leer una novela completa. </p>
<p>Este es la primer antología (y primer libro) que leo de Joe R. Lansdale, así que no tengo punto de referencia con este autor. No recuerdo cómo di con él, ni tampoco si ya lo había escuchado mencionar antes. Pero en fin, vayamos historia por historia, sin <em>spoilear</em>.</p>
<hr />
<h3>Fish Night</h3>
<p>La historia con la que arranca el libro es bastante corta (nueve páginas en la edición que tengo).  Trata sobre un padre y su hijo, quienes se quedan varados en una carretera por tomar el camino equivocado. </p>
<p>Me recordó un poco al estilo de Ray Bradbury por el toque fantástico y ambigüo. Creo que no sucede lo suficiente como para adentrarnos y conocer mejor a los personajes, aunque tal vez sea la intención de Lansdale. </p>
<p>No creo que califique como terror. Es bastante olvidable, en mi opinión, a no ser que no haya entendido el punto de la historia. </p>
<h3>The Pit</h3>
<p>Un poco más extensa que la anterior, pero sin mucho detalle, tampoco. Sé que no hay el mismo nivel de inmersión en una historia corta que en una novela, pero en fin.</p>
<p>Aquí conocemos una especie de secta(¿?) que se dedica a capturar hombres para obligarlos a realizar trabajo pesado y a pelear a muerte entre ellos. La historia inicia ya cuando el protagonista, llamado Harry, tiene que enfrentarse al que actualmente ha sobrevivido a todas las peleas. </p>
<p>Conocemos un poco del pasado de Harry y de como la vida de alguien puede cambiar tan repentinamente.  No sé si realmente hay una moraleja, pero quizá lo que la historia trata de hacer que nos preguntemos es si en realidad la vida que el protagonista llevaba antes era igual o peor que el infierno en el que se encuentra atrapado.</p>
<p>Sin duda mejor que la anterior, pero no podría decir que es de mis preferidas.</p>
<h3>Duck Hunt</h3>
<p>Esta historia apenas llega a las cuatro páginas. Trata sobre un adolescente que vive en un lugar en el que la cacería de animales (en este caso, patos) parece ser gran cosa; no solo un deporte, si no incluso una especie de rito de iniciación para que los niños se vuelvan hombres.</p>
<p>Se nos deja ver que Freddie (el protagonista), quien cuenta con quince años de edad, está más interesado en los comics que en disparar escopetas. Pero su padre está dispuesto a enseñarle que no solo son los patos lo interesante de la cacería.</p>
<p>Una historia algo oscura y quizá con un sabor amargo al final, pero me dejó indiferente porque como mencioné antes, no hay suficiente desarrollo como para sentir algo por los personajes. Fue más bien como: &quot;<em>Rednecks, am I right?</em>&quot;.</p>
<h3>By Bizarre Hands</h3>
<p>La historia que da título al libro trata sobre un sacerdote quien se siente culpable por la muerte de su hermana, quien fue asesinada cuando eran niños. Ella padecía una discapacidad mental (aunque el sacerdote se refiere a ella como &quot;retrasada&quot;), pero el trataba de hacer que se divirtiera a pesar de su impedimento.</p>
<p>En la época actual de la historia, el sacerdote conoce a una mujer quien tiene una hija con discapacidad mental, y decide que tiene que ayudarla, como no pudo hacer con su propia hermana.</p>
<p>Tal vez el haber leído un puño de historias de distintos autores a la fecha me haya ayudado a deducir gran parte del final. Incluso para no ser tan corta, creo que el final se siente abrupto y predecible. Si lo que Joe Lansdale trata de mostrarnos es que hay gente con visiones torcidas del mundo, lo entiendo, ¿pero cuando decidí leer esto sabía de qué iba, no? Tampoco esperaba encontrar una lista de frases motivacionales. </p>
<h3>The Steel Valentine</h3>
<p>Una historia de violencia y adulterio. Un hombre descubre de lo que es capaz el esposo de su amante al enterarse de su amorio. Venganza estilo mafia. Una historia bastante directa, no recomendada si eres sensible a violencia animal.</p>
<p>La verdad es que, como dije, es bastante directa. No hay &quot;vueltas de tuerca&quot; ni nada por el estilo. Y como ya me cansé de repetir, la historia no tiene suficientes páginas como para empatizar con nadie.</p>
<h3>I Tell You It's Love</h3>
<p>Una relación basada en el dolor físico es lo que se nos presenta en esta ocasión. Una pareja sadomasoquista... pero ella quiere ir cada vez más lejos; les toca descubrir a ustedes hasta donde. </p>
<p>La historia es corta, pero me gustó. Es una muestra de lo que puede llegar a hacer alguien por amor, o como lo dice el título de la historia: &quot;te digo que es amor&quot;. Como nota adicional quiero mencionar que me recordó a una historia de Boris Vian llamada &quot;Los perros, el deseo y la muerte&quot;, que es bastante recomendable.</p>
<h3>Letter from the South, Two Moons West of Nacogdoches</h3>
<p>Supongo que esta historia trata sobre la hipocresía de la religión, o de como algunas personas la distorsionan a su conveniencia. Se trata de una persona escribiendo una carta a otra. En ella menciona ciertas cosas que no le hacen sentido acerca de la religión bautista; cosas que se contradicen, pero al mismo tiempo menciona que tiene que asistir a una ejecución pública que no puede perderse. Al parecer se trata de una realidad alterna; un western en el que se menciona a &quot;un tal Martin Luther King&quot;. </p>
<p>En esta historia nuevamente se toca el tema del racismo. Y como en otras anteriores, sus personajes usan el término <em>nigger</em> con bastante frecuencia. La pongo en la categoría &quot;Qué hubiera pasado si...&quot;. Se nos muestra una versión más oscura (<em>no pun intended</em>) de lo que ya es la realidad.</p>
<h3>Boys Will Be Boys</h3>
<p>Un chico de secundaria que siempre se había sentido diferente, raro, conoce a otro chico a quien describe como &quot;pura energía&quot;, alguien que consigue lo que quiere, alguien que lo hace sentir que hay más como ellos. </p>
<p>Una historia de las que hacen que nos preguntemos &quot;¿Las personas se vuelven malas o ya nacen así?&quot; Manzanas podridas. </p>
<p>La verdad es que la historia comienza bien pero al final es como &quot;meh&quot;. Pareciera como la introducción o capítulo de lo que pudiera ser una novela <sup id="fnref1:night-runners"><a href="#fn:night-runners" class="footnote-ref">1</a></sup>.  Justo por el final me quedé pensando si leí algo sobre la condición humana o algo sobre fuerzas sobrenaturales. No estoy seguro.</p>
<p>Al igual que con <em>The Steel Valentine</em>, no la recomiendo para quienes son sensibles a la violencia animal, ya que esta en particular es algo descriptiva.</p>
<h3>The Fat Man and the Elephant</h3>
<p>Otra historia donde el protagonista es un sacerdote. En esta ocasión la trama se desarrolla en una especie de museo ambulante, de esos que supuestamente muestran criaturas raras pero en realidad solamente son farsas.
En este museo hay un elefante viejo, que es del interés del sacerdote. Este acude al lugar con frecuencia para ver al elefante, que se encuentra en un establo. Ahí, con ayuda de un trabajador negro del museo (sí, como en otras de las historias, hace bastante énfasis en que es negro), realiza una especie de ritual que involucra al elefante para obtener ciertas revelaciones. </p>
<p>Lo que entendí de esta historia es que cada quien cree lo que quiere creer. Lo digo porque por una parte, el sacerdote está convencido de lo que obtiene a través del ritual es algo muy valioso, como si se tratara de la verdad absoluta; por su parte, el trabajador del museo parece que solo le sigue el juego al sacerdote con tal de recibir un pago.</p>
<h3>Hell Through a Windshield</h3>
<p>Este es un ensayo del autor sobre la cultura de los autocinemas en Texas, en la década de los 80. Habla sobre la historia del autocinema, que comenzó oficialmente desde los años 30 en New Jersey. También menciona sobre una pesadilla recurrente y bizarra que tenía, y todo sucedía, adivinaron, en un autocinema.</p>
<p>Me pareció interesante aprender un poco de la experiencia de ver películas de esta manera.</p>
<h3>Down by the Sea Near the Great Big Rock</h3>
<p>Una familia se encuentra acampando en una playa. Pero en este lugar hay algo que cambia el comportamiento de quienes están cerca.</p>
<p>Una historia corta, pero directa y efectiva. No la más original, pero con elementos de una buena trama de terror.</p>
<h3>Trains not Taken</h3>
<p>No considero que esta historia califique como terror. Trata sobre un hombre viajando en tren con su esposa. Durante el viaje conoce a otro pasajero (quien también viaja con su respectiva esposa) y a una mujer atractiva que llama la atención de ambos.</p>
<p>En sí la historia trata sobre el protagonista reflexionando sobre su matrimonio; de que ya no es como antes; de si aún vale la pena. El título de la historia proviene de algo que el otro pasajero dice al protagonista, una especie de analogía sobre las posibilidades o diferentes variantes del futuro de cada persona; de todo lo que podría haber sido bajo otras circunstancias.</p>
<p>Me pareció buena a secas.</p>
<h3>Tight Little Stitches in a Dead Man's Back</h3>
<p>Esta es la historia que más me ha gustado. No sé si gran parte se debe a la temática o porque, en conjunto, me ha parecido bastante buena.</p>
<p>Trata sobre un hombre escribiendo un diario sobre los sucesos ocurridos durante varios años en un mundo post-apocalíptico. Al parecer hubo una especie de guerra, porque se mencionan bombas y sus efectos secundarios; y como en muchas historias de este tipo, hay criaturas producto de dichos efectos secundarios.</p>
<p>El hombre y su esposa viven en una especie de búnker, junto con más personas. En el diario cuenta lo ocurrido en ese lugar y en el mundo exterior. Durante el tiempo que es posible seguir viviendo ahí.</p>
<h3>The Windstorm Passes</h3>
<p>La historia trata sobre un chico que lleva una vida tranquila en la granja de sus padres. Durante el invierno, su padre y él, encuentran a un hombre con problemas mentales (hijo de una familia de los alrededores) casi congelado por exposición y se lo llevan para auxiliarlo. No logran salvarlo, pero el hombre antes de morir les dice que tiene un mensaje para ellos: están condenados y el viento los arrastrará.</p>
<p>Premonición o coincidencia, un incidente relacionado con el viento cambia la vida del chico y su familia.</p>
<p>La verdad es que no entendí como podría cuajar la historia al final. Me dejó la misma sensación que <em>Boys Will Be Boys</em>: parece la semilla para el capítulo de una novela, pero para una historia corta me resultó bastante anticlimática.</p>
<h3>Night They Missed the Horror Show</h3>
<p>Según leí en alguna parte, esta es de las historias más conocidas de Joe Lansdale. Trata sobre un par de adolescentes pensando en como matar el tiempo, después de que su primera opción de entretenimiento no les pareció tan buena idea: ir al autocinema a ver &quot;Night of the Living Dead&quot;. ¿Por qué decidieron no ir? Número uno: porque no había mujeres que quisieran tener una cita con ellos; número dos: porque el protagonista de la película era &quot;un negro&quot;.</p>
<p>Su ocio hace que al ver el cadaver de un perro atropellado sobre la carretera, decidan que sería divertido atarlo a una cadena y arrastralo con su auto. Después lamentarán no haber seguido con el plan original de haber ido al autocinema</p>
<p>Creo que de nueva cuenta un elemento presente en esta historia es el cinismo que puede acompañar la visión del mundo de las personas; lo que cada quien considera normal y correcto, en contraste con lo que ve como algo aberrante e imperdonable.</p>
<h3>On the Far Side of the Cadillac Desert with Dead Folks</h3>
<p>¿Soy yo o a Joe Lansdale le gusta usar títulos largos para sus historias? </p>
<p>De nuevo tenemos una ambientación post-apocalíptica, ahora con un toque western. Diría que se nota mucho la ausencia de tecnología por haberse desmoronado la civilización, pero luego recuerdo que este libro fue escrito a finales de los ochenta, entonces tampoco había tanta tecnología como ahora.</p>
<p>El protagonista es una especie de mercenario que va en busca de un criminal conocido por haber matado a otros mercenarios, por el que se ofrece una recompensa. Al criminal lo encuentra en una especie de bar donde hay un espectáculo algo peculiar... pues verán, en esta versión del mundo hubo un accidente en un laboratorio, un virus se mezcló en el aire y pues... muertos vivientes. Estos cadaveres andantes se venden como mercancía y uno de sus usos (sobre todo con mujeres) es amputarles las manos y cerrarles la mandíbula con metal para que no puedan, ya saben, comerse a la gente y esas cosas, y así servir de entretenimiento en bares de mala muerte (de mala muerte, ¿entendieron?).</p>
<p>Atrapar al criminal será el menor de los problemas de nuestro mercenario, pues no está solo y descubrirá que hay alguien con planes de desafiar la muerte, que es lo que más abunda en este mundo alternativo.</p>
<p>De nuevo, me atrapó la temática zombie / post-apocalíptica. Es interesante ver el mundo que plantea Lansdale durante una amenaza de zombies. Sé que no es el primero en imaginarse esto, pero en su caso nos trae una visión decadente y pesimista como parece ser su estilo. George Romero ya había hecho alusión a esto en sus películas: ¿quiénes son los verdaderos monstruos, los vivos o los muertos? <sup id="fnref1:book-of-the-dead"><a href="#fn:book-of-the-dead" class="footnote-ref">2</a></sup>. En su versión, Lansdale nos muestra un mundo más depravado y sin esperanza, que haría cuestionarnos si realmente es mejor ser parte de los que siguen vivos.</p>
<h3>Veredicto</h3>
<p>Hay varios elementos que parecen ser recurrentes en la literatura de Joe R. Lansdale: violencia, depravación, racismo; lo que realmente ocurre detrás del panorama que se aprecia en la superficie. Al igual que otros escritores, como Stephen King, Lansdale ambienta su mundo en el terreno que conoce de primera mano, en su caso, Texas. </p>
<p>Tal vez si estuviera más familizarizado con sus novelas y antologías podría dar un veredicto más objetivo, pero como no es el caso, me basaré solamente en esta colección. La verdad se me hizo regular (¿irregular?): hay historias que sobresalen, pero siento que la mayoría son olvidables. Aún así, el libro me despertó suficiente interés para querer leer más de Joe Lansdale, pero más que nada, novelas; para saber si lo suyo son las historias más desarrolladas.</p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:night-runners">
<p>esta historia se convirtió en lo que después sería la novela <a href="https://www.goodreads.com/book/show/219734.The_Nightrunners?from_search=true">The Nightrunners</a>&#160;<a href="#fnref1:night-runners" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:book-of-the-dead">
<p>esta historia se publicó en otra antología con temática zombie llamada <a href="https://www.goodreads.com/book/show/208161.Book_of_the_Dead">Book of the Dead</a>&#160;<a href="#fnref1:book-of-the-dead" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
</ol>
</div>]]></description>
      <content:encoded><![CDATA[<p><img src="https://i.imgur.com/s7zCscv.jpg" class="img-responsive img-thumbnail center-block" /></p>
<p>Desde hace bastante tiempo me considero fan de las antologías de terror. En muchos casos las prefiero a leer una novela completa. </p>
<p>Este es la primer antología (y primer libro) que leo de Joe R. Lansdale, así que no tengo punto de referencia con este autor. No recuerdo cómo di con él, ni tampoco si ya lo había escuchado mencionar antes. Pero en fin, vayamos historia por historia, sin <em>spoilear</em>.</p>
<hr />
<h3>Fish Night</h3>
<p>La historia con la que arranca el libro es bastante corta (nueve páginas en la edición que tengo).  Trata sobre un padre y su hijo, quienes se quedan varados en una carretera por tomar el camino equivocado. </p>
<p>Me recordó un poco al estilo de Ray Bradbury por el toque fantástico y ambigüo. Creo que no sucede lo suficiente como para adentrarnos y conocer mejor a los personajes, aunque tal vez sea la intención de Lansdale. </p>
<p>No creo que califique como terror. Es bastante olvidable, en mi opinión, a no ser que no haya entendido el punto de la historia. </p>
<h3>The Pit</h3>
<p>Un poco más extensa que la anterior, pero sin mucho detalle, tampoco. Sé que no hay el mismo nivel de inmersión en una historia corta que en una novela, pero en fin.</p>
<p>Aquí conocemos una especie de secta(¿?) que se dedica a capturar hombres para obligarlos a realizar trabajo pesado y a pelear a muerte entre ellos. La historia inicia ya cuando el protagonista, llamado Harry, tiene que enfrentarse al que actualmente ha sobrevivido a todas las peleas. </p>
<p>Conocemos un poco del pasado de Harry y de como la vida de alguien puede cambiar tan repentinamente.  No sé si realmente hay una moraleja, pero quizá lo que la historia trata de hacer que nos preguntemos es si en realidad la vida que el protagonista llevaba antes era igual o peor que el infierno en el que se encuentra atrapado.</p>
<p>Sin duda mejor que la anterior, pero no podría decir que es de mis preferidas.</p>
<h3>Duck Hunt</h3>
<p>Esta historia apenas llega a las cuatro páginas. Trata sobre un adolescente que vive en un lugar en el que la cacería de animales (en este caso, patos) parece ser gran cosa; no solo un deporte, si no incluso una especie de rito de iniciación para que los niños se vuelvan hombres.</p>
<p>Se nos deja ver que Freddie (el protagonista), quien cuenta con quince años de edad, está más interesado en los comics que en disparar escopetas. Pero su padre está dispuesto a enseñarle que no solo son los patos lo interesante de la cacería.</p>
<p>Una historia algo oscura y quizá con un sabor amargo al final, pero me dejó indiferente porque como mencioné antes, no hay suficiente desarrollo como para sentir algo por los personajes. Fue más bien como: &quot;<em>Rednecks, am I right?</em>&quot;.</p>
<h3>By Bizarre Hands</h3>
<p>La historia que da título al libro trata sobre un sacerdote quien se siente culpable por la muerte de su hermana, quien fue asesinada cuando eran niños. Ella padecía una discapacidad mental (aunque el sacerdote se refiere a ella como &quot;retrasada&quot;), pero el trataba de hacer que se divirtiera a pesar de su impedimento.</p>
<p>En la época actual de la historia, el sacerdote conoce a una mujer quien tiene una hija con discapacidad mental, y decide que tiene que ayudarla, como no pudo hacer con su propia hermana.</p>
<p>Tal vez el haber leído un puño de historias de distintos autores a la fecha me haya ayudado a deducir gran parte del final. Incluso para no ser tan corta, creo que el final se siente abrupto y predecible. Si lo que Joe Lansdale trata de mostrarnos es que hay gente con visiones torcidas del mundo, lo entiendo, ¿pero cuando decidí leer esto sabía de qué iba, no? Tampoco esperaba encontrar una lista de frases motivacionales. </p>
<h3>The Steel Valentine</h3>
<p>Una historia de violencia y adulterio. Un hombre descubre de lo que es capaz el esposo de su amante al enterarse de su amorio. Venganza estilo mafia. Una historia bastante directa, no recomendada si eres sensible a violencia animal.</p>
<p>La verdad es que, como dije, es bastante directa. No hay &quot;vueltas de tuerca&quot; ni nada por el estilo. Y como ya me cansé de repetir, la historia no tiene suficientes páginas como para empatizar con nadie.</p>
<h3>I Tell You It's Love</h3>
<p>Una relación basada en el dolor físico es lo que se nos presenta en esta ocasión. Una pareja sadomasoquista... pero ella quiere ir cada vez más lejos; les toca descubrir a ustedes hasta donde. </p>
<p>La historia es corta, pero me gustó. Es una muestra de lo que puede llegar a hacer alguien por amor, o como lo dice el título de la historia: &quot;te digo que es amor&quot;. Como nota adicional quiero mencionar que me recordó a una historia de Boris Vian llamada &quot;Los perros, el deseo y la muerte&quot;, que es bastante recomendable.</p>
<h3>Letter from the South, Two Moons West of Nacogdoches</h3>
<p>Supongo que esta historia trata sobre la hipocresía de la religión, o de como algunas personas la distorsionan a su conveniencia. Se trata de una persona escribiendo una carta a otra. En ella menciona ciertas cosas que no le hacen sentido acerca de la religión bautista; cosas que se contradicen, pero al mismo tiempo menciona que tiene que asistir a una ejecución pública que no puede perderse. Al parecer se trata de una realidad alterna; un western en el que se menciona a &quot;un tal Martin Luther King&quot;. </p>
<p>En esta historia nuevamente se toca el tema del racismo. Y como en otras anteriores, sus personajes usan el término <em>nigger</em> con bastante frecuencia. La pongo en la categoría &quot;Qué hubiera pasado si...&quot;. Se nos muestra una versión más oscura (<em>no pun intended</em>) de lo que ya es la realidad.</p>
<h3>Boys Will Be Boys</h3>
<p>Un chico de secundaria que siempre se había sentido diferente, raro, conoce a otro chico a quien describe como &quot;pura energía&quot;, alguien que consigue lo que quiere, alguien que lo hace sentir que hay más como ellos. </p>
<p>Una historia de las que hacen que nos preguntemos &quot;¿Las personas se vuelven malas o ya nacen así?&quot; Manzanas podridas. </p>
<p>La verdad es que la historia comienza bien pero al final es como &quot;meh&quot;. Pareciera como la introducción o capítulo de lo que pudiera ser una novela <sup id="fnref1:night-runners"><a href="#fn:night-runners" class="footnote-ref">1</a></sup>.  Justo por el final me quedé pensando si leí algo sobre la condición humana o algo sobre fuerzas sobrenaturales. No estoy seguro.</p>
<p>Al igual que con <em>The Steel Valentine</em>, no la recomiendo para quienes son sensibles a la violencia animal, ya que esta en particular es algo descriptiva.</p>
<h3>The Fat Man and the Elephant</h3>
<p>Otra historia donde el protagonista es un sacerdote. En esta ocasión la trama se desarrolla en una especie de museo ambulante, de esos que supuestamente muestran criaturas raras pero en realidad solamente son farsas.
En este museo hay un elefante viejo, que es del interés del sacerdote. Este acude al lugar con frecuencia para ver al elefante, que se encuentra en un establo. Ahí, con ayuda de un trabajador negro del museo (sí, como en otras de las historias, hace bastante énfasis en que es negro), realiza una especie de ritual que involucra al elefante para obtener ciertas revelaciones. </p>
<p>Lo que entendí de esta historia es que cada quien cree lo que quiere creer. Lo digo porque por una parte, el sacerdote está convencido de lo que obtiene a través del ritual es algo muy valioso, como si se tratara de la verdad absoluta; por su parte, el trabajador del museo parece que solo le sigue el juego al sacerdote con tal de recibir un pago.</p>
<h3>Hell Through a Windshield</h3>
<p>Este es un ensayo del autor sobre la cultura de los autocinemas en Texas, en la década de los 80. Habla sobre la historia del autocinema, que comenzó oficialmente desde los años 30 en New Jersey. También menciona sobre una pesadilla recurrente y bizarra que tenía, y todo sucedía, adivinaron, en un autocinema.</p>
<p>Me pareció interesante aprender un poco de la experiencia de ver películas de esta manera.</p>
<h3>Down by the Sea Near the Great Big Rock</h3>
<p>Una familia se encuentra acampando en una playa. Pero en este lugar hay algo que cambia el comportamiento de quienes están cerca.</p>
<p>Una historia corta, pero directa y efectiva. No la más original, pero con elementos de una buena trama de terror.</p>
<h3>Trains not Taken</h3>
<p>No considero que esta historia califique como terror. Trata sobre un hombre viajando en tren con su esposa. Durante el viaje conoce a otro pasajero (quien también viaja con su respectiva esposa) y a una mujer atractiva que llama la atención de ambos.</p>
<p>En sí la historia trata sobre el protagonista reflexionando sobre su matrimonio; de que ya no es como antes; de si aún vale la pena. El título de la historia proviene de algo que el otro pasajero dice al protagonista, una especie de analogía sobre las posibilidades o diferentes variantes del futuro de cada persona; de todo lo que podría haber sido bajo otras circunstancias.</p>
<p>Me pareció buena a secas.</p>
<h3>Tight Little Stitches in a Dead Man's Back</h3>
<p>Esta es la historia que más me ha gustado. No sé si gran parte se debe a la temática o porque, en conjunto, me ha parecido bastante buena.</p>
<p>Trata sobre un hombre escribiendo un diario sobre los sucesos ocurridos durante varios años en un mundo post-apocalíptico. Al parecer hubo una especie de guerra, porque se mencionan bombas y sus efectos secundarios; y como en muchas historias de este tipo, hay criaturas producto de dichos efectos secundarios.</p>
<p>El hombre y su esposa viven en una especie de búnker, junto con más personas. En el diario cuenta lo ocurrido en ese lugar y en el mundo exterior. Durante el tiempo que es posible seguir viviendo ahí.</p>
<h3>The Windstorm Passes</h3>
<p>La historia trata sobre un chico que lleva una vida tranquila en la granja de sus padres. Durante el invierno, su padre y él, encuentran a un hombre con problemas mentales (hijo de una familia de los alrededores) casi congelado por exposición y se lo llevan para auxiliarlo. No logran salvarlo, pero el hombre antes de morir les dice que tiene un mensaje para ellos: están condenados y el viento los arrastrará.</p>
<p>Premonición o coincidencia, un incidente relacionado con el viento cambia la vida del chico y su familia.</p>
<p>La verdad es que no entendí como podría cuajar la historia al final. Me dejó la misma sensación que <em>Boys Will Be Boys</em>: parece la semilla para el capítulo de una novela, pero para una historia corta me resultó bastante anticlimática.</p>
<h3>Night They Missed the Horror Show</h3>
<p>Según leí en alguna parte, esta es de las historias más conocidas de Joe Lansdale. Trata sobre un par de adolescentes pensando en como matar el tiempo, después de que su primera opción de entretenimiento no les pareció tan buena idea: ir al autocinema a ver &quot;Night of the Living Dead&quot;. ¿Por qué decidieron no ir? Número uno: porque no había mujeres que quisieran tener una cita con ellos; número dos: porque el protagonista de la película era &quot;un negro&quot;.</p>
<p>Su ocio hace que al ver el cadaver de un perro atropellado sobre la carretera, decidan que sería divertido atarlo a una cadena y arrastralo con su auto. Después lamentarán no haber seguido con el plan original de haber ido al autocinema</p>
<p>Creo que de nueva cuenta un elemento presente en esta historia es el cinismo que puede acompañar la visión del mundo de las personas; lo que cada quien considera normal y correcto, en contraste con lo que ve como algo aberrante e imperdonable.</p>
<h3>On the Far Side of the Cadillac Desert with Dead Folks</h3>
<p>¿Soy yo o a Joe Lansdale le gusta usar títulos largos para sus historias? </p>
<p>De nuevo tenemos una ambientación post-apocalíptica, ahora con un toque western. Diría que se nota mucho la ausencia de tecnología por haberse desmoronado la civilización, pero luego recuerdo que este libro fue escrito a finales de los ochenta, entonces tampoco había tanta tecnología como ahora.</p>
<p>El protagonista es una especie de mercenario que va en busca de un criminal conocido por haber matado a otros mercenarios, por el que se ofrece una recompensa. Al criminal lo encuentra en una especie de bar donde hay un espectáculo algo peculiar... pues verán, en esta versión del mundo hubo un accidente en un laboratorio, un virus se mezcló en el aire y pues... muertos vivientes. Estos cadaveres andantes se venden como mercancía y uno de sus usos (sobre todo con mujeres) es amputarles las manos y cerrarles la mandíbula con metal para que no puedan, ya saben, comerse a la gente y esas cosas, y así servir de entretenimiento en bares de mala muerte (de mala muerte, ¿entendieron?).</p>
<p>Atrapar al criminal será el menor de los problemas de nuestro mercenario, pues no está solo y descubrirá que hay alguien con planes de desafiar la muerte, que es lo que más abunda en este mundo alternativo.</p>
<p>De nuevo, me atrapó la temática zombie / post-apocalíptica. Es interesante ver el mundo que plantea Lansdale durante una amenaza de zombies. Sé que no es el primero en imaginarse esto, pero en su caso nos trae una visión decadente y pesimista como parece ser su estilo. George Romero ya había hecho alusión a esto en sus películas: ¿quiénes son los verdaderos monstruos, los vivos o los muertos? <sup id="fnref1:book-of-the-dead"><a href="#fn:book-of-the-dead" class="footnote-ref">2</a></sup>. En su versión, Lansdale nos muestra un mundo más depravado y sin esperanza, que haría cuestionarnos si realmente es mejor ser parte de los que siguen vivos.</p>
<h3>Veredicto</h3>
<p>Hay varios elementos que parecen ser recurrentes en la literatura de Joe R. Lansdale: violencia, depravación, racismo; lo que realmente ocurre detrás del panorama que se aprecia en la superficie. Al igual que otros escritores, como Stephen King, Lansdale ambienta su mundo en el terreno que conoce de primera mano, en su caso, Texas. </p>
<p>Tal vez si estuviera más familizarizado con sus novelas y antologías podría dar un veredicto más objetivo, pero como no es el caso, me basaré solamente en esta colección. La verdad se me hizo regular (¿irregular?): hay historias que sobresalen, pero siento que la mayoría son olvidables. Aún así, el libro me despertó suficiente interés para querer leer más de Joe Lansdale, pero más que nada, novelas; para saber si lo suyo son las historias más desarrolladas.</p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:night-runners">
<p>esta historia se convirtió en lo que después sería la novela <a href="https://www.goodreads.com/book/show/219734.The_Nightrunners?from_search=true">The Nightrunners</a>&#160;<a href="#fnref1:night-runners" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:book-of-the-dead">
<p>esta historia se publicó en otra antología con temática zombie llamada <a href="https://www.goodreads.com/book/show/208161.Book_of_the_Dead">Book of the Dead</a>&#160;<a href="#fnref1:book-of-the-dead" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
</ol>
</div>]]></content:encoded>
      <guid>https://jborbon.me/resena-de-by-bizarre-hands</guid>
      <pubDate>Tue, 13 Mar 2018 19:34:16 -0600</pubDate>
    </item>
    <item>
      <title><![CDATA[uso de operadores bit a bit en PHP]]></title>
      <link>https://jborbon.me/uso-de-operadores-bit-a-bit-en-php</link>
      <description><![CDATA[<p>Al programar estamos acostumbrados a realizar distintos tipos de operaciones. Quizá las más comunes sean operaciones matemáticas, manipulación de strings y comparaciones. </p>
<p>Existe un conjunto de operadores que sirven para trabajar con datos a nivel de bit, o a nivel binario. En sistemas electrónicos estos son muy comunes, pero en aplicaciones web tal vez no tanto en comparación con el resto de operadores. Creo que en parte se debe a que en cualquier lenguaje se explica la función de estos operadores, pero no se dan ejemplos de su uso.</p>
<p>En este post se exponen  un par de casos de uso con operadores a nivel de bit. Trataré de explicar cada uno lo mejor que pueda.</p>
<hr />
<h2>Manejo de permisos</h2>
<p>Manejar permisos en una aplicación no es algo nuevo, ni algo exclusivo de los operadores bit a bit. Una forma común de implementar esta funcionalidad es almacenar un listado de permisos  disponibles en una base de datos y relacionarlos con los usuarios de la aplicación para determinar quien tiene permisos sobre cuales recursos: la o las tablas que representan los permisos contienen un registro por cada permiso disponible. </p>
<p>Con operadores bit a bit podemos almacenar distintos permisos en un solo valor decimal, aprovechando que un valor decimal también puede representarse como un número binario. De esta manera, cada uno de los bits o dígitos binarios que componen un número puede representar un permiso.</p>
<p><img src="https://i.imgur.com/VzAZ308.png" class="img-responsive img-thumbnail center-block" /></p>
<p>Para tratar de simplificar el post he utilizado solo seis posiciones en la imagen  (hasta 2<sup>5</sup>), pero el número de posiciones máximo depende del sistema operativo sobre el que se ejecute PHP. Por ejemplo, en sistemas de 32 bits un solo valor entero puede representar 32 posiciones/permisos (hasta 2<sup>31</sup>), mientras que en un sistema de 64 bits se puede almacenar 64 posiciones/permisos  (hasta 2<sup>63</sup>) en un solo valor entero.</p>
<p>¿Cuáles y qué clase de permisos podemos almacenar de esta manera? Los que deseemos. Utilicemos un ejemplo muy trillado: un blog. Vamos a suponer que deseamos controlar permisos relacionados con posts y comentarios. Podríamos tener los siguientes permisos: crear, editar y eliminar posts; crear, editar y eliminar comentarios.</p>
<p>En términos de variables, podrían quedar de la siguiente manera:</p>
<p><code>$createPost</code>, <code>$editPost</code>, <code>$deletePost</code>, <code>$createComment</code>, <code>$editComment</code>, <code>$deleteComment</code></p>
<p>Lo importante es que cada permiso debe ser una potencia de 2 (1, 2, 4...) O en otras palabras, valores que solo tengan un bit con valor 1 (0001, 0010, 0100...) Ya depende de nosotros el valor que asignemos a cada uno. Para fines del post usemos los siguientes valores:</p>
<pre><code>$createPost = 1;
$editPost = 2;
$deletePost = 4;
$createComment = 8;
$editComment = 16;
$deleteComment = 32;</code></pre>
<p>Ya con estos valores asignados y tomando como referencia la imagen anterior, tendríamos que el número entero 37 representa un grupo de permisos en los que están activos los bits correspondientes a las posiciones 1, 3 y 6 (de derecha a izquierda); es decir, están activos los permisos correspondientes a crear post, eliminar post y eliminar comentarios:</p>
<pre><code>// $userPermissions = 1 + 4 + 32
$userPermissions = $createPost + $deletePost  + $deleteComment</code></pre>
<p>El valor 63 de la imagen representaría un grupo de permisos donde todos los bits o permisos están activos:</p>
<pre><code>// $userPermissions = 1 + 2 + 4 + 8 + 16 + 32
$userPermissions = $createPost + $editPost + $deletePost  + $createComment + $editComment + $deleteComment</code></pre>
<p>Podemos ver que para agregar varios permisos simplemente sumamos su valor decimal. Esto es el equivalente a utilizar el primer operador a nivel de bit: OR. Un OR a nivel de bit es similar a un OR booleano. Una diferencia es que la comparación se hace con cada uno de los bits del valor binario; otra diferencia es que el símbolo de OR a nivel de bit es un símbolo <strong><em>pipe</em></strong> <strong>|</strong>, mientras que el OR booleano son dos símbolos <strong><em>pipe</em></strong> <strong>||</strong>.</p>
<p>Por ejemplo, si queremos agregar los permisos para crear y editar posts, tendríamos algo como lo siguiente:</p>
<pre><code>$userPermissions = $createPost  | $editPost;
var_dump($userPermissions); // imprime 3</code></pre>
<p>Podemos ver que es igual a haber sumado los valores de <code>$createPost</code> y <code>$editPost</code>. Pero hay que tener cuidado, <strong>esta equivalencia solamente se da cuando utilizamos valores que son potencia de 2 como en este caso</strong>. Veamos qué sucede a nivel de bit cuando utilizamos el operador OR a nivel bit.</p>
<p>Primero que nada, cuando se utilizan operadores a nivel de bit con números enteros, PHP convierte los valores a su equivalente binario. Entonces tendríamos que <code>$createPost</code> y <code>$editPost</code> tienen los siguientes valores, respectivamente: <code>000001</code> y <code>000010</code>.</p>
<p>Y si aplicamos un OR para cada bit, tendremos lo siguiente:</p>
<pre><code>000001
000010
------
000011</code></pre>
<p>Se aplica una operación OR por cada bit. Una operación OR da como resultado alguno de los siguientes valores, dependiendo del valor de cada operando (A y B):</p>
<table>
<thead>
<tr>
<th style="text-align: center;">primer valor (A)</th>
<th style="text-align: center;">segundo valor (B)</th>
<th style="text-align: center;">resultado (A OR B)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
</tr>
</tbody>
</table>
<h3>Verificando permisos</h3>
<p>Ya sabemos como almacenar varios permisos en una variable usando el operador OR a nivel bit o con una simple suma. Pero ahora, ¿cómo podemos comprobar si un permiso en particular se encuentra en un grupo de permisos? Para este caso utilizaremos el operador a nivel de bit AND.</p>
<p>Supongamos que hemos asignado permisos para crear, editar y eliminar posts a la variable <code>$userPermissions</code> y deseamos saber si contiene el permiso para editar posts. Para ello haríamos lo siguiente:</p>
<pre><code>if ($userPermissions &amp; $editPost) {
    echo "tiene permisos para editar posts";
} else {
    echo "no tiene permisos para editar posts";
}</code></pre>
<p>Procedamos a la explicación. Primero que nada, hay que notar que estamos usando el símbolo  <strong>&amp;</strong> (<em>ampersand</em>). Este es el operador AND a nivel de bit en PHP; el operador AND booleano se representa con dos símbolos <em>ampersand</em> <strong>&amp;&amp;</strong>.</p>
<p>Al igual que el operador OR a nivel de bit, AND realiza una operación lógica por cada uno de los bits en los operandos, por lo que en nuestro ejemplo tendríamos lo siguiente:</p>
<pre><code>000111
000010
------
000010</code></pre>
<p>El resultado de la operación AND por cada bit, dependerá de los siguientes valores:</p>
<table>
<thead>
<tr>
<th style="text-align: center;">primer valor (A)</th>
<th style="text-align: center;">segundo valor (B)</th>
<th style="text-align: center;">resultado (A AND B)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0</td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
</tr>
</tbody>
</table>
<p>En este caso el resultado es <code>000010</code>, porque solamente en el segundo bit (de derecha a izquierda) de ambos valores binarios el valor es <code>1</code>. Y como podemos ver en la tabla anterior, el resultado de la operación AND será <code>1</code> solamente cuando se cumpla esa condición.</p>
<p>Ahora probemos con otro ejemplo. Verifiquemos con otro permiso que sabemos que existe en <code>$userPermissions</code>: el permiso para eliminar posts (<code>000100</code> binario / <code>4</code> decimal):</p>
<pre><code>000111
000100
------
000100</code></pre>
<p>El resultado es 000100 porque solamente en el tercer bit (de derecha a izquierda) de ambos operandos el valor es <code>1</code>. Y como sabemos <code>1</code> <strong>AND</strong> <code>1</code> es igual a <code>1</code>.</p>
<p>Ahora, ¿qué sucede si el permiso a verificar no se encuentra en el grupo de permisos asignados? Probemos con el permiso para crear comentarios (<code>001000</code> binario / <code>8</code> decimal):</p>
<pre><code>000111
001000
------
000000</code></pre>
<p>El valor es resultado es <code>000000</code> porque no hay algún bit en ambos operandos donde el valor sea <code>1</code>. </p>
<p>Aquí se puede notar un patrón al utilizar el operador AND a nivel de bit: cuando el permiso a verificar existe, el resultado es el valor del permiso a verificar; cuando el permiso no existe, el valor es <code>0</code>:</p>
<pre><code>// el permiso existe, entonces se regresa el valor de $createPost (1)
var_dump($userPermissions &amp; $createPost);

// el permiso existe, entonces se regresa el valor de $editPost (2)
var_dump($userPermissions &amp; $editPost);

// el permiso existe, entonces se regresa el valor de $deletePost (4)
var_dump($userPermissions &amp; $createComments); 

// el permiso no existe, entonces se regresa 0
var_dump($userPermissions &amp; $deletePost); </code></pre>
<p>Ahora, recordemos que el código de verificación es el siguiente:</p>
<pre><code>if ($userPermissions &amp; $editPost) {
    echo "tiene permisos para editar posts";
} else {
    echo "no tiene permisos para editar posts";
}</code></pre>
<p>Utilizar el resultado de la operación en la condición <code>if</code> es simplemente un atajo que aprovecha la conversión implícita de PHP. Como podemos ver, el resultado de la operación AND a nivel bit es un número entero. Al usar dicho número  en una condición, PHP lo convertirá a su equivalente booleano. En el caso de valores enteros, PHP convierte a <code>true</code> cualquier número entero distinto a <code>0</code>, mientras que un valor igual a <code>0</code> se convertirá al valor booleano <code>false</code>.  Por lo tanto, cuando el permiso exista, la condición <code>if</code> resultará verdadera, y cuando no, resultará falsa.</p>
<h3>Quitando permisos</h3>
<p>Ya vimos como asignar y verificar permisos. En esta sección veremos cómo funcionan otros operadores a nivel de bit: XOR y NOT.</p>
<p>XOR no lo utilizaremos en realidad, pero quise incluirlo por la siguiente razón: hay artículos qué sugieren el uso de este operador para desactivar ciertos bits de un valor binario, pero aunque se puede usar con este fin, su funcionamiento es como el de un interruptor de corriente: si está activo, se desactiva; si está desactivado, se activa. Veamos más a detalle cómo se ve este comportamiento a nivel de bits.</p>
<p>El operador XOR es similar a OR, con la diferencia de que devuelve <code>0</code> si ambos operandos son iguales. El operador XOR se representa con el símbolo <em>caret</em> <strong>^</strong> o &quot;conito&quot;, si tienen 5 años mentales.</p>
<table>
<thead>
<tr>
<th style="text-align: center;">primer valor (A)</th>
<th style="text-align: center;">segundo valor (B)</th>
<th style="text-align: center;">resultado (A XOR B)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
</tr>
</tbody>
</table>
<p>Ahora supongamos que usamos XOR para remover un permiso. Como en el ejemplo anterior, tendremos una variable llamada <code>$userPermissions</code> que contendrá permisos para crear, editar y eliminar posts (1 + 2 + 4  = 7). Aplicaremos una operación XOR para remover el permiso para editar posts (<code>000010</code>):</p>
<pre><code>000111 
000010
------
000101</code></pre>
<p>Podemos ver que se remueve el segundo bit (de derecha a izquierda) del resultado, es decir, el bit equivalente a editar posts. En este caso se obtuvo el resultado esperado porque <code>000111</code> tenía activo el bit que nos interesaba desactivar, ¿pero qué sucede si tratamos de usar XOR para remover un permiso que no se encuentra otorgado?</p>
<pre><code>000101 
000010
------
000111</code></pre>
<p>Como había mencionado, XOR funciona como un interruptor, por lo que en este caso, el segundo bit se activó. Esto no es lo que esperábamos, nuestra intencion era desactivar el bit correspondiente al permiso, se encontrara activo o no.</p>
<p>Existe una forma de lograr el resultado esperado utilizando XOR, pero requiere verificar previamente si el permiso  existe:</p>
<pre><code>if ($userPermissions &amp; $editPost) {
    $userPermissions = $userPermissions ^ $editPost;
} </code></pre>
<p>Esto funciona, pero podemos aprovechar otro operador que nos puede facilitar el trabajo: el operador NOT. </p>
<p>El símbolo de operador NOT a nivel de bit es la <em>tilde</em> <strong>~</strong>. Este operador, al igual que el operador NOT booleano, invierte un valor, pero en este caso lo hace bit por bit. Ejemplo:</p>
<pre><code>~ 00000000000000000000000000101001 
-----------------------------------
  11111111111111111111111111010110</code></pre>
<p>No podemos usar solamente el operador NOT para remover un permiso, pues esto resultaria en activar los bits de permisos no otorgados y desactivar los bits de permisos otorgados. Para lograr nuestro cometido, hay que combinar el operador NOT con el operador AND. Supongamos de nuevo que tenemos una variable llamada <code>$userPermissions</code> con permisos para crear, editar y eliminar posts. Como en el ejemplo de XOR, deseamos eliminar el permiso para editar posts. Utilizando NOT y AND quedaría de la siguiente manera:</p>
<pre><code>// Primero se asignan permisos. Equivalente a 1 + 2 + 4
$userPermissions = $createPost | $editPost | $deletePost;

// removemos el permiso equivalente a editar posts
$userPermissions = $userPermissions &amp;  ~$editPost;

// imprime 5, equivalente a crear y eliminar posts
var_dump($userPermissions); 

// removemos el permiso equivalente a editar posts
$userPermissions = $userPermissions &amp;  ~$editPost;

// sigue imprimiendo 5 aunque hayamos aplicado la operación dos veces
var_dump($userPermissions); </code></pre>
<p>En términos de bits lo que sucedería es lo siguiente. Primero el permiso para editar posts aplicándole el operador NOT:</p>
<pre><code>~ 000010
---------
  111101</code></pre>
<p>Y luego este valor invertido lo usamos en la operación AND con los permisos existentes:</p>
<pre><code>000111
111101 &amp;
-------
000101</code></pre>
<p>Como se puede ver en el resultado, el bit correspondiente a editar permisos (segundo bit de derecha a izquierda) se ha desactivado, mientras que los bits correspondientes a crear y eliminar posts (primer y tercer bits de derecha a izquierda) permanecieron activos. </p>
<p>Lo que se hizo aquí se conoce como <em>bit masking</em><sup id="fnref1:bitmasking"><a href="#fn:bitmasking" class="footnote-ref">1</a></sup> y sirve para dejar ciertos bits intactos mientras que otros bits se desactivan. Es importante prestar atención pues utilizaremos esta misma técnica más adelante. <strong>Lo importante a resaltar es que la &quot;máscara&quot; debe tener como valor <code>1</code> en las posiciones de los bits que se desean dejar intactos, valor <code>0</code> en los bits que se desean desactivar y se debe realizar una operacion AND.</strong></p>
<p>En nuestro ejemplo el valor de la máscara es <code>111101</code>, porque deseamos desactivar el segundo bit de derecha a izquierda y conservar los valores del resto. Originalmente el valor de la máscara era <code>000010</code> pero para que esta técnica funcionara utilizamos el operador NOT para invertir los valores.</p>
<hr />
<h3>Configuración de errores con operadores bit a bit</h3>
<p>En PHP es posible indicar el nivel de errores que deseamos que se muestren. Por ejemplo, en un entorno de desarrollo deseamos que se muestren todos los errores para poder depurar y detectar la fuente de una falla más fácilmente; pero en un ambiente de producción no deseamos que este sea el caso pues estos errores se mostrarían a cualquiera que visite el sitio.</p>
<p>El nivel de errores a mostrar puede configurarse desde el archivo <code>php.ini</code> o de forma dinámica utilizando la función <code>error_reporting</code><sup id="fnref1:errorreporting"><a href="#fn:errorreporting" class="footnote-ref">2</a></sup>. Por ejemplo, podemos indicar solamente los errores que deseamos que se muestren, utilizando el operador OR, que como vimos en ejemplos anteriores, es el equivalente a sumar los valores que sean potencia de 2. De hecho, si revisan la lista de constantes de error<sup id="fnref1:errorconstants"><a href="#fn:errorconstants" class="footnote-ref">3</a></sup> verán que, efectivamente, sus valores son potencia de 2.</p>
<pre><code>error_reporting(E_ERROR | E_RECOVERABLE_ERROR);</code></pre>
<p>Otra forma de indicar el nivel de errores es utilizando algo que ya vimos antes: una máscara de bits y los operadores NOT y AND para partir de un valor que represente todos los errores y remover los que no deseemos incluir. En el siguiente ejemplo indicamos que se muestren todos los niveles de error, excepto <code>E_NOTICE</code>.</p>
<pre><code>error_reporting(E_ALL &amp; ~E_NOTICE);</code></pre>
<hr />
<h2>Conversión de colores</h2>
<p>Ahora pasemos a otro ejemplo de uso distinto que hará uso de los operadores que ya vimos junto con un par más.</p>
<p>Hay diferentes sistemas para representar colores, siendo de los más comunes el sistema hexadecimal y RGB (Red, Green, Blue). Tomemos como ejemplo el color del rectángulo en la siguiente imagen:</p>
<p><img src="https://i.imgur.com/ZEPSuIf.png" class="img-responsive img-thumbnail center-block" /></p>
<p>Este color de relleno que es como de un tono naranja se representa en formato hexadecimal con el valor <code>EAB44B</code>. En el sistema RGB se indican tres valores decimales que pueden ir desde el <code>0</code> hasta <code>255</code> cada uno (desde <code>00000000</code> hasta <code>11111111</code>). Cada valor representa un color: el primero representa la cantidad de color rojo (R); el segundo representa la cantidad de color verde (G); el tercero representa la cantidad de color azul (B). Estos valores combinados forman el color de tono naranja de la imagen.</p>
<p>¿Cómo se obtienen los valores RGB <code>234</code>, <code>180</code> y <code>75</code> a partir del valor hexadecimal <code>EAB44B</code>? Para averiguarlo, primero veamos la relación que hay entre este valor hexadecimal con el sistema binario y decimal.</p>
<p>En el sistema hexadecimal, los posibles valores se componen de números y letras. Los números son los mismos que en el sistema decimal: del 0 al 9. Las letras que pueden utilizarse van de la A hasta la F. Esto nos da un total de 16 valores.</p>
<table>
<thead>
<tr>
<th style="text-align: center;">hexadecimal</th>
<th style="text-align: center;">decimal</th>
<th style="text-align: center;">binario</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0000</td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0001</td>
</tr>
<tr>
<td style="text-align: center;">2</td>
<td style="text-align: center;">2</td>
<td style="text-align: center;">0010</td>
</tr>
<tr>
<td style="text-align: center;">3</td>
<td style="text-align: center;">3</td>
<td style="text-align: center;">0011</td>
</tr>
<tr>
<td style="text-align: center;">4</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">0100</td>
</tr>
<tr>
<td style="text-align: center;">5</td>
<td style="text-align: center;">5</td>
<td style="text-align: center;">0101</td>
</tr>
<tr>
<td style="text-align: center;">6</td>
<td style="text-align: center;">6</td>
<td style="text-align: center;">0110</td>
</tr>
<tr>
<td style="text-align: center;">7</td>
<td style="text-align: center;">7</td>
<td style="text-align: center;">0111</td>
</tr>
<tr>
<td style="text-align: center;">8</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">1000</td>
</tr>
<tr>
<td style="text-align: center;">9</td>
<td style="text-align: center;">9</td>
<td style="text-align: center;">1001</td>
</tr>
<tr>
<td style="text-align: center;">A</td>
<td style="text-align: center;">10</td>
<td style="text-align: center;">1010</td>
</tr>
<tr>
<td style="text-align: center;">B</td>
<td style="text-align: center;">11</td>
<td style="text-align: center;">1011</td>
</tr>
<tr>
<td style="text-align: center;">C</td>
<td style="text-align: center;">12</td>
<td style="text-align: center;">1100</td>
</tr>
<tr>
<td style="text-align: center;">D</td>
<td style="text-align: center;">13</td>
<td style="text-align: center;">1101</td>
</tr>
<tr>
<td style="text-align: center;">E</td>
<td style="text-align: center;">14</td>
<td style="text-align: center;">1110</td>
</tr>
<tr>
<td style="text-align: center;">F</td>
<td style="text-align: center;">15</td>
<td style="text-align: center;">1111</td>
</tr>
</tbody>
</table>
<p>Los primeros dos caracteres del valor hexadecimal representan la cantidad de rojo (R), el tercer y cuarto caracter la cantidad de verde (G) y el quinto y sexto la cantidad de azul (B). Para obtener los valores decimales de cada color, primero hay que obtener el valor binario de cada uno. Por ejemplo, la parte correspondiente a rojo es <code>EA</code>. La letra <code>E</code> corresponde al valor binario <code>1110</code>, mientras que la letra <code>A</code> corresponde al valor binario <code>1010</code>. Entonces tenemos que el valor hexadecimal <code>EA</code> equivale al valor binario <code>11101010</code>. El valor <code>11101010</code> equivale al valor <code>234</code> en decimal, que es la cantidad de rojo (R) en RGB.</p>
<p>Muy bien, ¿pero cómo obtenemos estos valores utilizando operadores a nivel de bit? Para esto usaremos la misma técnica de <em>bit masking</em> que en la sección de permisos. En este caso nos servirá para &quot;aislar&quot; los bits que corresponden al color que nos interesa y descartar el resto. </p>
<p>Primero tomemos el valor completo en hexadecimal y su equivalente en binario. <code>EAB44B</code> equivale a <code>111010101011010001001011</code>.</p>
<p>Los primeros ocho dígitos binarios representan el color rojo. Usando la técnica de <em>bit masking</em>, tendríamos que usar el operador AND y el siguiente valor binario como máscara para conservar los valores de los primeros ocho dígitos y descartar el resto: <code>111111110000000000000000</code>. </p>
<p>Entonces tendríamos la siguiente operación:</p>
<pre><code>111010101011010001001011  &amp;
111111110000000000000000
-------------------------
111010100000000000000000</code></pre>
<p>Vemos que los primeros ocho dígitos del resultado son <code>11101010</code>, mientras que el resto quedó en ceros. ¡Pero no tan rápido! Hay un pequeño problema, el equivalente decimal del resultado debería ser <code>234</code>, pero <code>111010100000000000000000</code> equivale a <code>15335424</code> decimal. Esto se debe a que el valor de cada dígito depende de su posición. En este caso los valores <code>1</code> están en las posiciones 16, 18, 20, 21 y 22 (de derecha a izquierda y considerando que la primera posición se eleva a <code>0</code>, la segunda a <code>1</code> y así sucesivamente), por lo que tenemos 2<sup>17</sup> + 2<sup>19</sup> + 2<sup>21</sup> + 2<sup>22</sup> + 2<sup>23</sup>  = 131072 + 524288 + 2097152 + 4194304 + 8388608 = 15335424.</p>
<p>Para solucionar este problema hay que recorrer los dígitos hacia la derecha para que ocupen las posiciones del 1 al 8 (potencias del 0 al 7) y así obtener el valor decimal correcto. Para esto podemos usar otro operador a nivel de bit que precisamente se encarga de mover a la derecha el número de posiciones que le indiquemos: el operador <em>shift right</em> <strong>&gt;&gt;</strong>.</p>
<p>Necesitamos recorrer 16 posiciones hacia la derecha para que el resultado quede en las posiciones correctas. Entonces tendríamos lo siguiente:</p>
<pre><code>111010101011010001001011  &amp;
111111110000000000000000
-------------------------
111010100000000000000000 &gt;&gt; 16
-------------------------
000000000000000011101010 = 234 decimal</code></pre>
<p>Las posiciones &quot;libres&quot; a la izquierda se rellenan automáticamente con ceros cuando se usa este tipo de operación. Ahora, ¿cómo quedaría en código PHP? Primero que nada, para representar un valor hexadecimal en PHP hay que usar el prefijo <code>0x</code>. Con esto representaremos los valores del color y la máscara:</p>
<pre><code>$color = 0xEAB44B;

$mask = 0xFF0000; // equivale a 111111110000000000000000

$redColor = ($color &amp; $mask) &gt;&gt; 16;

var_dump($redColor); //imprime 234
</code></pre>
<p>Hay que resaltar un par de cosas importantes en este código: al utilizar operador a nivel de bit, PHP convierte automáticamente los operandos en su equivalente binario para poder realizar la operación. Es por ello que la operación funciona aunque los valores de <code>$color</code> y <code>$mask</code> no sean binarios sino hexadecimales. De hecho, el valor de <code>$mask</code> se convierte al valor binario <code>111111110000000000000000</code> que es el valor binario de la máscara mostrada en los ejemplos anteriores.</p>
<p>Para obtener los valores del color verde y azul usamos el mismo procedimiento pero usando diferentes valores para la máscara. Por ejemplo, para obtener el color verde hay que recorrer 8 posiciones a la derecha y el valor de la máscara es <code>000000001111111100000000</code>:</p>
<pre><code>111010101011010001001011  &amp;
000000001111111100000000
-------------------------
000000001011010000000000 &gt;&gt; 8
-------------------------
000000000000000010110100 = 180 decimal</code></pre>
<p>Y en codigo tenemos que:</p>
<pre><code>$color = 0xEAB44B;

$mask = 0x00FF00; // equivale a 000000001111111100000000

$greenColor = ($color &amp; $mask) &gt;&gt; 8;

var_dump($greenColor); // imprime 180</code></pre>
<p>El color azul tiene una peculiaridad, y es que no hay necesidad de recorrer posiciones. Esto se debe a que los dígitos correspondientes al color azul ya se encuentran en las posiciones correctas. Para obtener su valor tendríamos lo siguiente:</p>
<pre><code>111010101011010001001011  &amp;
000000000000000011111111
-------------------------
000000000000000001001011 = 75 decimal</code></pre>
<p>Lo cual en codigo sería:</p>
<pre><code>$color = 0xEAB44B;

$mask = 0x0000FF; // equivale a 000000000000000011111111

$blueColor = ($color &amp; $mask);

var_dump($blueColor); // imprime 75</code></pre>
<p>Y ya con el código para todos los colores:</p>
<pre><code>$color = 0xEAB44B;

$redMask = 0xFF0000; // equivale a 111111110000000000000000
$greenMask = 0x00FF00; // equivale a 000000001111111100000000
$blueMask = 0x0000FF; // equivale a 000000000000000011111111

$redColor = ($color &amp; $redMask) &gt;&gt; 16;
$greenColor = ($color &amp; $greenMask) &gt;&gt; 8;
$blueColor = ($color &amp; $blueMask);

var_dump($redColor); // imprime int(234)
var_dump($greenColor); // imprime int(180)
var_dump($blueColor); // imprime int(75)</code></pre>
<p>Tengo algo qué confesar: hay una forma más &quot;directa&quot; de hacer estas conversiones. En el código anterior utilizamos una máscara distinta para cada color, cada máscara contiene activos los bits para las posiciones correspondientes al color a extraer. Si invertimos el orden de las operaciones y usamos <code>0x0000FF</code> (<code>000000000000000011111111</code> binario) como máscara para todos los colores obtendremos el mismo resultado, puesto que al recorrer primero los bits ya los colocamos o &quot;alineamos&quot; con los valores de la máscara:</p>
<pre><code>$color = 0xEAB44B;

$mask = 0x0000FF; // equivale a 000000000000000011111111

$redColor = ($color &gt;&gt; 16) &amp; $mask;
$greenColor = ($color &gt;&gt; 8) &amp; $mask;
$blueColor = ($color &amp; $mask);

var_dump($redColor); // imprime int(234)
var_dump($greenColor); // imprime int(180)
var_dump($blueColor); // imprime int(75)</code></pre>
<p>¿Y si queremos obtener el color en hexadecimal a partir de RGB? Primero hacemos el proceso inverso en los colores rojo y verde usando el operador llamado <em>shift left</em> que recorre posiciones hacia la izquierda en lugar de hacia la derecha. A partir de ahí combinamos los colores con el operador OR:</p>
<pre><code>000000000000000011101010 // rojo &lt;&lt; 16
000000000000000010110100 // verde &lt;&lt; 8
000000000000000001001011 // azul no necesita recorrerse
-------------------------
111010100000000000000000 OR 
000000001011010000000000 OR
000000000000000001001011
-------------------------
111010101011010001001011</code></pre>
<p>Que en código quedaría de la siguiente forma:</p>
<pre><code>$hexColor = dechex(($redColor &lt;&lt; 16) | ($greenColor &lt;&lt; 8) | $blueColor);

var_dump($hexColor); // imprime string(6) "eab44b"</code></pre>
<p><div class="alert alert-warning">Noten que se utiliza la función <code>dechex()</code> puesto que si tratamos de imprimir directamente el valor de la operación con <code>var_dump()</code> nos mostrará el equivalente en decimal.</div></p>
<hr />
<p>Y así termina este pequeño post, tutorial o como le quieran llamar. Algo extenso pero quería cubrir el uso de estos operadores y con un poco de suerte ayudar a entender a alguien que los haya usado antes sin saber realmente su funcionamiento o algún ejemplo de su uso. </p>
<p>Si leyeron el post y hay algo que no queda claro, no duden en preguntar. Si creen que no tengo idea de lo que hablo, también pueden comentar. Si les ayudó en algo, me doy por bien servido.</p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:bitmasking">
<p><a href="https://es.wikipedia.org/wiki/M%C3%A1scara_(inform%C3%A1tica)">bit masking</a>&#160;<a href="#fnref1:bitmasking" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:errorreporting">
<p><a href="http://php.net/manual/es/function.error-reporting.php">funcion error_reporting</a>&#160;<a href="#fnref1:errorreporting" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:errorconstants">
<p><a href="http://php.net/manual/es/errorfunc.constants.php">constantes para error_reporting</a>&#160;<a href="#fnref1:errorconstants" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
</ol>
</div>]]></description>
      <content:encoded><![CDATA[<p>Al programar estamos acostumbrados a realizar distintos tipos de operaciones. Quizá las más comunes sean operaciones matemáticas, manipulación de strings y comparaciones. </p>
<p>Existe un conjunto de operadores que sirven para trabajar con datos a nivel de bit, o a nivel binario. En sistemas electrónicos estos son muy comunes, pero en aplicaciones web tal vez no tanto en comparación con el resto de operadores. Creo que en parte se debe a que en cualquier lenguaje se explica la función de estos operadores, pero no se dan ejemplos de su uso.</p>
<p>En este post se exponen  un par de casos de uso con operadores a nivel de bit. Trataré de explicar cada uno lo mejor que pueda.</p>
<hr />
<h2>Manejo de permisos</h2>
<p>Manejar permisos en una aplicación no es algo nuevo, ni algo exclusivo de los operadores bit a bit. Una forma común de implementar esta funcionalidad es almacenar un listado de permisos  disponibles en una base de datos y relacionarlos con los usuarios de la aplicación para determinar quien tiene permisos sobre cuales recursos: la o las tablas que representan los permisos contienen un registro por cada permiso disponible. </p>
<p>Con operadores bit a bit podemos almacenar distintos permisos en un solo valor decimal, aprovechando que un valor decimal también puede representarse como un número binario. De esta manera, cada uno de los bits o dígitos binarios que componen un número puede representar un permiso.</p>
<p><img src="https://i.imgur.com/VzAZ308.png" class="img-responsive img-thumbnail center-block" /></p>
<p>Para tratar de simplificar el post he utilizado solo seis posiciones en la imagen  (hasta 2<sup>5</sup>), pero el número de posiciones máximo depende del sistema operativo sobre el que se ejecute PHP. Por ejemplo, en sistemas de 32 bits un solo valor entero puede representar 32 posiciones/permisos (hasta 2<sup>31</sup>), mientras que en un sistema de 64 bits se puede almacenar 64 posiciones/permisos  (hasta 2<sup>63</sup>) en un solo valor entero.</p>
<p>¿Cuáles y qué clase de permisos podemos almacenar de esta manera? Los que deseemos. Utilicemos un ejemplo muy trillado: un blog. Vamos a suponer que deseamos controlar permisos relacionados con posts y comentarios. Podríamos tener los siguientes permisos: crear, editar y eliminar posts; crear, editar y eliminar comentarios.</p>
<p>En términos de variables, podrían quedar de la siguiente manera:</p>
<p><code>$createPost</code>, <code>$editPost</code>, <code>$deletePost</code>, <code>$createComment</code>, <code>$editComment</code>, <code>$deleteComment</code></p>
<p>Lo importante es que cada permiso debe ser una potencia de 2 (1, 2, 4...) O en otras palabras, valores que solo tengan un bit con valor 1 (0001, 0010, 0100...) Ya depende de nosotros el valor que asignemos a cada uno. Para fines del post usemos los siguientes valores:</p>
<pre><code>$createPost = 1;
$editPost = 2;
$deletePost = 4;
$createComment = 8;
$editComment = 16;
$deleteComment = 32;</code></pre>
<p>Ya con estos valores asignados y tomando como referencia la imagen anterior, tendríamos que el número entero 37 representa un grupo de permisos en los que están activos los bits correspondientes a las posiciones 1, 3 y 6 (de derecha a izquierda); es decir, están activos los permisos correspondientes a crear post, eliminar post y eliminar comentarios:</p>
<pre><code>// $userPermissions = 1 + 4 + 32
$userPermissions = $createPost + $deletePost  + $deleteComment</code></pre>
<p>El valor 63 de la imagen representaría un grupo de permisos donde todos los bits o permisos están activos:</p>
<pre><code>// $userPermissions = 1 + 2 + 4 + 8 + 16 + 32
$userPermissions = $createPost + $editPost + $deletePost  + $createComment + $editComment + $deleteComment</code></pre>
<p>Podemos ver que para agregar varios permisos simplemente sumamos su valor decimal. Esto es el equivalente a utilizar el primer operador a nivel de bit: OR. Un OR a nivel de bit es similar a un OR booleano. Una diferencia es que la comparación se hace con cada uno de los bits del valor binario; otra diferencia es que el símbolo de OR a nivel de bit es un símbolo <strong><em>pipe</em></strong> <strong>|</strong>, mientras que el OR booleano son dos símbolos <strong><em>pipe</em></strong> <strong>||</strong>.</p>
<p>Por ejemplo, si queremos agregar los permisos para crear y editar posts, tendríamos algo como lo siguiente:</p>
<pre><code>$userPermissions = $createPost  | $editPost;
var_dump($userPermissions); // imprime 3</code></pre>
<p>Podemos ver que es igual a haber sumado los valores de <code>$createPost</code> y <code>$editPost</code>. Pero hay que tener cuidado, <strong>esta equivalencia solamente se da cuando utilizamos valores que son potencia de 2 como en este caso</strong>. Veamos qué sucede a nivel de bit cuando utilizamos el operador OR a nivel bit.</p>
<p>Primero que nada, cuando se utilizan operadores a nivel de bit con números enteros, PHP convierte los valores a su equivalente binario. Entonces tendríamos que <code>$createPost</code> y <code>$editPost</code> tienen los siguientes valores, respectivamente: <code>000001</code> y <code>000010</code>.</p>
<p>Y si aplicamos un OR para cada bit, tendremos lo siguiente:</p>
<pre><code>000001
000010
------
000011</code></pre>
<p>Se aplica una operación OR por cada bit. Una operación OR da como resultado alguno de los siguientes valores, dependiendo del valor de cada operando (A y B):</p>
<table>
<thead>
<tr>
<th style="text-align: center;">primer valor (A)</th>
<th style="text-align: center;">segundo valor (B)</th>
<th style="text-align: center;">resultado (A OR B)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
</tr>
</tbody>
</table>
<h3>Verificando permisos</h3>
<p>Ya sabemos como almacenar varios permisos en una variable usando el operador OR a nivel bit o con una simple suma. Pero ahora, ¿cómo podemos comprobar si un permiso en particular se encuentra en un grupo de permisos? Para este caso utilizaremos el operador a nivel de bit AND.</p>
<p>Supongamos que hemos asignado permisos para crear, editar y eliminar posts a la variable <code>$userPermissions</code> y deseamos saber si contiene el permiso para editar posts. Para ello haríamos lo siguiente:</p>
<pre><code>if ($userPermissions &amp; $editPost) {
    echo "tiene permisos para editar posts";
} else {
    echo "no tiene permisos para editar posts";
}</code></pre>
<p>Procedamos a la explicación. Primero que nada, hay que notar que estamos usando el símbolo  <strong>&amp;</strong> (<em>ampersand</em>). Este es el operador AND a nivel de bit en PHP; el operador AND booleano se representa con dos símbolos <em>ampersand</em> <strong>&amp;&amp;</strong>.</p>
<p>Al igual que el operador OR a nivel de bit, AND realiza una operación lógica por cada uno de los bits en los operandos, por lo que en nuestro ejemplo tendríamos lo siguiente:</p>
<pre><code>000111
000010
------
000010</code></pre>
<p>El resultado de la operación AND por cada bit, dependerá de los siguientes valores:</p>
<table>
<thead>
<tr>
<th style="text-align: center;">primer valor (A)</th>
<th style="text-align: center;">segundo valor (B)</th>
<th style="text-align: center;">resultado (A AND B)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0</td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
</tr>
</tbody>
</table>
<p>En este caso el resultado es <code>000010</code>, porque solamente en el segundo bit (de derecha a izquierda) de ambos valores binarios el valor es <code>1</code>. Y como podemos ver en la tabla anterior, el resultado de la operación AND será <code>1</code> solamente cuando se cumpla esa condición.</p>
<p>Ahora probemos con otro ejemplo. Verifiquemos con otro permiso que sabemos que existe en <code>$userPermissions</code>: el permiso para eliminar posts (<code>000100</code> binario / <code>4</code> decimal):</p>
<pre><code>000111
000100
------
000100</code></pre>
<p>El resultado es 000100 porque solamente en el tercer bit (de derecha a izquierda) de ambos operandos el valor es <code>1</code>. Y como sabemos <code>1</code> <strong>AND</strong> <code>1</code> es igual a <code>1</code>.</p>
<p>Ahora, ¿qué sucede si el permiso a verificar no se encuentra en el grupo de permisos asignados? Probemos con el permiso para crear comentarios (<code>001000</code> binario / <code>8</code> decimal):</p>
<pre><code>000111
001000
------
000000</code></pre>
<p>El valor es resultado es <code>000000</code> porque no hay algún bit en ambos operandos donde el valor sea <code>1</code>. </p>
<p>Aquí se puede notar un patrón al utilizar el operador AND a nivel de bit: cuando el permiso a verificar existe, el resultado es el valor del permiso a verificar; cuando el permiso no existe, el valor es <code>0</code>:</p>
<pre><code>// el permiso existe, entonces se regresa el valor de $createPost (1)
var_dump($userPermissions &amp; $createPost);

// el permiso existe, entonces se regresa el valor de $editPost (2)
var_dump($userPermissions &amp; $editPost);

// el permiso existe, entonces se regresa el valor de $deletePost (4)
var_dump($userPermissions &amp; $createComments); 

// el permiso no existe, entonces se regresa 0
var_dump($userPermissions &amp; $deletePost); </code></pre>
<p>Ahora, recordemos que el código de verificación es el siguiente:</p>
<pre><code>if ($userPermissions &amp; $editPost) {
    echo "tiene permisos para editar posts";
} else {
    echo "no tiene permisos para editar posts";
}</code></pre>
<p>Utilizar el resultado de la operación en la condición <code>if</code> es simplemente un atajo que aprovecha la conversión implícita de PHP. Como podemos ver, el resultado de la operación AND a nivel bit es un número entero. Al usar dicho número  en una condición, PHP lo convertirá a su equivalente booleano. En el caso de valores enteros, PHP convierte a <code>true</code> cualquier número entero distinto a <code>0</code>, mientras que un valor igual a <code>0</code> se convertirá al valor booleano <code>false</code>.  Por lo tanto, cuando el permiso exista, la condición <code>if</code> resultará verdadera, y cuando no, resultará falsa.</p>
<h3>Quitando permisos</h3>
<p>Ya vimos como asignar y verificar permisos. En esta sección veremos cómo funcionan otros operadores a nivel de bit: XOR y NOT.</p>
<p>XOR no lo utilizaremos en realidad, pero quise incluirlo por la siguiente razón: hay artículos qué sugieren el uso de este operador para desactivar ciertos bits de un valor binario, pero aunque se puede usar con este fin, su funcionamiento es como el de un interruptor de corriente: si está activo, se desactiva; si está desactivado, se activa. Veamos más a detalle cómo se ve este comportamiento a nivel de bits.</p>
<p>El operador XOR es similar a OR, con la diferencia de que devuelve <code>0</code> si ambos operandos son iguales. El operador XOR se representa con el símbolo <em>caret</em> <strong>^</strong> o &quot;conito&quot;, si tienen 5 años mentales.</p>
<table>
<thead>
<tr>
<th style="text-align: center;">primer valor (A)</th>
<th style="text-align: center;">segundo valor (B)</th>
<th style="text-align: center;">resultado (A XOR B)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0</td>
</tr>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
</tr>
</tbody>
</table>
<p>Ahora supongamos que usamos XOR para remover un permiso. Como en el ejemplo anterior, tendremos una variable llamada <code>$userPermissions</code> que contendrá permisos para crear, editar y eliminar posts (1 + 2 + 4  = 7). Aplicaremos una operación XOR para remover el permiso para editar posts (<code>000010</code>):</p>
<pre><code>000111 
000010
------
000101</code></pre>
<p>Podemos ver que se remueve el segundo bit (de derecha a izquierda) del resultado, es decir, el bit equivalente a editar posts. En este caso se obtuvo el resultado esperado porque <code>000111</code> tenía activo el bit que nos interesaba desactivar, ¿pero qué sucede si tratamos de usar XOR para remover un permiso que no se encuentra otorgado?</p>
<pre><code>000101 
000010
------
000111</code></pre>
<p>Como había mencionado, XOR funciona como un interruptor, por lo que en este caso, el segundo bit se activó. Esto no es lo que esperábamos, nuestra intencion era desactivar el bit correspondiente al permiso, se encontrara activo o no.</p>
<p>Existe una forma de lograr el resultado esperado utilizando XOR, pero requiere verificar previamente si el permiso  existe:</p>
<pre><code>if ($userPermissions &amp; $editPost) {
    $userPermissions = $userPermissions ^ $editPost;
} </code></pre>
<p>Esto funciona, pero podemos aprovechar otro operador que nos puede facilitar el trabajo: el operador NOT. </p>
<p>El símbolo de operador NOT a nivel de bit es la <em>tilde</em> <strong>~</strong>. Este operador, al igual que el operador NOT booleano, invierte un valor, pero en este caso lo hace bit por bit. Ejemplo:</p>
<pre><code>~ 00000000000000000000000000101001 
-----------------------------------
  11111111111111111111111111010110</code></pre>
<p>No podemos usar solamente el operador NOT para remover un permiso, pues esto resultaria en activar los bits de permisos no otorgados y desactivar los bits de permisos otorgados. Para lograr nuestro cometido, hay que combinar el operador NOT con el operador AND. Supongamos de nuevo que tenemos una variable llamada <code>$userPermissions</code> con permisos para crear, editar y eliminar posts. Como en el ejemplo de XOR, deseamos eliminar el permiso para editar posts. Utilizando NOT y AND quedaría de la siguiente manera:</p>
<pre><code>// Primero se asignan permisos. Equivalente a 1 + 2 + 4
$userPermissions = $createPost | $editPost | $deletePost;

// removemos el permiso equivalente a editar posts
$userPermissions = $userPermissions &amp;  ~$editPost;

// imprime 5, equivalente a crear y eliminar posts
var_dump($userPermissions); 

// removemos el permiso equivalente a editar posts
$userPermissions = $userPermissions &amp;  ~$editPost;

// sigue imprimiendo 5 aunque hayamos aplicado la operación dos veces
var_dump($userPermissions); </code></pre>
<p>En términos de bits lo que sucedería es lo siguiente. Primero el permiso para editar posts aplicándole el operador NOT:</p>
<pre><code>~ 000010
---------
  111101</code></pre>
<p>Y luego este valor invertido lo usamos en la operación AND con los permisos existentes:</p>
<pre><code>000111
111101 &amp;
-------
000101</code></pre>
<p>Como se puede ver en el resultado, el bit correspondiente a editar permisos (segundo bit de derecha a izquierda) se ha desactivado, mientras que los bits correspondientes a crear y eliminar posts (primer y tercer bits de derecha a izquierda) permanecieron activos. </p>
<p>Lo que se hizo aquí se conoce como <em>bit masking</em><sup id="fnref1:bitmasking"><a href="#fn:bitmasking" class="footnote-ref">1</a></sup> y sirve para dejar ciertos bits intactos mientras que otros bits se desactivan. Es importante prestar atención pues utilizaremos esta misma técnica más adelante. <strong>Lo importante a resaltar es que la &quot;máscara&quot; debe tener como valor <code>1</code> en las posiciones de los bits que se desean dejar intactos, valor <code>0</code> en los bits que se desean desactivar y se debe realizar una operacion AND.</strong></p>
<p>En nuestro ejemplo el valor de la máscara es <code>111101</code>, porque deseamos desactivar el segundo bit de derecha a izquierda y conservar los valores del resto. Originalmente el valor de la máscara era <code>000010</code> pero para que esta técnica funcionara utilizamos el operador NOT para invertir los valores.</p>
<hr />
<h3>Configuración de errores con operadores bit a bit</h3>
<p>En PHP es posible indicar el nivel de errores que deseamos que se muestren. Por ejemplo, en un entorno de desarrollo deseamos que se muestren todos los errores para poder depurar y detectar la fuente de una falla más fácilmente; pero en un ambiente de producción no deseamos que este sea el caso pues estos errores se mostrarían a cualquiera que visite el sitio.</p>
<p>El nivel de errores a mostrar puede configurarse desde el archivo <code>php.ini</code> o de forma dinámica utilizando la función <code>error_reporting</code><sup id="fnref1:errorreporting"><a href="#fn:errorreporting" class="footnote-ref">2</a></sup>. Por ejemplo, podemos indicar solamente los errores que deseamos que se muestren, utilizando el operador OR, que como vimos en ejemplos anteriores, es el equivalente a sumar los valores que sean potencia de 2. De hecho, si revisan la lista de constantes de error<sup id="fnref1:errorconstants"><a href="#fn:errorconstants" class="footnote-ref">3</a></sup> verán que, efectivamente, sus valores son potencia de 2.</p>
<pre><code>error_reporting(E_ERROR | E_RECOVERABLE_ERROR);</code></pre>
<p>Otra forma de indicar el nivel de errores es utilizando algo que ya vimos antes: una máscara de bits y los operadores NOT y AND para partir de un valor que represente todos los errores y remover los que no deseemos incluir. En el siguiente ejemplo indicamos que se muestren todos los niveles de error, excepto <code>E_NOTICE</code>.</p>
<pre><code>error_reporting(E_ALL &amp; ~E_NOTICE);</code></pre>
<hr />
<h2>Conversión de colores</h2>
<p>Ahora pasemos a otro ejemplo de uso distinto que hará uso de los operadores que ya vimos junto con un par más.</p>
<p>Hay diferentes sistemas para representar colores, siendo de los más comunes el sistema hexadecimal y RGB (Red, Green, Blue). Tomemos como ejemplo el color del rectángulo en la siguiente imagen:</p>
<p><img src="https://i.imgur.com/ZEPSuIf.png" class="img-responsive img-thumbnail center-block" /></p>
<p>Este color de relleno que es como de un tono naranja se representa en formato hexadecimal con el valor <code>EAB44B</code>. En el sistema RGB se indican tres valores decimales que pueden ir desde el <code>0</code> hasta <code>255</code> cada uno (desde <code>00000000</code> hasta <code>11111111</code>). Cada valor representa un color: el primero representa la cantidad de color rojo (R); el segundo representa la cantidad de color verde (G); el tercero representa la cantidad de color azul (B). Estos valores combinados forman el color de tono naranja de la imagen.</p>
<p>¿Cómo se obtienen los valores RGB <code>234</code>, <code>180</code> y <code>75</code> a partir del valor hexadecimal <code>EAB44B</code>? Para averiguarlo, primero veamos la relación que hay entre este valor hexadecimal con el sistema binario y decimal.</p>
<p>En el sistema hexadecimal, los posibles valores se componen de números y letras. Los números son los mismos que en el sistema decimal: del 0 al 9. Las letras que pueden utilizarse van de la A hasta la F. Esto nos da un total de 16 valores.</p>
<table>
<thead>
<tr>
<th style="text-align: center;">hexadecimal</th>
<th style="text-align: center;">decimal</th>
<th style="text-align: center;">binario</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0</td>
<td style="text-align: center;">0000</td>
</tr>
<tr>
<td style="text-align: center;">1</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0001</td>
</tr>
<tr>
<td style="text-align: center;">2</td>
<td style="text-align: center;">2</td>
<td style="text-align: center;">0010</td>
</tr>
<tr>
<td style="text-align: center;">3</td>
<td style="text-align: center;">3</td>
<td style="text-align: center;">0011</td>
</tr>
<tr>
<td style="text-align: center;">4</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">0100</td>
</tr>
<tr>
<td style="text-align: center;">5</td>
<td style="text-align: center;">5</td>
<td style="text-align: center;">0101</td>
</tr>
<tr>
<td style="text-align: center;">6</td>
<td style="text-align: center;">6</td>
<td style="text-align: center;">0110</td>
</tr>
<tr>
<td style="text-align: center;">7</td>
<td style="text-align: center;">7</td>
<td style="text-align: center;">0111</td>
</tr>
<tr>
<td style="text-align: center;">8</td>
<td style="text-align: center;">8</td>
<td style="text-align: center;">1000</td>
</tr>
<tr>
<td style="text-align: center;">9</td>
<td style="text-align: center;">9</td>
<td style="text-align: center;">1001</td>
</tr>
<tr>
<td style="text-align: center;">A</td>
<td style="text-align: center;">10</td>
<td style="text-align: center;">1010</td>
</tr>
<tr>
<td style="text-align: center;">B</td>
<td style="text-align: center;">11</td>
<td style="text-align: center;">1011</td>
</tr>
<tr>
<td style="text-align: center;">C</td>
<td style="text-align: center;">12</td>
<td style="text-align: center;">1100</td>
</tr>
<tr>
<td style="text-align: center;">D</td>
<td style="text-align: center;">13</td>
<td style="text-align: center;">1101</td>
</tr>
<tr>
<td style="text-align: center;">E</td>
<td style="text-align: center;">14</td>
<td style="text-align: center;">1110</td>
</tr>
<tr>
<td style="text-align: center;">F</td>
<td style="text-align: center;">15</td>
<td style="text-align: center;">1111</td>
</tr>
</tbody>
</table>
<p>Los primeros dos caracteres del valor hexadecimal representan la cantidad de rojo (R), el tercer y cuarto caracter la cantidad de verde (G) y el quinto y sexto la cantidad de azul (B). Para obtener los valores decimales de cada color, primero hay que obtener el valor binario de cada uno. Por ejemplo, la parte correspondiente a rojo es <code>EA</code>. La letra <code>E</code> corresponde al valor binario <code>1110</code>, mientras que la letra <code>A</code> corresponde al valor binario <code>1010</code>. Entonces tenemos que el valor hexadecimal <code>EA</code> equivale al valor binario <code>11101010</code>. El valor <code>11101010</code> equivale al valor <code>234</code> en decimal, que es la cantidad de rojo (R) en RGB.</p>
<p>Muy bien, ¿pero cómo obtenemos estos valores utilizando operadores a nivel de bit? Para esto usaremos la misma técnica de <em>bit masking</em> que en la sección de permisos. En este caso nos servirá para &quot;aislar&quot; los bits que corresponden al color que nos interesa y descartar el resto. </p>
<p>Primero tomemos el valor completo en hexadecimal y su equivalente en binario. <code>EAB44B</code> equivale a <code>111010101011010001001011</code>.</p>
<p>Los primeros ocho dígitos binarios representan el color rojo. Usando la técnica de <em>bit masking</em>, tendríamos que usar el operador AND y el siguiente valor binario como máscara para conservar los valores de los primeros ocho dígitos y descartar el resto: <code>111111110000000000000000</code>. </p>
<p>Entonces tendríamos la siguiente operación:</p>
<pre><code>111010101011010001001011  &amp;
111111110000000000000000
-------------------------
111010100000000000000000</code></pre>
<p>Vemos que los primeros ocho dígitos del resultado son <code>11101010</code>, mientras que el resto quedó en ceros. ¡Pero no tan rápido! Hay un pequeño problema, el equivalente decimal del resultado debería ser <code>234</code>, pero <code>111010100000000000000000</code> equivale a <code>15335424</code> decimal. Esto se debe a que el valor de cada dígito depende de su posición. En este caso los valores <code>1</code> están en las posiciones 16, 18, 20, 21 y 22 (de derecha a izquierda y considerando que la primera posición se eleva a <code>0</code>, la segunda a <code>1</code> y así sucesivamente), por lo que tenemos 2<sup>17</sup> + 2<sup>19</sup> + 2<sup>21</sup> + 2<sup>22</sup> + 2<sup>23</sup>  = 131072 + 524288 + 2097152 + 4194304 + 8388608 = 15335424.</p>
<p>Para solucionar este problema hay que recorrer los dígitos hacia la derecha para que ocupen las posiciones del 1 al 8 (potencias del 0 al 7) y así obtener el valor decimal correcto. Para esto podemos usar otro operador a nivel de bit que precisamente se encarga de mover a la derecha el número de posiciones que le indiquemos: el operador <em>shift right</em> <strong>&gt;&gt;</strong>.</p>
<p>Necesitamos recorrer 16 posiciones hacia la derecha para que el resultado quede en las posiciones correctas. Entonces tendríamos lo siguiente:</p>
<pre><code>111010101011010001001011  &amp;
111111110000000000000000
-------------------------
111010100000000000000000 &gt;&gt; 16
-------------------------
000000000000000011101010 = 234 decimal</code></pre>
<p>Las posiciones &quot;libres&quot; a la izquierda se rellenan automáticamente con ceros cuando se usa este tipo de operación. Ahora, ¿cómo quedaría en código PHP? Primero que nada, para representar un valor hexadecimal en PHP hay que usar el prefijo <code>0x</code>. Con esto representaremos los valores del color y la máscara:</p>
<pre><code>$color = 0xEAB44B;

$mask = 0xFF0000; // equivale a 111111110000000000000000

$redColor = ($color &amp; $mask) &gt;&gt; 16;

var_dump($redColor); //imprime 234
</code></pre>
<p>Hay que resaltar un par de cosas importantes en este código: al utilizar operador a nivel de bit, PHP convierte automáticamente los operandos en su equivalente binario para poder realizar la operación. Es por ello que la operación funciona aunque los valores de <code>$color</code> y <code>$mask</code> no sean binarios sino hexadecimales. De hecho, el valor de <code>$mask</code> se convierte al valor binario <code>111111110000000000000000</code> que es el valor binario de la máscara mostrada en los ejemplos anteriores.</p>
<p>Para obtener los valores del color verde y azul usamos el mismo procedimiento pero usando diferentes valores para la máscara. Por ejemplo, para obtener el color verde hay que recorrer 8 posiciones a la derecha y el valor de la máscara es <code>000000001111111100000000</code>:</p>
<pre><code>111010101011010001001011  &amp;
000000001111111100000000
-------------------------
000000001011010000000000 &gt;&gt; 8
-------------------------
000000000000000010110100 = 180 decimal</code></pre>
<p>Y en codigo tenemos que:</p>
<pre><code>$color = 0xEAB44B;

$mask = 0x00FF00; // equivale a 000000001111111100000000

$greenColor = ($color &amp; $mask) &gt;&gt; 8;

var_dump($greenColor); // imprime 180</code></pre>
<p>El color azul tiene una peculiaridad, y es que no hay necesidad de recorrer posiciones. Esto se debe a que los dígitos correspondientes al color azul ya se encuentran en las posiciones correctas. Para obtener su valor tendríamos lo siguiente:</p>
<pre><code>111010101011010001001011  &amp;
000000000000000011111111
-------------------------
000000000000000001001011 = 75 decimal</code></pre>
<p>Lo cual en codigo sería:</p>
<pre><code>$color = 0xEAB44B;

$mask = 0x0000FF; // equivale a 000000000000000011111111

$blueColor = ($color &amp; $mask);

var_dump($blueColor); // imprime 75</code></pre>
<p>Y ya con el código para todos los colores:</p>
<pre><code>$color = 0xEAB44B;

$redMask = 0xFF0000; // equivale a 111111110000000000000000
$greenMask = 0x00FF00; // equivale a 000000001111111100000000
$blueMask = 0x0000FF; // equivale a 000000000000000011111111

$redColor = ($color &amp; $redMask) &gt;&gt; 16;
$greenColor = ($color &amp; $greenMask) &gt;&gt; 8;
$blueColor = ($color &amp; $blueMask);

var_dump($redColor); // imprime int(234)
var_dump($greenColor); // imprime int(180)
var_dump($blueColor); // imprime int(75)</code></pre>
<p>Tengo algo qué confesar: hay una forma más &quot;directa&quot; de hacer estas conversiones. En el código anterior utilizamos una máscara distinta para cada color, cada máscara contiene activos los bits para las posiciones correspondientes al color a extraer. Si invertimos el orden de las operaciones y usamos <code>0x0000FF</code> (<code>000000000000000011111111</code> binario) como máscara para todos los colores obtendremos el mismo resultado, puesto que al recorrer primero los bits ya los colocamos o &quot;alineamos&quot; con los valores de la máscara:</p>
<pre><code>$color = 0xEAB44B;

$mask = 0x0000FF; // equivale a 000000000000000011111111

$redColor = ($color &gt;&gt; 16) &amp; $mask;
$greenColor = ($color &gt;&gt; 8) &amp; $mask;
$blueColor = ($color &amp; $mask);

var_dump($redColor); // imprime int(234)
var_dump($greenColor); // imprime int(180)
var_dump($blueColor); // imprime int(75)</code></pre>
<p>¿Y si queremos obtener el color en hexadecimal a partir de RGB? Primero hacemos el proceso inverso en los colores rojo y verde usando el operador llamado <em>shift left</em> que recorre posiciones hacia la izquierda en lugar de hacia la derecha. A partir de ahí combinamos los colores con el operador OR:</p>
<pre><code>000000000000000011101010 // rojo &lt;&lt; 16
000000000000000010110100 // verde &lt;&lt; 8
000000000000000001001011 // azul no necesita recorrerse
-------------------------
111010100000000000000000 OR 
000000001011010000000000 OR
000000000000000001001011
-------------------------
111010101011010001001011</code></pre>
<p>Que en código quedaría de la siguiente forma:</p>
<pre><code>$hexColor = dechex(($redColor &lt;&lt; 16) | ($greenColor &lt;&lt; 8) | $blueColor);

var_dump($hexColor); // imprime string(6) "eab44b"</code></pre>
<p><div class="alert alert-warning">Noten que se utiliza la función <code>dechex()</code> puesto que si tratamos de imprimir directamente el valor de la operación con <code>var_dump()</code> nos mostrará el equivalente en decimal.</div></p>
<hr />
<p>Y así termina este pequeño post, tutorial o como le quieran llamar. Algo extenso pero quería cubrir el uso de estos operadores y con un poco de suerte ayudar a entender a alguien que los haya usado antes sin saber realmente su funcionamiento o algún ejemplo de su uso. </p>
<p>Si leyeron el post y hay algo que no queda claro, no duden en preguntar. Si creen que no tengo idea de lo que hablo, también pueden comentar. Si les ayudó en algo, me doy por bien servido.</p>
<div class="footnotes">
<hr />
<ol>
<li id="fn:bitmasking">
<p><a href="https://es.wikipedia.org/wiki/M%C3%A1scara_(inform%C3%A1tica)">bit masking</a>&#160;<a href="#fnref1:bitmasking" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:errorreporting">
<p><a href="http://php.net/manual/es/function.error-reporting.php">funcion error_reporting</a>&#160;<a href="#fnref1:errorreporting" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
<li id="fn:errorconstants">
<p><a href="http://php.net/manual/es/errorfunc.constants.php">constantes para error_reporting</a>&#160;<a href="#fnref1:errorconstants" rev="footnote" class="footnote-backref">&#8617;</a></p>
</li>
</ol>
</div>]]></content:encoded>
      <guid>https://jborbon.me/uso-de-operadores-bit-a-bit-en-php</guid>
      <pubDate>Thu, 23 Feb 2017 09:31:30 -0600</pubDate>
    </item>
    <item>
      <title><![CDATA[ingenioso pero complejo]]></title>
      <link>https://jborbon.me/ingenioso-pero-complejo</link>
      <description><![CDATA[<p>No, no estoy hablando de mi (<strong><em>grillos.mp3</em></strong>).</p>
<p>A veces creamos complejidad innecesaria al programar solo por tratar de ser ingeniosos. A veces una &quot;simple&quot; decisión crea un efecto dominó que se refleja en la lógica de nuestro código.</p>
<p>Sé que mucha gente que no programa en PHP o que lo hizo en el año 2000 A.C., dice que el lenguaje es horrible, que solo programadores inexpertos lo utilizan y no sé qué. Admito que el lenguaje tiene muchas fallas en su diseño al incorporar elementos de Perl, Java, C..., que es fácil iniciarse con él y eso es su gran ventaja y desventaja a la vez, pero en su defensa, me atrevo a decir que el lenguaje no hace al programador. Si bien, hay lenguajes que inculcan buenas prácticas, se puede ser malo programando en cualquier lenguaje.</p>
<p>En este post voy a mostrar un ejemplo de código que me topé por ahí en un proyecto. Desde mi punto de vista ilustra bastante bien de lo que va este rant: complejidad innecesaria.</p>
<p>He creado un <a href="https://github.com/dexbarrett/plugin-encoder-refactor">repositorio en Github</a> con las clases del código, clases para las pruebas de unidad, archivos de configuración para PHPUnit, PHP CodeSniffer y Composer , así como archivos de texto con el resultado esperado de algunas de las pruebas (fixtures).</p>
<p><div class="alert alert-warning"><strong>Aviso</strong>: los nombres de las clases, métodos y variables fueron cambiados para proteger su identidad.</div></p>
<p>La clase que mostraré a continuación tiene un solo propósito: recibir una lista de plugins que se utilizarán en el lado del cliente, codificarla en JSON y opcionalmente escapar los caracteres HTML. ¿Sencillo, no? Bueno, veamos como se encarga de ello nuestra clase:</p>
<p><strong><em>src/PluginHelperOriginal.php</em></strong></p>
<pre><code>&lt;?php
namespace App;

class PluginHelperOriginal
{
    public function encodePluginSettings()
    {

        $attributeValue = '';

        $pluginList = func_get_args();

        if (isset($pluginList[0])) {
            if ($pluginList[0] === false) {
                array_shift($pluginList);
                $attributeValue = json_encode($pluginList);
            } elseif ($pluginList[0] === true) {
                array_shift($pluginList);
                $attributeValue = htmlentities(json_encode($pluginList));
            } else {
                $attributeValue = htmlentities(json_encode($pluginList));
            }
        }

        return $attributeValue;
    }
}</code></pre>
<p><img src="//i.imgur.com/ITzF0fq.gif" class="img-responsive img-thumbnail center-block" /></p>
<p>Tenemos un método llamado <strong><em>encodePluginSettings()</em></strong> pero vemos que no tiene ningún parámetro definido. Entonces, ¿cómo y donde recibe los parámetros? Buena pregunta. Y es aquí donde comienza la confusión.</p>
<p>¿Por qué digo que aquí comienza la confusión?  Primero que nada, tenemos que indagar un poco en el código para darnos cuenta que este método utiliza una función de PHP llamada <strong><em>func_get_args()</em></strong> que devuelve un arreglo con los parámetros enviados a un método. Pero no hay parámetros definidos, ¿cómo es eso posible?
En PHP no es posible invocar a un método con MENOS parámetros de los que se definieron porque ocurre un error, pero sí es posible invocar un método con MÁS parámetros de los que se definieron. En este caso no se define ninguno para <strong><em>encodePluginSettings()</em></strong>, por lo que se puede invocar con 0 o más parámetros. Esto es un &quot;truco&quot; utilizado en PHP para utilizar <span class="post-tooltip" title="funciones con un número variable de parámetros"><em>funciones variádicas</em></span>. Por lo menos se hacía de esta manera hasta la versión 5.6 donde <a href="http://php.net/manual/es/migration56.new-features.php">ya se implementaron como parte del lenguaje</a>.</p>
<p>El método se puede invocar con 0 o más parámetros, pero se supone que debe recibir algo para transformarlo a JSON, ¿no? Cierto. Entonces hay que validar que se haya enviado <em>por lo menos</em> un parámetro. Esto es precisamente lo que verifica la primer condición; y si no se pasó algún parámetro, lo que devuelve es... el valor inicial de <strong>$attributeValue</strong>. No pues chingón, si se invoca con uno o más parámetros se procede a codificar y si no, se devuelve un <em>string</em> vacío, que todos sabemos es JSON perfectamente válido (NOT).</p>
<p>Dejando por un momento de lado esta inconsistencia, veamos qué sucede si enviamos por lo menos un parámetro. Vemos que inmediatamente después hay otra condición, pero esta se encarga de verificar si el valor de este primer parámetro equivale a <strong><em>false</em></strong>, de ser así lo remueve del array usando la función  <strong><em>array_shift()</em></strong>. Después procede a codificar a formato JSON los valores restantes con <strong><em>json_encode()</em></strong>.</p>
<p>Luego tenemos otra condición que hace algo similar, pero esta revisa si el valor del primer parámetro equivale a  <strong><em>true</em></strong>. Si la condición se cumple, también remueve el primer parámetro del array y codifica a JSON, pero adicionalmente escapa los caracteres HTML mediante  <strong><em>htmlentities() </em></strong>. Ok, parece ser que el primer parámetro es el que se utiliza para indicar si deseamos que se escapen los caracteres HTML. Pero no queremos que ese primer parámetro se codifique junto al resto, es por eso que se elimina del array.</p>
<p>Hay una condición <strong><em>else</em></strong> que se ejecutará cuando exista el primer parámetro pero su valor no sea <strong><em>true</em></strong> ni <strong><em>false</em></strong>. De cumplirse este <strong><em>else</em></strong> se codifican los parámetros en formato JSON y también se escapan los caracteres HTML. ¡Un momento!, esto es muy parecido a lo que sucede con la condición anterior, solo que no se elimina el primer elemento del array. Incluso también es muy similar a la condición que verifica si el valor del primer parámetro es <strong><em>false</em></strong>.</p>
<p>Recapitulando tenemos que:</p>
<p>Si existe al menos un parámetro y tiene un valor <strong><em>true</em></strong> entonces hay que codificar en formato JSON y escapar caracteres HTML; si el parámetro tiene un valor <strong><em>false</em></strong> solo hay que codificar en formato JSON y no escapar caracteres HTML. Pero si existe por lo menos un parámetro pero no contiene un valor tipo <strong><em>boolean</em></strong> entonces el comportamiento default es codificar a JSON y escapar caracteres HTML.</p>
<p>No sé, muchas condiciones y lógica duplicada. Sin mencionar que existen algunas inconsistencias. Por ejemplo, ¿qué pasa si se envía solamente un parámetro y es de tipo <strong><em>boolean</em></strong>? El método devolverá un <em>string</em> vacío. Esto no está bien, el método debería fallar si no recibe lo mínimo que necesita para funcionar correctamente.</p>
<p>¿Cómo podemos mejorar el código para que sea más entendible, o por lo menos no tan complejo? No se pierda nuestro próximo epis... no es cierto, veamos cómo. Pero antes de hacer cualquier modificación vamos a crear algunas pruebas de unidad en base a los resultados que arroja el código que tenemos. Haremos esto para asegurarnos que nuestros cambios no afecten los resultados.</p>
<p>Voy a utilizar Composer para aprovechar su autoloader compatible con <a href="http://www.php-fig.org/psr/psr-4/">PSR-4</a> y también para instalar algunas librerías como PHPUnit y PHP CodeSniffer (este último no es necesario, pero acostumbro utilizar el <a href="http://www.php-fig.org/psr/psr-2/">estándar PSR2</a> en mi código y esta herramienta valida que el código cumpla con este u otro estándar que le indiquemos). Ya lo mencioné antes, pero subí el código terminado y demás archivos a un <a href="https://github.com/dexbarrett/plugin-encoder-refactor">repositorio de Github</a> por si lo quieren revisar.</p>
<p>Las pruebas de unidad verifican el resultado arrojado por el método <strong><em>encodePluginSettings()</em></strong> con diferentes combinaciones de parámetros. Por ejemplo, es posible pasar uno o más <em>strings</em> que representen el nombre de plugins a utilizar, pero también es posible que cada plugin indique opciones de configuración, en cuyo caso dicho parámetro deberá ser un array.</p>
<p>Por ejemplo, supongamos que deseamos incluir un plugin llamado <em>ScrollSpy</em> y no queremos que se escapen caracteres HTML; lo haríamos de la siguiente manera:</p>
<pre><code>$pluginHelper = new App\PluginHelperOriginal;

// imprime ["scrollSpy"]
var_dump($pluginHelper-&gt;encodePluginSettings(false, 'scrollSpy')); </code></pre>
<p>Noten que pasamos como primer parámetro un valor <strong><em>false</em></strong> para indicar que no se escapen caracteres HTML. Todo lo que venga después del parámetro <em>boolean</em> se considera como la lista de plugins y se convierte a JSON.</p>
<p>En base al comportamiento actual de la clase, crearé un par de pruebas de unidad para verificar lo que arroja actualmente y  a partir de ahí hacer las modificaciones que desee y asegurarme que no se alteren los resultados:</p>
<p><strong><em>tests/EncodeOldTest.php</em></strong></p>
<pre><code>&lt;?php
require __DIR__ . '/../vendor/autoload.php';

use PHPUnit\Framework\TestCase;
use App\PluginHelperOriginal as PluginHelper;

class EncodeOldTest extends TestCase
{
    protected $pluginHelper;

    protected function setUp()
    {
        $this-&gt;pluginHelper = new PluginHelper;
    }

    protected function getFixtureContent($fixtureName)
    {
        return file_get_contents(__DIR__ . '/fixtures/' . $fixtureName . '.txt');
    }

    public function testCanEncodeASinglePlugin()
    {
        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture1'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings('ScrollSpy')
        );

        $this-&gt;assertEquals(
            '["ScrollSpy"]',
            $this-&gt;pluginHelper-&gt;encodePluginSettings(false, 'ScrollSpy')
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture2'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                ['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']]
            )
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture3'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                false,
                ['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']]
            )
        );
    }

    public function testCanEncodeMultiplePlugins()
    {
        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture4'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings('ScrollSpy', 'TabbedNavigation')
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture5'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                ['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']],
                ['TabbedNavigation', ['responsive' =&gt; true, 'color' =&gt; '#ff0000']]
            )
        );

        $this-&gt;assertEquals(
            '["ScrollSpy","TabbedNavigation"]',
            $this-&gt;pluginHelper-&gt;encodePluginSettings(false, 'ScrollSpy', 'TabbedNavigation')
        );

           $this-&gt;assertEquals(
               $this-&gt;getFixtureContent('fixture6'),
               $this-&gt;pluginHelper-&gt;encodePluginSettings(
                   false,
                   ['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']],
                   ['TabbedNavigation',['responsive' =&gt; true,'color' =&gt; '#ff0000']]
               )
           );
    }
}</code></pre>
<p>Estas pruebas simplemente comparan lo que arroja el método <strong><em>encodePluginSettings()</em></strong> con distintos parámetros; pasando un solo nombre de plugin, varios plugins, y esto mismo pero escapando y no escapando caracteres HTML.</p>
<p>Lo que vale la pena resaltar tal vez es el  método <strong><em>setUp()</em></strong>. Este es un método de PHPUnit que sirve para ejecutar código de forma automática para cada una de las pruebas definidas; esto es útil para no tener que repetir código en cada una de ellas y para inicializar objetos y así evitar que se recicle el mismo objeto y se obtenga algún resultado inesperado.</p>
<p>También se ha definido un método llamado <strong><em>getFixtureContent()</em></strong>, el cual simplemente es un método auxiliar que definí para leer contenido desde un archivo de texto. Esto debido a que algunas llamadas a <strong><em>encodePluginSettings()</em></strong> devuelven un <em>string</em> algo largo como para ponerlo directamente en el código.</p>
<p>Para ejecutar las pruebas de unidad y verificar que pasan, ejecutamos PHPUnit para este nuevo archivo desde la carpeta <strong>vendor</strong> ya que se ha instalado mediante Composer. Si ya tienen PHPUnit instalado globalmente omitan el path antes de <strong><em>phpunit</em></strong>:</p>
<pre><code>./vendor/bin/phpunit tests/EncodeOldTest.php
PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 71 ms, Memory: 3.50MB

OK (2 tests, 8 assertions)</code></pre>
<hr />
<p>Ok, las pruebas pasan, pero ahora hay que pensar cómo simplificar el código.  Lo primero que se me ocurre es que, definitivamente,  quiero deshacerme de los parámetros dinámicos. Esto podría considerarse como &quot;trampa&quot;, ya que implica modificar los parámetros del método, y por lo tanto las pruebas de unidad. Si bien es cierto, es con el fin de mejorar el código; además, no se van a alterar los resultados de las pruebas.</p>
<p>Lo más sencillo tal vez es definir dos parámetros: la lista de plugins como un array y un parámetro opcional para indicar si se debe escapar los caracteres HTML. Nombraré al primero <strong>$pluginList</strong> y al segundo <strong>$escapeHTML</strong>, el cual tendrá como valor default <strong><em>true</em></strong>:</p>
<p><strong><em>src/PluginHelper.php</em></strong></p>
<pre><code>&lt;?php
namespace App;

class PluginHelper
{
    public function encodePluginSettings(array $pluginList, $escapeHTML = true)
    {

    }
}</code></pre>
<p>El archivo de pruebas de la versión nueva de la clase espera los mismos valores, solo cambia la manera en que se mandan los parámetros. Por ejemplo, ahora se manda un array con la lista de plugins en lugar de mandar cada uno por separado. Si el plugin contiene opciones de configuración, entonces deberá ser a su vez un array, en el que el primer elemento del array es el nombre del plugin y el segundo elemento un array de opciones de configuración. Ya sé, muchos arrays en esta nueva forma de invocación, pero desde mi punto de vista es más claro dónde empieza y termina cada plugin. Es más explícito.</p>
<p><strong><em>tests/EncodeTest.php</em></strong></p>
<pre><code>&lt;?php
require __DIR__ . '/../vendor/autoload.php';

use PHPUnit\Framework\TestCase;
use App\PluginHelper;

class EncodeTest extends TestCase
{
    protected $pluginHelper;

    protected function setUp()
    {
        $this-&gt;pluginHelper = new PluginHelper;
    }

    protected function getFixtureContent($fixtureName)
    {
        return file_get_contents(__DIR__ . '/fixtures/' . $fixtureName . '.txt');
    }

    public function testCanEncodeASinglePlugin()
    {
        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture1'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(['ScrollSpy'])
        );

        $this-&gt;assertEquals(
            '["ScrollSpy"]',
            $this-&gt;pluginHelper-&gt;encodePluginSettings(['ScrollSpy'], false)
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture2'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                [['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']]]
            )
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture3'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                [['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']]],
                false
            )
        );
    }

    public function testCanEncodeMultiplePlugins()
    {
        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture4'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(['ScrollSpy', 'TabbedNavigation'])
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture5'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                [
                    [ 'ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']],
                    ['TabbedNavigation', ['responsive' =&gt; true, 'color' =&gt; '#ff0000']]
                ]
            )
        );

        $this-&gt;assertEquals(
            '["ScrollSpy","TabbedNavigation"]',
            $this-&gt;pluginHelper-&gt;encodePluginSettings(['ScrollSpy', 'TabbedNavigation'], false)
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture6'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                [
                   ['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']],
                   ['TabbedNavigation',['responsive' =&gt; true,'color' =&gt; '#ff0000']]
                ],
                false
            )
        );
    }
}</code></pre>
<p>Si ejecutamos PHPUnit para el archivo <strong><em>EncodeTest.php</em></strong> las pruebas van a fallar porque el método <strong><em>encodePluginSettings()</em></strong> no contiene código. ¿Qué deberá llevar entonces? Algo como lo siguiente:</p>
<p><strong><em>src/PluginHelper.php</em></strong></p>
<pre><code>&lt;?php
namespace App;

class PluginHelper
{
    public function encodePluginSettings(array $pluginList, $escapeHTML = true)
    {

        $attributeValue = json_encode($pluginList);

        if ($escapeHTML) {
            $attributeValue = htmlentities($attributeValue);
        }

        return $attributeValue;
    }
}</code></pre>
<p>Veamos las diferencias. Simplemente con definir los parámetros de manera explícita ya no es necesario verificar si existe el primer parámetro y qué tipo de valor contiene para saber cómo responder. El parser de PHP se encargará de lanzar un error si no se pasan por lo menos estos dos parámetros que hemos especificado.</p>
<p>Al definir <strong><em>$escapeHTML</em></strong> con un valor por default, no tuvimos que utilizar aquella condición <strong><em>else</em></strong> a manera de <em>fallback</em> en caso de que no se haya especificado un valor.</p>
<p>Ahora el propósito del código es más claro: se codifica a JSON la lista de plugins y sus opciones de configuración y se guarda el resultado en una variable. Si se ha pasado un valor para el parámetro <strong> $escapeHTML</strong>  y el valor es <strong><em>true</em></strong> entonces hay que escapar los caracteres HTML de los datos que fueron codificados en JSON. Si no se especifica un valor para <strong> $escapeHTML</strong> entonces simplemente tomará el valor por default (<strong><em>true</em></strong>).</p>
<p>¡Pero no tan rápido! ¿Cómo sabemos que esta nueva implentación funciona? Bueno, no me miren a mi, veamos que dice PHPUnit. Para ello hay que ejecutar <strong>phpunit</strong> con el archivo de pruebas correspondiente a la nueva implementación, es decir, el archivo <strong><em>EncodeTest.php</em></strong>:</p>
<pre><code>./vendor/bin/phpunit tests/EncodeTest.php
PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 67 ms, Memory: 3.50MB

OK (2 tests, 8 assertions)</code></pre>
<p>Las pruebas funcionan, así que la nueva implementación no afecta el resultado esperado. Lo hemos logrado.</p>
<p>Como ven, a veces es fácil complicarse para solucionar algo muy sencillo. O simplemente queremos buscar la forma más corta o ingeniosa de programar, pero olvidamos que no es coincidencia que los lenguajes modernos se asemejen a un idioma usado por las personas (en este caso, y en su mayoría, inglés). El código debe ser comprensible para los desarrolladores, no para las computadoras. De lo contrario todos programaríamos en lenguaje binario.</p>
<hr />
<p>Enlaces:</p>
<ul>
<li><a href="https://github.com/dexbarrett/plugin-encoder-refactor">código y archivos del post</a></li>
</ul>]]></description>
      <content:encoded><![CDATA[<p>No, no estoy hablando de mi (<strong><em>grillos.mp3</em></strong>).</p>
<p>A veces creamos complejidad innecesaria al programar solo por tratar de ser ingeniosos. A veces una &quot;simple&quot; decisión crea un efecto dominó que se refleja en la lógica de nuestro código.</p>
<p>Sé que mucha gente que no programa en PHP o que lo hizo en el año 2000 A.C., dice que el lenguaje es horrible, que solo programadores inexpertos lo utilizan y no sé qué. Admito que el lenguaje tiene muchas fallas en su diseño al incorporar elementos de Perl, Java, C..., que es fácil iniciarse con él y eso es su gran ventaja y desventaja a la vez, pero en su defensa, me atrevo a decir que el lenguaje no hace al programador. Si bien, hay lenguajes que inculcan buenas prácticas, se puede ser malo programando en cualquier lenguaje.</p>
<p>En este post voy a mostrar un ejemplo de código que me topé por ahí en un proyecto. Desde mi punto de vista ilustra bastante bien de lo que va este rant: complejidad innecesaria.</p>
<p>He creado un <a href="https://github.com/dexbarrett/plugin-encoder-refactor">repositorio en Github</a> con las clases del código, clases para las pruebas de unidad, archivos de configuración para PHPUnit, PHP CodeSniffer y Composer , así como archivos de texto con el resultado esperado de algunas de las pruebas (fixtures).</p>
<p><div class="alert alert-warning"><strong>Aviso</strong>: los nombres de las clases, métodos y variables fueron cambiados para proteger su identidad.</div></p>
<p>La clase que mostraré a continuación tiene un solo propósito: recibir una lista de plugins que se utilizarán en el lado del cliente, codificarla en JSON y opcionalmente escapar los caracteres HTML. ¿Sencillo, no? Bueno, veamos como se encarga de ello nuestra clase:</p>
<p><strong><em>src/PluginHelperOriginal.php</em></strong></p>
<pre><code>&lt;?php
namespace App;

class PluginHelperOriginal
{
    public function encodePluginSettings()
    {

        $attributeValue = '';

        $pluginList = func_get_args();

        if (isset($pluginList[0])) {
            if ($pluginList[0] === false) {
                array_shift($pluginList);
                $attributeValue = json_encode($pluginList);
            } elseif ($pluginList[0] === true) {
                array_shift($pluginList);
                $attributeValue = htmlentities(json_encode($pluginList));
            } else {
                $attributeValue = htmlentities(json_encode($pluginList));
            }
        }

        return $attributeValue;
    }
}</code></pre>
<p><img src="//i.imgur.com/ITzF0fq.gif" class="img-responsive img-thumbnail center-block" /></p>
<p>Tenemos un método llamado <strong><em>encodePluginSettings()</em></strong> pero vemos que no tiene ningún parámetro definido. Entonces, ¿cómo y donde recibe los parámetros? Buena pregunta. Y es aquí donde comienza la confusión.</p>
<p>¿Por qué digo que aquí comienza la confusión?  Primero que nada, tenemos que indagar un poco en el código para darnos cuenta que este método utiliza una función de PHP llamada <strong><em>func_get_args()</em></strong> que devuelve un arreglo con los parámetros enviados a un método. Pero no hay parámetros definidos, ¿cómo es eso posible?
En PHP no es posible invocar a un método con MENOS parámetros de los que se definieron porque ocurre un error, pero sí es posible invocar un método con MÁS parámetros de los que se definieron. En este caso no se define ninguno para <strong><em>encodePluginSettings()</em></strong>, por lo que se puede invocar con 0 o más parámetros. Esto es un &quot;truco&quot; utilizado en PHP para utilizar <span class="post-tooltip" title="funciones con un número variable de parámetros"><em>funciones variádicas</em></span>. Por lo menos se hacía de esta manera hasta la versión 5.6 donde <a href="http://php.net/manual/es/migration56.new-features.php">ya se implementaron como parte del lenguaje</a>.</p>
<p>El método se puede invocar con 0 o más parámetros, pero se supone que debe recibir algo para transformarlo a JSON, ¿no? Cierto. Entonces hay que validar que se haya enviado <em>por lo menos</em> un parámetro. Esto es precisamente lo que verifica la primer condición; y si no se pasó algún parámetro, lo que devuelve es... el valor inicial de <strong>$attributeValue</strong>. No pues chingón, si se invoca con uno o más parámetros se procede a codificar y si no, se devuelve un <em>string</em> vacío, que todos sabemos es JSON perfectamente válido (NOT).</p>
<p>Dejando por un momento de lado esta inconsistencia, veamos qué sucede si enviamos por lo menos un parámetro. Vemos que inmediatamente después hay otra condición, pero esta se encarga de verificar si el valor de este primer parámetro equivale a <strong><em>false</em></strong>, de ser así lo remueve del array usando la función  <strong><em>array_shift()</em></strong>. Después procede a codificar a formato JSON los valores restantes con <strong><em>json_encode()</em></strong>.</p>
<p>Luego tenemos otra condición que hace algo similar, pero esta revisa si el valor del primer parámetro equivale a  <strong><em>true</em></strong>. Si la condición se cumple, también remueve el primer parámetro del array y codifica a JSON, pero adicionalmente escapa los caracteres HTML mediante  <strong><em>htmlentities() </em></strong>. Ok, parece ser que el primer parámetro es el que se utiliza para indicar si deseamos que se escapen los caracteres HTML. Pero no queremos que ese primer parámetro se codifique junto al resto, es por eso que se elimina del array.</p>
<p>Hay una condición <strong><em>else</em></strong> que se ejecutará cuando exista el primer parámetro pero su valor no sea <strong><em>true</em></strong> ni <strong><em>false</em></strong>. De cumplirse este <strong><em>else</em></strong> se codifican los parámetros en formato JSON y también se escapan los caracteres HTML. ¡Un momento!, esto es muy parecido a lo que sucede con la condición anterior, solo que no se elimina el primer elemento del array. Incluso también es muy similar a la condición que verifica si el valor del primer parámetro es <strong><em>false</em></strong>.</p>
<p>Recapitulando tenemos que:</p>
<p>Si existe al menos un parámetro y tiene un valor <strong><em>true</em></strong> entonces hay que codificar en formato JSON y escapar caracteres HTML; si el parámetro tiene un valor <strong><em>false</em></strong> solo hay que codificar en formato JSON y no escapar caracteres HTML. Pero si existe por lo menos un parámetro pero no contiene un valor tipo <strong><em>boolean</em></strong> entonces el comportamiento default es codificar a JSON y escapar caracteres HTML.</p>
<p>No sé, muchas condiciones y lógica duplicada. Sin mencionar que existen algunas inconsistencias. Por ejemplo, ¿qué pasa si se envía solamente un parámetro y es de tipo <strong><em>boolean</em></strong>? El método devolverá un <em>string</em> vacío. Esto no está bien, el método debería fallar si no recibe lo mínimo que necesita para funcionar correctamente.</p>
<p>¿Cómo podemos mejorar el código para que sea más entendible, o por lo menos no tan complejo? No se pierda nuestro próximo epis... no es cierto, veamos cómo. Pero antes de hacer cualquier modificación vamos a crear algunas pruebas de unidad en base a los resultados que arroja el código que tenemos. Haremos esto para asegurarnos que nuestros cambios no afecten los resultados.</p>
<p>Voy a utilizar Composer para aprovechar su autoloader compatible con <a href="http://www.php-fig.org/psr/psr-4/">PSR-4</a> y también para instalar algunas librerías como PHPUnit y PHP CodeSniffer (este último no es necesario, pero acostumbro utilizar el <a href="http://www.php-fig.org/psr/psr-2/">estándar PSR2</a> en mi código y esta herramienta valida que el código cumpla con este u otro estándar que le indiquemos). Ya lo mencioné antes, pero subí el código terminado y demás archivos a un <a href="https://github.com/dexbarrett/plugin-encoder-refactor">repositorio de Github</a> por si lo quieren revisar.</p>
<p>Las pruebas de unidad verifican el resultado arrojado por el método <strong><em>encodePluginSettings()</em></strong> con diferentes combinaciones de parámetros. Por ejemplo, es posible pasar uno o más <em>strings</em> que representen el nombre de plugins a utilizar, pero también es posible que cada plugin indique opciones de configuración, en cuyo caso dicho parámetro deberá ser un array.</p>
<p>Por ejemplo, supongamos que deseamos incluir un plugin llamado <em>ScrollSpy</em> y no queremos que se escapen caracteres HTML; lo haríamos de la siguiente manera:</p>
<pre><code>$pluginHelper = new App\PluginHelperOriginal;

// imprime ["scrollSpy"]
var_dump($pluginHelper-&gt;encodePluginSettings(false, 'scrollSpy')); </code></pre>
<p>Noten que pasamos como primer parámetro un valor <strong><em>false</em></strong> para indicar que no se escapen caracteres HTML. Todo lo que venga después del parámetro <em>boolean</em> se considera como la lista de plugins y se convierte a JSON.</p>
<p>En base al comportamiento actual de la clase, crearé un par de pruebas de unidad para verificar lo que arroja actualmente y  a partir de ahí hacer las modificaciones que desee y asegurarme que no se alteren los resultados:</p>
<p><strong><em>tests/EncodeOldTest.php</em></strong></p>
<pre><code>&lt;?php
require __DIR__ . '/../vendor/autoload.php';

use PHPUnit\Framework\TestCase;
use App\PluginHelperOriginal as PluginHelper;

class EncodeOldTest extends TestCase
{
    protected $pluginHelper;

    protected function setUp()
    {
        $this-&gt;pluginHelper = new PluginHelper;
    }

    protected function getFixtureContent($fixtureName)
    {
        return file_get_contents(__DIR__ . '/fixtures/' . $fixtureName . '.txt');
    }

    public function testCanEncodeASinglePlugin()
    {
        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture1'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings('ScrollSpy')
        );

        $this-&gt;assertEquals(
            '["ScrollSpy"]',
            $this-&gt;pluginHelper-&gt;encodePluginSettings(false, 'ScrollSpy')
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture2'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                ['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']]
            )
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture3'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                false,
                ['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']]
            )
        );
    }

    public function testCanEncodeMultiplePlugins()
    {
        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture4'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings('ScrollSpy', 'TabbedNavigation')
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture5'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                ['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']],
                ['TabbedNavigation', ['responsive' =&gt; true, 'color' =&gt; '#ff0000']]
            )
        );

        $this-&gt;assertEquals(
            '["ScrollSpy","TabbedNavigation"]',
            $this-&gt;pluginHelper-&gt;encodePluginSettings(false, 'ScrollSpy', 'TabbedNavigation')
        );

           $this-&gt;assertEquals(
               $this-&gt;getFixtureContent('fixture6'),
               $this-&gt;pluginHelper-&gt;encodePluginSettings(
                   false,
                   ['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']],
                   ['TabbedNavigation',['responsive' =&gt; true,'color' =&gt; '#ff0000']]
               )
           );
    }
}</code></pre>
<p>Estas pruebas simplemente comparan lo que arroja el método <strong><em>encodePluginSettings()</em></strong> con distintos parámetros; pasando un solo nombre de plugin, varios plugins, y esto mismo pero escapando y no escapando caracteres HTML.</p>
<p>Lo que vale la pena resaltar tal vez es el  método <strong><em>setUp()</em></strong>. Este es un método de PHPUnit que sirve para ejecutar código de forma automática para cada una de las pruebas definidas; esto es útil para no tener que repetir código en cada una de ellas y para inicializar objetos y así evitar que se recicle el mismo objeto y se obtenga algún resultado inesperado.</p>
<p>También se ha definido un método llamado <strong><em>getFixtureContent()</em></strong>, el cual simplemente es un método auxiliar que definí para leer contenido desde un archivo de texto. Esto debido a que algunas llamadas a <strong><em>encodePluginSettings()</em></strong> devuelven un <em>string</em> algo largo como para ponerlo directamente en el código.</p>
<p>Para ejecutar las pruebas de unidad y verificar que pasan, ejecutamos PHPUnit para este nuevo archivo desde la carpeta <strong>vendor</strong> ya que se ha instalado mediante Composer. Si ya tienen PHPUnit instalado globalmente omitan el path antes de <strong><em>phpunit</em></strong>:</p>
<pre><code>./vendor/bin/phpunit tests/EncodeOldTest.php
PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 71 ms, Memory: 3.50MB

OK (2 tests, 8 assertions)</code></pre>
<hr />
<p>Ok, las pruebas pasan, pero ahora hay que pensar cómo simplificar el código.  Lo primero que se me ocurre es que, definitivamente,  quiero deshacerme de los parámetros dinámicos. Esto podría considerarse como &quot;trampa&quot;, ya que implica modificar los parámetros del método, y por lo tanto las pruebas de unidad. Si bien es cierto, es con el fin de mejorar el código; además, no se van a alterar los resultados de las pruebas.</p>
<p>Lo más sencillo tal vez es definir dos parámetros: la lista de plugins como un array y un parámetro opcional para indicar si se debe escapar los caracteres HTML. Nombraré al primero <strong>$pluginList</strong> y al segundo <strong>$escapeHTML</strong>, el cual tendrá como valor default <strong><em>true</em></strong>:</p>
<p><strong><em>src/PluginHelper.php</em></strong></p>
<pre><code>&lt;?php
namespace App;

class PluginHelper
{
    public function encodePluginSettings(array $pluginList, $escapeHTML = true)
    {

    }
}</code></pre>
<p>El archivo de pruebas de la versión nueva de la clase espera los mismos valores, solo cambia la manera en que se mandan los parámetros. Por ejemplo, ahora se manda un array con la lista de plugins en lugar de mandar cada uno por separado. Si el plugin contiene opciones de configuración, entonces deberá ser a su vez un array, en el que el primer elemento del array es el nombre del plugin y el segundo elemento un array de opciones de configuración. Ya sé, muchos arrays en esta nueva forma de invocación, pero desde mi punto de vista es más claro dónde empieza y termina cada plugin. Es más explícito.</p>
<p><strong><em>tests/EncodeTest.php</em></strong></p>
<pre><code>&lt;?php
require __DIR__ . '/../vendor/autoload.php';

use PHPUnit\Framework\TestCase;
use App\PluginHelper;

class EncodeTest extends TestCase
{
    protected $pluginHelper;

    protected function setUp()
    {
        $this-&gt;pluginHelper = new PluginHelper;
    }

    protected function getFixtureContent($fixtureName)
    {
        return file_get_contents(__DIR__ . '/fixtures/' . $fixtureName . '.txt');
    }

    public function testCanEncodeASinglePlugin()
    {
        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture1'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(['ScrollSpy'])
        );

        $this-&gt;assertEquals(
            '["ScrollSpy"]',
            $this-&gt;pluginHelper-&gt;encodePluginSettings(['ScrollSpy'], false)
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture2'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                [['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']]]
            )
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture3'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                [['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']]],
                false
            )
        );
    }

    public function testCanEncodeMultiplePlugins()
    {
        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture4'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(['ScrollSpy', 'TabbedNavigation'])
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture5'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                [
                    [ 'ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']],
                    ['TabbedNavigation', ['responsive' =&gt; true, 'color' =&gt; '#ff0000']]
                ]
            )
        );

        $this-&gt;assertEquals(
            '["ScrollSpy","TabbedNavigation"]',
            $this-&gt;pluginHelper-&gt;encodePluginSettings(['ScrollSpy', 'TabbedNavigation'], false)
        );

        $this-&gt;assertEquals(
            $this-&gt;getFixtureContent('fixture6'),
            $this-&gt;pluginHelper-&gt;encodePluginSettings(
                [
                   ['ScrollSpy', ['delay' =&gt; 500, 'color' =&gt; '#ff00ff']],
                   ['TabbedNavigation',['responsive' =&gt; true,'color' =&gt; '#ff0000']]
                ],
                false
            )
        );
    }
}</code></pre>
<p>Si ejecutamos PHPUnit para el archivo <strong><em>EncodeTest.php</em></strong> las pruebas van a fallar porque el método <strong><em>encodePluginSettings()</em></strong> no contiene código. ¿Qué deberá llevar entonces? Algo como lo siguiente:</p>
<p><strong><em>src/PluginHelper.php</em></strong></p>
<pre><code>&lt;?php
namespace App;

class PluginHelper
{
    public function encodePluginSettings(array $pluginList, $escapeHTML = true)
    {

        $attributeValue = json_encode($pluginList);

        if ($escapeHTML) {
            $attributeValue = htmlentities($attributeValue);
        }

        return $attributeValue;
    }
}</code></pre>
<p>Veamos las diferencias. Simplemente con definir los parámetros de manera explícita ya no es necesario verificar si existe el primer parámetro y qué tipo de valor contiene para saber cómo responder. El parser de PHP se encargará de lanzar un error si no se pasan por lo menos estos dos parámetros que hemos especificado.</p>
<p>Al definir <strong><em>$escapeHTML</em></strong> con un valor por default, no tuvimos que utilizar aquella condición <strong><em>else</em></strong> a manera de <em>fallback</em> en caso de que no se haya especificado un valor.</p>
<p>Ahora el propósito del código es más claro: se codifica a JSON la lista de plugins y sus opciones de configuración y se guarda el resultado en una variable. Si se ha pasado un valor para el parámetro <strong> $escapeHTML</strong>  y el valor es <strong><em>true</em></strong> entonces hay que escapar los caracteres HTML de los datos que fueron codificados en JSON. Si no se especifica un valor para <strong> $escapeHTML</strong> entonces simplemente tomará el valor por default (<strong><em>true</em></strong>).</p>
<p>¡Pero no tan rápido! ¿Cómo sabemos que esta nueva implentación funciona? Bueno, no me miren a mi, veamos que dice PHPUnit. Para ello hay que ejecutar <strong>phpunit</strong> con el archivo de pruebas correspondiente a la nueva implementación, es decir, el archivo <strong><em>EncodeTest.php</em></strong>:</p>
<pre><code>./vendor/bin/phpunit tests/EncodeTest.php
PHPUnit 5.5.4 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 67 ms, Memory: 3.50MB

OK (2 tests, 8 assertions)</code></pre>
<p>Las pruebas funcionan, así que la nueva implementación no afecta el resultado esperado. Lo hemos logrado.</p>
<p>Como ven, a veces es fácil complicarse para solucionar algo muy sencillo. O simplemente queremos buscar la forma más corta o ingeniosa de programar, pero olvidamos que no es coincidencia que los lenguajes modernos se asemejen a un idioma usado por las personas (en este caso, y en su mayoría, inglés). El código debe ser comprensible para los desarrolladores, no para las computadoras. De lo contrario todos programaríamos en lenguaje binario.</p>
<hr />
<p>Enlaces:</p>
<ul>
<li><a href="https://github.com/dexbarrett/plugin-encoder-refactor">código y archivos del post</a></li>
</ul>]]></content:encoded>
      <guid>https://jborbon.me/ingenioso-pero-complejo</guid>
      <pubDate>Sat, 17 Sep 2016 16:08:45 -0500</pubDate>
    </item>
    <item>
      <title><![CDATA[SMS vs WhatsApp]]></title>
      <link>https://jborbon.me/sms-vs-whatsapp</link>
      <description><![CDATA[<p>— <em>¿Cómo nos ponemos de acuerdo?</em></p>
<p>— <em>Te mando un WhatsApp al rato.</em></p>
<p>Dejando de lado que es incorrecto decir &quot;un WhatsApp&quot;, ya es bien común que la mayoría de la gente se comunique usando esta aplicación. En lo personal, me caga que supongan que todo mundo la utiliza.<br />
Sí, es muy popular y no sé qué. Pero su popularidad en México solo es prueba de qué tan jodidos están los planes de telefonía; en otros países hace mucho que existen planes de llamadas y mensajes ilimitados.</p>
<p>Aquí es algo relativamente nuevo; lo usual era escuchar gente quejarse porque le llegó la factura más reciente de su celular y darse cuenta que ese mes va  a tener que vivir de puro atún y tortillas. Entonces, es lógico que si había una alternativa a pagar tanto, la gente la iba a usar. </p>
<blockquote>
<p>¿Dice que con esto puedo mandar mensajes a otros que también la usen y solo debo estar conectado al WiFi? ¿A quién tengo que matar?</p>
</blockquote>
<p>Ahora, el post no se trata sobre la gente que utiliza WhatsApp, pero sí va por el lado de la popularidad y creer que reemplaza cualquier forma de comunicación.</p>
<p>Hace tiempo leí un tweet que decía algo como <em>¿Por qué gastan o se molestan en implementar notificaciones por SMS en sus aplicaciones web cuando pueden notificar a los usuarios mediante WhatsApp?</em></p>
<p>Esta persona al parecer quiso dar a entender que los SMS ya son obsoletos. Yo pienso que no, y aquí algunas razones de por qué lo creo:</p>
<p><strong>Compatibilidad</strong></p>
<p>Sé que ahora hasta en los smartphone más económicos se puede instalar WhatsApp, pero si hemos creado una aplicación que notifica usuarios y queremos aseguramos casi al 100% de que van a recibir el mensaje, un SMS es mejor opción en ese caso. Los SMS no requieren que el usuario tenga un plan de datos activo, solo señal y suficiente batería. Puedes tener un teléfono de hace 10 años y aún así vas a recibir tu SMS.</p>
<p><strong>Independencia de aplicaciones</strong></p>
<p>Muy similar al punto anterior, pero este va más ligado a la libertad de elegir, o dicho de otra manera, a no tener que obligar a los usuarios a utilizar tal o cual aplicación. Como dije antes, WhatsApp es muy popular en México, pero hay quienes simplemente prefieren utilizar otras aplicaciones de mensajería. Un SMS no necesita de una aplicación especial instalada pues podríamos decir que es &quot;nativo&quot; al teléfono.</p>
<p><strong>Facilidad de integración</strong></p>
<p>Integrar servicios de SMS como <a href="https://www.clockworksms.com/">Clockwork</a> es muy sencillo. Además de abonar crédito solo es necesario crear una API key y utilizar alguna de sus librerías para comunicarse con su API, y listo. </p>
<p>Integrar notificaciones por WhatsApp no es tan sencillo. Primero que nada, WhatsApp no tiene una API oficial (por lo menos no a la fecha de este post).  Las librerías que existen solo son resultado de ingeniería inversa al servicio de mensajería de WhatsApp. Al no existir una API oficial tampoco hay soporte ni garantía de que el servicio vaya a funcionar a un nivel confiable en nuestra aplicación. Algo más que mencionar es que las peticiones al servicio de mensajería son bien pinche lentas, por lo menos usando la librería de PHP; no sé si ocurre lo mismo con otras librerías.</p>
<hr />
<p>¿Alguna otra alternativa ? Las hay, pero el problema es si las personas las vayan a usar. Por ejemplo, podríamos simplemente enviar un correo electrónico. La persona puede revisarlo en su computadora, tablet, teléfono, siempre y cuando el dispositivo esté conectado a internet. </p>
<p>Podríamos por ejemplo integrar otra aplicación de mensajería que sí tenga API, como <a href="https://core.telegram.org/">Telegram</a>. Aquí el problema es que —para bien o para mal— WhatsApp sigue siendo más popular y resulta más práctico para los usuarios puesto que los planes de datos, incluso los que se pueden activar mediante prepago, ofrecen conexión ilimitada para WhatsApp, Facebook y Twitter. Telegram no está incluído en esta lista, por lo que consume datos del plan.</p>
<p>Vaya, es algo complicado, ¿cierto? </p>
<p>Entonces, ¿cual es el punto? Yo diría que lo importante es decidir qué es lo importante a la hora de integrar notificaciones en una aplicación. ¿Sabemos que la mayoría de nuestros usuarios usan WhatsApp y no tenemos problema con que haya intermitencia en el servicio o que llegue a ser lento? ¡WhatsApp! </p>
<p>¿Queremos estar lo más seguro posible de que los usuarios reciban las notificaciones y podemos pagar el costo de un servicio SMS? ¡SMS!</p>
<p>También podríamos hacer un poco de magia negra para lograr que la gente comience a usar otras aplicaciones y para que las compañías de telefonía incluyan más aplicaciones en su lista de consumo de datos ilimitado. Pero por lo pronto, la respuesta es la misma para cualquier pregunta complicada: <strong>depende</strong>.</p>]]></description>
      <content:encoded><![CDATA[<p>— <em>¿Cómo nos ponemos de acuerdo?</em></p>
<p>— <em>Te mando un WhatsApp al rato.</em></p>
<p>Dejando de lado que es incorrecto decir &quot;un WhatsApp&quot;, ya es bien común que la mayoría de la gente se comunique usando esta aplicación. En lo personal, me caga que supongan que todo mundo la utiliza.<br />
Sí, es muy popular y no sé qué. Pero su popularidad en México solo es prueba de qué tan jodidos están los planes de telefonía; en otros países hace mucho que existen planes de llamadas y mensajes ilimitados.</p>
<p>Aquí es algo relativamente nuevo; lo usual era escuchar gente quejarse porque le llegó la factura más reciente de su celular y darse cuenta que ese mes va  a tener que vivir de puro atún y tortillas. Entonces, es lógico que si había una alternativa a pagar tanto, la gente la iba a usar. </p>
<blockquote>
<p>¿Dice que con esto puedo mandar mensajes a otros que también la usen y solo debo estar conectado al WiFi? ¿A quién tengo que matar?</p>
</blockquote>
<p>Ahora, el post no se trata sobre la gente que utiliza WhatsApp, pero sí va por el lado de la popularidad y creer que reemplaza cualquier forma de comunicación.</p>
<p>Hace tiempo leí un tweet que decía algo como <em>¿Por qué gastan o se molestan en implementar notificaciones por SMS en sus aplicaciones web cuando pueden notificar a los usuarios mediante WhatsApp?</em></p>
<p>Esta persona al parecer quiso dar a entender que los SMS ya son obsoletos. Yo pienso que no, y aquí algunas razones de por qué lo creo:</p>
<p><strong>Compatibilidad</strong></p>
<p>Sé que ahora hasta en los smartphone más económicos se puede instalar WhatsApp, pero si hemos creado una aplicación que notifica usuarios y queremos aseguramos casi al 100% de que van a recibir el mensaje, un SMS es mejor opción en ese caso. Los SMS no requieren que el usuario tenga un plan de datos activo, solo señal y suficiente batería. Puedes tener un teléfono de hace 10 años y aún así vas a recibir tu SMS.</p>
<p><strong>Independencia de aplicaciones</strong></p>
<p>Muy similar al punto anterior, pero este va más ligado a la libertad de elegir, o dicho de otra manera, a no tener que obligar a los usuarios a utilizar tal o cual aplicación. Como dije antes, WhatsApp es muy popular en México, pero hay quienes simplemente prefieren utilizar otras aplicaciones de mensajería. Un SMS no necesita de una aplicación especial instalada pues podríamos decir que es &quot;nativo&quot; al teléfono.</p>
<p><strong>Facilidad de integración</strong></p>
<p>Integrar servicios de SMS como <a href="https://www.clockworksms.com/">Clockwork</a> es muy sencillo. Además de abonar crédito solo es necesario crear una API key y utilizar alguna de sus librerías para comunicarse con su API, y listo. </p>
<p>Integrar notificaciones por WhatsApp no es tan sencillo. Primero que nada, WhatsApp no tiene una API oficial (por lo menos no a la fecha de este post).  Las librerías que existen solo son resultado de ingeniería inversa al servicio de mensajería de WhatsApp. Al no existir una API oficial tampoco hay soporte ni garantía de que el servicio vaya a funcionar a un nivel confiable en nuestra aplicación. Algo más que mencionar es que las peticiones al servicio de mensajería son bien pinche lentas, por lo menos usando la librería de PHP; no sé si ocurre lo mismo con otras librerías.</p>
<hr />
<p>¿Alguna otra alternativa ? Las hay, pero el problema es si las personas las vayan a usar. Por ejemplo, podríamos simplemente enviar un correo electrónico. La persona puede revisarlo en su computadora, tablet, teléfono, siempre y cuando el dispositivo esté conectado a internet. </p>
<p>Podríamos por ejemplo integrar otra aplicación de mensajería que sí tenga API, como <a href="https://core.telegram.org/">Telegram</a>. Aquí el problema es que —para bien o para mal— WhatsApp sigue siendo más popular y resulta más práctico para los usuarios puesto que los planes de datos, incluso los que se pueden activar mediante prepago, ofrecen conexión ilimitada para WhatsApp, Facebook y Twitter. Telegram no está incluído en esta lista, por lo que consume datos del plan.</p>
<p>Vaya, es algo complicado, ¿cierto? </p>
<p>Entonces, ¿cual es el punto? Yo diría que lo importante es decidir qué es lo importante a la hora de integrar notificaciones en una aplicación. ¿Sabemos que la mayoría de nuestros usuarios usan WhatsApp y no tenemos problema con que haya intermitencia en el servicio o que llegue a ser lento? ¡WhatsApp! </p>
<p>¿Queremos estar lo más seguro posible de que los usuarios reciban las notificaciones y podemos pagar el costo de un servicio SMS? ¡SMS!</p>
<p>También podríamos hacer un poco de magia negra para lograr que la gente comience a usar otras aplicaciones y para que las compañías de telefonía incluyan más aplicaciones en su lista de consumo de datos ilimitado. Pero por lo pronto, la respuesta es la misma para cualquier pregunta complicada: <strong>depende</strong>.</p>]]></content:encoded>
      <guid>https://jborbon.me/sms-vs-whatsapp</guid>
      <pubDate>Sun, 14 Aug 2016 19:26:51 -0500</pubDate>
    </item>
    <item>
      <title><![CDATA[siempre hay una mejor manera]]></title>
      <link>https://jborbon.me/siempre-hay-una-mejor-manera</link>
      <description><![CDATA[<p>En ocasiones al estar programando algo, la intuición nos dice que básicamente traemos un desmadre. No sabemos con exactitud cómo encontrar una solución más elegante y pensamos: <em>seguro hay una mejor manera</em>.</p>
<p>Esto no es del todo paranóico o perfeccionista. No siempre es posible encontrar la forma más rápida o creativa de programar algo, pero a veces, si investigamos un poco, podemos toparnos con un ángulo distinto.</p>
<p>Pongamos dos ejemplos; ambos sencillos. El punto aquí es ver cómo resolveríamos cada uno.</p>
<hr />
<h2>Suma de números consecutivos</h2>
<p>Primer ejemplo: obtener la suma de números enteros del 1 a n. Bastante sencillo. Podemos hacer un ciclo e ir acumulando la sumatoria:</p>
<pre><code>$suma = 0;

for($c=1; $c&lt;= 100; $c++) {
    $suma += $c;
}

echo $suma; // imprime 5050</code></pre>
<p>Funciona: Imprime el resultado correcto. Pero no sé, me parece innecesario solamente para sumar una serie de valores. Una alternativa es evitar utilizar un ciclo y aprovechar las funciones de PHP. En ese caso podemos utilizar <code>array_sum()</code>, pero esta función requiere de un arreglo. Bueno, para eso podemos utilizar otra función del lenguaje: <code>range()</code>, que genera un arreglo con los valores mínimo y máximo indicados. Esta solución quedaría de la siguiente manera:</p>
<pre><code>echo array_sum(range(1, 100)); // imprime 5050</code></pre>
<p>El mismo resultado, en una sola línea. Aunque, claro, esto no se trata de reducir el número de líneas. El código es más corto que el anterior, pero aunque hemos delegado al lenguaje el trabajo de sumatoria, aún así generamos un arreglo al utilizar <code>range()</code>. </p>
<p>Cómo mencionaba antes, no siempre será posible encontrar una solución perfecta. En programación siempre  vamos a tener que decidir que es más importante para la aplicación: que se ejecute con rapidez, que el código sea entendible... entre otras decisiones.</p>
<p>Para este ejemplo en particular, existe una fórmula que permite sumar la serie de números. Esta fórmula se llama <em>Suma de Gauss</em>. La forma simplificada de la fórmula es la siguiente:</p>
<p>n (n +1)  / 2</p>
<p>Utilicémosla en un ejemplo de código:</p>
<pre><code>$n = 100;

$sum = (($n + 1) * $n) / 2;

echo $sum; //imprime 5050</code></pre>
<blockquote>
<p><em>Ok, ok. Esta solución también es correcta, pero la solución anterior con <code>array_sum()</code> y <code>range()</code> sigue siendo más corta y entendible.</em></p>
</blockquote>
<p>De acuerdo, pero no tan rápido, amigos. Resulta que la solución que usa <code>range()</code> tiene un &quot;pequeño&quot; problema. Crear un arreglo con valores del 1 al 100 es asunto trivial, pero si el valor superior es muy grande, ahí es cuando todo se viene abajo. </p>
<p>Supongamos que deseamos obtener la suma de números del 1 al 9223399 utilizando la solución con <code>range()</code>:</p>
<pre><code>echo array_sum(range(1, 9223399));</code></pre>
<p>Si ejecutamos el código es posible que obtengamos el siguiente mensaje de error:</p>
<pre><code>Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 536870920 bytes)</code></pre>
<p>¡Ups, parece que se agotó la memoria disponible para PHP! ¡Por sumar un rango de números! En este caso la memoria disponible es de 134217728 (128 Mb) y nuestro código requiere  536870920 bytes (512 Mb). Así es, 512 Megabytes para sumar una secuencia de números.</p>
<p>Ahora probemos obtener la suma del mismo rango pero ahora utilizando la fórmula de Gauss:</p>
<pre><code>$n = 9223399;

$sum = (($n + 1) * $n) / 2;

echo $sum; // imprime 42535549168300</code></pre>
<p>Funciona, y no requiere 512 Mb de memoria.</p>
<p>Entonces, ¿siempre hay que buscar fórmulas? No, pero habrá que decidir qué es más importante. O en casos como el de este ejemplo, quizá no tendremos opción si la solución actual afecta al rendimiento.</p>
<hr />
<h2>Conversión de Bytes</h2>
<p>En el ejemplo de suma de números recibimos un error cuando se agotó la memoria disponible. La cantidad de memoria se mostraba en bytes. Esto nos lleva a nuestro segundo ejemplo. Si 1024 bytes equivale a 1 Kilobyte, 1024 Kilobytes equivale a 1 Megabyte, y así sucesivamente, ¿cómo haríamos una función que convierta de bytes a su equivalente?</p>
<p>La solución más lógica quizá, consiste en determinar el rango de valores de la cantidad de bytes recibida y convertirla:</p>
<pre><code>function convertBytes($bytes) {
    if($bytes == 0) {
        return '0 Bytes';
    } else if($bytes &gt; 0 &amp;&amp; $bytes &lt; 1024) {
        return $bytes . ' Bytes';
    } else if($bytes &gt; 1024 &amp;&amp; $bytes &lt; 1048576) {
        return ($bytes / 1024) . ' KB';
    } else if($bytes &gt; 1048576 &amp;&amp; $bytes &lt; 1073741824) {
        return ($bytes / 1048576) . ' MB';
    } else if($bytes &gt; 1073741824) {
        return ($bytes / 1073741824) . ' GB';
    }
}</code></pre>
<p>Esta solución funciona pero podemos ver que es bastante desordenada y contiene varios <span class="post-tooltip" title="valor en el código cuyo propósito no es claro a simple vista">números mágicos</span>, y eso que solamente maneja conversiones hasta el rango de los Gigabytes. Pero al igual que en el ejemplo de suma de números, existe una fórmula que podemos usar para conversión de bytes:</p>
<pre><code>function convertBytes($bytes) {

    if($bytes == 0) {
        return '0 Byte';
    }

    $k = 1024;
    $sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];

    $i = floor(log($bytes) / log($k));
    return round($bytes / pow($k, $i), 2) . ' ' . $sizes[$i];

}</code></pre>
<p>Esta solución utiliza las funciones <code>log()</code> (logaritmo natural) y <code>pow()</code> (elevar a una potencia) para lograr su cometido. La fórmula,  a grandes rasgos,  funciona de la siguiente manera: la parte que utiliza logaritmo natural sirve para determinar a qué rango o potencia pertenece la cantidad de bytes proporcionada. Después hay que dividir la cantidad de bytes entre el número de bytes que representa una unidad de la potencia a la que pertenece. Por ejemplo, si sabemos que pertenece al rango de los Kilobytes, entonces hay que dividir entre 1024, ya que 1024 bytes equivale a 1 Kilobyte. La función <code>pow()</code> se usa para elevar la base ($k) a la potencia calculada ($i) y así poder realizar la división en términos de bytes.</p>
<p>Como se puede ver, la solución es más corta, pero quizá no tan fácil de entender como la solución que utiliza la serie de condiciones IF. La ventaja en este caso es que es más fácil agregar nuevas potencias al arreglo <code>$sizes</code>, siempre y cuando se agreguen en el orden correcto. En el caso de la solución basada en condiciones, hay que conocer los valores en bytes de cada potencia para poder realizar los cálculos correctamente.</p>
<hr />
<p>¿Cuál es el punto? ¿Hay que buscar fórmulas para todo?</p>
<p>Como dije antes, no hay que buscar la fórmula o solución perfecta. Hay que buscar alternativas y decidir qué consideramos &quot;suficientemente bueno&quot;. Si buscamos diferentes ángulos a un problema o vemos cómo lo han resuelto otras personas, es muy probable que encontremos una mejor manera. </p>]]></description>
      <content:encoded><![CDATA[<p>En ocasiones al estar programando algo, la intuición nos dice que básicamente traemos un desmadre. No sabemos con exactitud cómo encontrar una solución más elegante y pensamos: <em>seguro hay una mejor manera</em>.</p>
<p>Esto no es del todo paranóico o perfeccionista. No siempre es posible encontrar la forma más rápida o creativa de programar algo, pero a veces, si investigamos un poco, podemos toparnos con un ángulo distinto.</p>
<p>Pongamos dos ejemplos; ambos sencillos. El punto aquí es ver cómo resolveríamos cada uno.</p>
<hr />
<h2>Suma de números consecutivos</h2>
<p>Primer ejemplo: obtener la suma de números enteros del 1 a n. Bastante sencillo. Podemos hacer un ciclo e ir acumulando la sumatoria:</p>
<pre><code>$suma = 0;

for($c=1; $c&lt;= 100; $c++) {
    $suma += $c;
}

echo $suma; // imprime 5050</code></pre>
<p>Funciona: Imprime el resultado correcto. Pero no sé, me parece innecesario solamente para sumar una serie de valores. Una alternativa es evitar utilizar un ciclo y aprovechar las funciones de PHP. En ese caso podemos utilizar <code>array_sum()</code>, pero esta función requiere de un arreglo. Bueno, para eso podemos utilizar otra función del lenguaje: <code>range()</code>, que genera un arreglo con los valores mínimo y máximo indicados. Esta solución quedaría de la siguiente manera:</p>
<pre><code>echo array_sum(range(1, 100)); // imprime 5050</code></pre>
<p>El mismo resultado, en una sola línea. Aunque, claro, esto no se trata de reducir el número de líneas. El código es más corto que el anterior, pero aunque hemos delegado al lenguaje el trabajo de sumatoria, aún así generamos un arreglo al utilizar <code>range()</code>. </p>
<p>Cómo mencionaba antes, no siempre será posible encontrar una solución perfecta. En programación siempre  vamos a tener que decidir que es más importante para la aplicación: que se ejecute con rapidez, que el código sea entendible... entre otras decisiones.</p>
<p>Para este ejemplo en particular, existe una fórmula que permite sumar la serie de números. Esta fórmula se llama <em>Suma de Gauss</em>. La forma simplificada de la fórmula es la siguiente:</p>
<p>n (n +1)  / 2</p>
<p>Utilicémosla en un ejemplo de código:</p>
<pre><code>$n = 100;

$sum = (($n + 1) * $n) / 2;

echo $sum; //imprime 5050</code></pre>
<blockquote>
<p><em>Ok, ok. Esta solución también es correcta, pero la solución anterior con <code>array_sum()</code> y <code>range()</code> sigue siendo más corta y entendible.</em></p>
</blockquote>
<p>De acuerdo, pero no tan rápido, amigos. Resulta que la solución que usa <code>range()</code> tiene un &quot;pequeño&quot; problema. Crear un arreglo con valores del 1 al 100 es asunto trivial, pero si el valor superior es muy grande, ahí es cuando todo se viene abajo. </p>
<p>Supongamos que deseamos obtener la suma de números del 1 al 9223399 utilizando la solución con <code>range()</code>:</p>
<pre><code>echo array_sum(range(1, 9223399));</code></pre>
<p>Si ejecutamos el código es posible que obtengamos el siguiente mensaje de error:</p>
<pre><code>Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 536870920 bytes)</code></pre>
<p>¡Ups, parece que se agotó la memoria disponible para PHP! ¡Por sumar un rango de números! En este caso la memoria disponible es de 134217728 (128 Mb) y nuestro código requiere  536870920 bytes (512 Mb). Así es, 512 Megabytes para sumar una secuencia de números.</p>
<p>Ahora probemos obtener la suma del mismo rango pero ahora utilizando la fórmula de Gauss:</p>
<pre><code>$n = 9223399;

$sum = (($n + 1) * $n) / 2;

echo $sum; // imprime 42535549168300</code></pre>
<p>Funciona, y no requiere 512 Mb de memoria.</p>
<p>Entonces, ¿siempre hay que buscar fórmulas? No, pero habrá que decidir qué es más importante. O en casos como el de este ejemplo, quizá no tendremos opción si la solución actual afecta al rendimiento.</p>
<hr />
<h2>Conversión de Bytes</h2>
<p>En el ejemplo de suma de números recibimos un error cuando se agotó la memoria disponible. La cantidad de memoria se mostraba en bytes. Esto nos lleva a nuestro segundo ejemplo. Si 1024 bytes equivale a 1 Kilobyte, 1024 Kilobytes equivale a 1 Megabyte, y así sucesivamente, ¿cómo haríamos una función que convierta de bytes a su equivalente?</p>
<p>La solución más lógica quizá, consiste en determinar el rango de valores de la cantidad de bytes recibida y convertirla:</p>
<pre><code>function convertBytes($bytes) {
    if($bytes == 0) {
        return '0 Bytes';
    } else if($bytes &gt; 0 &amp;&amp; $bytes &lt; 1024) {
        return $bytes . ' Bytes';
    } else if($bytes &gt; 1024 &amp;&amp; $bytes &lt; 1048576) {
        return ($bytes / 1024) . ' KB';
    } else if($bytes &gt; 1048576 &amp;&amp; $bytes &lt; 1073741824) {
        return ($bytes / 1048576) . ' MB';
    } else if($bytes &gt; 1073741824) {
        return ($bytes / 1073741824) . ' GB';
    }
}</code></pre>
<p>Esta solución funciona pero podemos ver que es bastante desordenada y contiene varios <span class="post-tooltip" title="valor en el código cuyo propósito no es claro a simple vista">números mágicos</span>, y eso que solamente maneja conversiones hasta el rango de los Gigabytes. Pero al igual que en el ejemplo de suma de números, existe una fórmula que podemos usar para conversión de bytes:</p>
<pre><code>function convertBytes($bytes) {

    if($bytes == 0) {
        return '0 Byte';
    }

    $k = 1024;
    $sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];

    $i = floor(log($bytes) / log($k));
    return round($bytes / pow($k, $i), 2) . ' ' . $sizes[$i];

}</code></pre>
<p>Esta solución utiliza las funciones <code>log()</code> (logaritmo natural) y <code>pow()</code> (elevar a una potencia) para lograr su cometido. La fórmula,  a grandes rasgos,  funciona de la siguiente manera: la parte que utiliza logaritmo natural sirve para determinar a qué rango o potencia pertenece la cantidad de bytes proporcionada. Después hay que dividir la cantidad de bytes entre el número de bytes que representa una unidad de la potencia a la que pertenece. Por ejemplo, si sabemos que pertenece al rango de los Kilobytes, entonces hay que dividir entre 1024, ya que 1024 bytes equivale a 1 Kilobyte. La función <code>pow()</code> se usa para elevar la base ($k) a la potencia calculada ($i) y así poder realizar la división en términos de bytes.</p>
<p>Como se puede ver, la solución es más corta, pero quizá no tan fácil de entender como la solución que utiliza la serie de condiciones IF. La ventaja en este caso es que es más fácil agregar nuevas potencias al arreglo <code>$sizes</code>, siempre y cuando se agreguen en el orden correcto. En el caso de la solución basada en condiciones, hay que conocer los valores en bytes de cada potencia para poder realizar los cálculos correctamente.</p>
<hr />
<p>¿Cuál es el punto? ¿Hay que buscar fórmulas para todo?</p>
<p>Como dije antes, no hay que buscar la fórmula o solución perfecta. Hay que buscar alternativas y decidir qué consideramos &quot;suficientemente bueno&quot;. Si buscamos diferentes ángulos a un problema o vemos cómo lo han resuelto otras personas, es muy probable que encontremos una mejor manera. </p>]]></content:encoded>
      <guid>https://jborbon.me/siempre-hay-una-mejor-manera</guid>
      <pubDate>Mon, 13 Jun 2016 13:04:27 -0500</pubDate>
    </item>
    <item>
      <title><![CDATA[¿Por qué debes conocer tus herramientas?]]></title>
      <link>https://jborbon.me/por-que-debes-conocer-tus-herramientas</link>
      <description><![CDATA[<p>Cuando apenas comienzas a involucrarte en las artes oscuras de la programación, normalmente te conformas con que las cosas funcionen. Pero conforme vas ganando experiencia y aprendes de tus errores, mejoras como programador y tu código es la evidencia... bueno, no en todos los casos.</p>
<blockquote>
<p><em>Pero, pero... eso es muy idealista. ¿Y los <strong>deadlines</strong> qué?  Mejor código funcional que código perfecto que nunca ve la luz del día.</em></p>
</blockquote>
<p>Si bien es cierto que no hay que esperar a dominar un lenguaje para crear aplicaciones con él, debo decir que esto no justifica el ser un desarrollador mediocre. No se trata de ser una documentación viviente y saber de memoria las funciones y el órden de sus parámetros. Aún así, hay que invertir tiempo aprendiendo los fundamentos. </p>
<p>¿A qué viene todo esto? ¡Pensé que nunca lo preguntarían! No, en serio, pensé que nunca lo preguntarían... </p>
<hr />
<p>La siguiente imagen es un ejemplo perfecto de lo que hablo. ¿Lo mejor de todo? Es un fragmento de código real, que existe en una aplicación PHP real.</p>
<p><img src="//i.imgur.com/KT0QeBz.png" class="img-responsive img-thumbnail center-block" /></p>
<p>¿Qué hay de malo en este código? Bueno, empecemos por el hecho de que es bastante confuso para lograr algo muy trivial: buscar si una categoría (<code>$category</code>) existe dentro de un array llamado <code>$catComponents</code>. Nada grave a primera vista; algo que se puede simplificar utilizando alguna función para arrays como <code>array_key_exists()</code>.</p>
<p>Luego, nada como un poco de redundancia para darle sabor a nuestro código. La instrucción <code>else</code> y <code>continue</code> son innecesarias ya que no hay nada más además de la condición <code>if</code>. Si esta no se cumple, el código simplemente continuará con la siguiente iteración del ciclo.</p>
<p>Ahora vayamos un poco hacia arriba para analizar la condición <code>if</code>. Vemos que hay un <code>preg_match()</code> que sirve para comparar una cadena de texto utilizando expresiones regulares, pero se puede apreciar que no hay una expresión regular como tal, simplemente se compara que el valor de <code>$component</code> sea igual al de <code>$category</code>. Para una comparación como esta bastaría con utilizar alguna función como <code>strcmp()</code>. </p>
<p>Algo que también es importante mencionar es que la función <code>preg_match()</code> es una de tantas funciones de PHP que —para bien o mal— devuelven un tipo de dato según sea el caso: un valor entero <code>1</code> si existe alguna coincidencia; un valor entero <code>0</code> si no existe coincidencia alguna y <code>false</code> si ocurre un error (por ejemplo si los caracteres para delimitar la expresión regular también forman parte de ella y no han sido escapados). </p>
<p>En el código de la imagen se comete un error muy común al utilizar este tipo de funciones: olvidar o ignorar que PHP es un lenguaje dinámico, por lo que realizará una conversión implícita. En este caso se utiliza dentro de una condición <code>if</code>, la cual espera expresiones tipo <code>boolean</code>, pero de no encontrar valores de este tipo los convertirá a su equivalente. Si <code>preg_match()</code> encuentra una coincidencia devolverá <code>1</code>, el cual será convertido a <code>true</code>; si no hay coincidencias devolverá <code>0</code>, el cual será convertido a <code>false</code>. Entonces, ¿cuál es el problema? Si ocurre un error la función regresará <code>false</code>, lo cual puede interpretarse como que simplemente no hubo alguna coincidencia.</p>
<p>Pongamos un sencillo ejemplo. En el siguiente fragmento de código comparamos si el valor de <code>$currentPage</code> coincide con el valor <code>about</code>. Los valores son los mismos (hay que recordar que cuando usamos expresiones regulares, los caracteres al inicio y final no forman parte del valor a comparar sino que son los delimitadores de la expresión regular), así que se imprimirá el mensaje <em>existen coincidencias</em>.</p>
<pre><code>// imprime: existen coincidencias
$currentPage = 'about';

if(preg_match('/about/', $currentPage)) {
    echo 'existen coincidencias';
} else {
    echo 'no existen coincidencias';
}</code></pre>
<p>Ahora modifiquemos ligeramente el código y agreguemos una diagonal al principio del valor a comparar, tanto en <code>$currentPage</code> como en <code>preg_match()</code> para que siga existiendo una coincidencia:</p>
<pre><code>$currentPage = '/about';

if(preg_match('//about/', $currentPage)) {
    echo 'existen coincidencias';
} else {
    echo 'no existen coincidencias';
}</code></pre>
<p>Pero si ejecutamos el código se imprime el mensaje <em>no existen coincidencias</em>.</p>
<p>Lo que sucede es que estamos incluyendo la diagonal como delimitador de la expresión regular y como parte de ella. Esto genera un error y hace que <code>preg_match()</code> devuelva <code>false</code> por lo que se ejecuta la instrucción <code>else</code>, la cual también se ejecuta cuando se devuelve <code>0</code> (el cual es convertido implícitamente a <code>false</code>). Cambiemos el código para que haga comparaciones estrictas e imprima el mensaje correcto.</p>
<pre><code>$currentPage = '/about';

$matches = preg_match('//about/', $currentPage);

if($matches === 1) {
    echo 'existen coincidencias';
} elseif ($matches === 0) {
    echo 'no existen coincidencias';
} else {
    echo 'ocurrió un error';
}</code></pre>
<p>Ahora el código imprimirá el mensaje <em>ocurrió un error</em>.</p>
<blockquote>
<p><em>WTF, ¿todo un rant por un bloque de código?</em></p>
</blockquote>
<p>Todavía no llego a mi parte favorita...</p>
<p><img src="//i.imgur.com/Zn9aCjS.jpg" class="img-responsive img-thumbnail center-block" /></p>
<p class="text-center"><i>¡Aún hay más!</i></p>
<p>Quizá la parte que más llamó mi atención fue el uso del operador módulo <code>%</code> en la condición <code>if</code>. Me pregunto para que lo utiliza. Si la condición se cumple, entonces usa una variable llamada <code>$explodeOver</code> y le asigna un elemento del array; en este caso, el elemento que coincide con el índice actual de la iteración más uno.</p>
<p>Tiene que ser una broma... Al parecer usa los índices númericos 0, 2, 4, ... (números divisibles entre 2) para almacenar los &quot;índices&quot; del array, y los índices numéricos 1, 3, 5, ... (números no divisibles entre 2) para almacenar los valores. A esto me refiero cuando digo que debemos conocer por lo menos los fundamentos de un lenguaje. Entre algunas otras razones, para evitar crear código innecesariamente complejo.</p>
<p>El código anterior pudiera haber utilizado el array de categorías de la siguiente manera:</p>
<pre><code>$catComponents = array(
    'article' =&gt; 'blog/articles',
    'tutorial' =&gt; 'techblog/tutorials'
);</code></pre>
<p>Pero al ignorar el uso de los arrays asociativos, tenemos algo así:</p>
<pre><code>$catComponents = array(
    'article',
    'blog/articles',
    'tutorial',
    'techblog/tutorials'
);</code></pre>
<p>Usando un array asociativo es obvia la relación índice - valor; en el segundo caso hay que saber cómo funciona el código para entender dicha relación entre los valores.</p>
<p>Una forma simplificada de lograr lo anterior sería la siguiente. Por ejemplo, definir una función que busque en el array en base al índice y devuelva el valor correspondiente:</p>
<pre><code>function findCategoryURI($category, $categories) {

    if(array_key_exists($category, $categories)){
        return $categories[$category];
    }

    return null;
}</code></pre>
<p>Y un ejemplo de uso sería algo como lo siguiente:</p>
<pre><code>$catComponents = array(
    'article' =&gt; 'blog/articles',
    'tutorial' =&gt; 'techblog/tutorials'
);

$categoryURI = findCategoryURI('article', $catComponents);

if(is_null($categoryURI)) {
    echo 'categoria no existente';
} else {
    echo "la URI de la categoria es $categoryURI";
}</code></pre>
<p>El objetivo es el mismo: buscar un elemento en un array, pero con algunas diferencias. Al usar un array asociativo no tenemos que recurrir a técnicas de <em>vudú</em> como el uso del operador módulo; tampoco es necesario un ciclo ni condiciones, pues simplemente usamos una función para arrays disponible en el lenguaje: <code>array_key_exists()</code>. El resultado: código más simple y claro.</p>
<p>Y hablando de <em>vudú</em>, tengo otro ejemplo de código muy interesante...</p>
<hr />
<h2>Mejor usa un <em>left join</em>, patiño</h2>
<p>A diferencia del código anterior, no tengo una captura de pantalla del siguiente ofensor. Lo bueno (?) es que recuerdo lo suficiente para recrearlo en PHP (el código estaba en Perl, que es casi lo mismo, pero no). </p>
<p>Se trataba de una aplicación para hacer exámenes o <em>quizzes</em>.</p>
<p>Utilizando una estructura simplificada de tablas, supondremos lo siguiente:</p>
<ul>
<li>Hay una tabla de quizzes</li>
<li>Hay una tabla de preguntas. Cada pregunta sólo puede ser asociada con un quiz</li>
<li>Hay una tabla de respuestas. Las respuestas son abiertas. Cada respuesta está asociada con una pregunta. Hay respuestas que pueden estar en blanco.</li>
</ul>
<p>Hay más información que debería ser incluída, como otra tabla donde almacenar las respuestas de un usuario para cada pregunta, pero esto lo dejaremos fuera pues con las tablas siguientes espero poder explicar  el problema del código.</p>
<p><strong>Tabla de quizzes</strong></p>
<table>
<thead>
<tr>
<th style="text-align: center;">id</th>
<th style="text-align: center;">title</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">4323</td>
<td style="text-align: center;">Quiz de prueba</td>
</tr>
</tbody>
</table>
<p><strong>Tabla de preguntas</strong></p>
<table>
<thead>
<tr>
<th style="text-align: center;">id</th>
<th style="text-align: center;">quiz_id</th>
<th style="text-align: center;">title</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">4533</td>
<td style="text-align: center;">4323</td>
<td style="text-align: center;">ejemplo de pregunta</td>
</tr>
<tr>
<td style="text-align: center;">4534</td>
<td style="text-align: center;">4323</td>
<td style="text-align: center;">otro ejemplo de pregunta</td>
</tr>
</tbody>
</table>
<p><strong>Tabla de respuestas </strong></p>
<table>
<thead>
<tr>
<th style="text-align: center;">id</th>
<th style="text-align: center;">question_id</th>
<th style="text-align: center;">answer</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">467</td>
<td style="text-align: center;">4533</td>
<td style="text-align: center;">respuesta para la pregunta 4533</td>
</tr>
<tr>
<td style="text-align: center;">524</td>
<td style="text-align: center;">3211</td>
<td style="text-align: center;">respuesta para pregunta XXXX</td>
</tr>
<tr>
<td style="text-align: center;">348</td>
<td style="text-align: center;">1125</td>
<td style="text-align: center;">respuesta para la pregunta XXXX</td>
</tr>
</tbody>
</table>
<p>Las tablas muestran un quiz con dos preguntas asociadas. Y en la tabla de respuestas solo hay una respuesta asociada a este quiz: la que tiene el <strong><em>id</em></strong> <strong>467</strong>, correspondiente a la pregunta <strong>4533</strong>. Los registros con <strong><em>id</em></strong> <strong>524</strong> y <strong>348</strong> pertenecen a preguntas de otros quizzes, y la pregunta con <strong><em>id</em></strong> <strong>4534</strong> no tiene respuesta.</p>
<p>La aplicación en cuestión tenía una sección para mostrar un quiz que se deseara responder. Primero tomaba el <strong><em>id</em></strong> del quiz y en base a esto buscaba las preguntas y respuestas asociadas. No voy a mostrar código de conexión a base de datos ni nada por el estilo, únicamente mostraré arrays que simulen datos obtenidos de una base de datos.</p>
<p>Entonces, el código encargado de esta tarea hacía algo como lo siguiente:</p>
<pre><code>&lt;?php
// este array se obtenía realizando una consulta a la tabla 
// de quizzes, en base al quiz_id
$quizQuestions = [

    [
        'id'      =&gt; 4533,
        'quiz_id' =&gt; 4323,
        'title'   =&gt; 'ejemplo de pregunta'
    ],

    [
        'id'      =&gt; 4534,
        'quiz_id' =&gt; 4323,
        'title'   =&gt; 'otro ejemplo de pregunta'
    ]

];

// este array era una consulta que obtenía todos los registros
// de la tabla de respuestas. Sí, todos los registros
$answers = [

    [
        'id'          =&gt; 467,
        'question_id' =&gt; 4533,
        'answer'       =&gt; 'respuesta para la pregunta 4533'
    ],

    [
        'id'          =&gt; 524,
        'question_id' =&gt; 3211,
        'answer'       =&gt; 'respuesta para pregunta XXXX'
    ],

    [
        'id'          =&gt; 348,
        'question_id' =&gt; 1125,
        'answer'       =&gt; 'respuesta para pregunta XXXX'
    ]

];

// Se imprimen las preguntas y respuestas del quizz
// En la aplicación esto era un formulario con campos de texto
foreach($quizQuestions as $question) {
    echo $question['title'] . ' - respuesta: ';

    foreach($answers as $answer) {
        if($answer['question_id'] == $question['id']) {
            echo $answer['answer'];
        }
    }

    echo PHP_EOL;
}</code></pre>
<p>Veamos. El código primero hace una consulta a la tabla de preguntas y obtiene aquellas correspondientes al quiz que desea mostrar; el resultado de la consulta se almacena en un array. Nada inusual, pero es a partir de aquí cuando todo comienza a desmoronarse.</p>
<p>Luego tenemos otro array que almacena todas las respuestas habidas y por haber. Cero discriminación, ¡todas las respuestas son bienvenidas!</p>
<p>Luego tenemos un ciclo <code>foreach</code> que recorre el array de preguntas del quiz y muestra el título de la pregunta. Después... qué tenemos aquí, ¡un ciclo anidado! Este segundo ciclo, por cada pregunta, recorre TODO el array de respuestas (que ya de por si contiene TODOS los registros de la tabla de respuestas) comparando el campo <strong><em>id</em></strong> de la pregunta con el índice <strong><em>id_question</em></strong> de la respuesta para determinar si la pregunta de la iteración actual tiene una respuesta almacenada, y de ser así, muestra la respuesta.</p>
<p>A veces creo que alguien le dijo a la persona o personas encargadas de este código: </p>
<blockquote>
<p><em>Necesito que muestren las preguntas y respuestas de un quiz, pero quiero que sea complejo e ineficiente. Sean creativos, den rienda suelta a su imaginación. ¡Azoten las teclas como si no hubiera un mañana, hijos!</em></p>
</blockquote>
<p>Creo que jamás sabré las razones verdaderas, pero mi teoría es la siguiente. Nuestros amigos se dieron cuenta que al relacionar dos tablas con el tipo de join más común del mundo (inner join) se muestran los registros de ambas <strong>únicamente</strong> si hay registros correspondientes. Esto les presentó un problema: <strong><em>cuando existieran preguntas sin responder, no existiría un registro en la tabla de respuestas para dicha pregunta</em></strong>. ¿Resultado? La información de la pregunta no se mostraría.</p>
<p>Había que encontrar la manera de mostrar el título de cada pregunta y su respuesta (en caso de haberla). Encontraron la manera, aunque no la mejor.</p>
<p>Si en lugar de eso se hubiera utilizado un <strong> LEFT JOIN</strong>, más que simplificar el código, se hubiera logrado algo más eficiente, que no tuviera que obtener la tabla completa de respuestas y recorrer dicho array por cada pregunta.</p>
<p>El siguiente código muestra un ejemplo de cómo puede haber sido una alternativa. Las preguntas sin respuesta correspondiente devuelven un valor <code>null</code> los campos seleccionados, por lo que simplemente hay que verificar si el valor es distinto a <code>null</code> para desplegar la respuesta.</p>
<pre><code>/*
Internamente, la consulta pudo haber sido algo como lo siguiente:

SELECT q.id as question_id, q.title as question, a.answer as answer
  FROM questions q LEFT JOIN answers a
    ON q.id = a.question_id
 WHERE q.quiz_id = 4323;

 Lo que arrojaría los resultados como los de $questionData
 */

$questionData = [

    [
        'question_id' =&gt; 4533,
        'question'    =&gt; 'ejemplo de pregunta',
        'answer'      =&gt; 'respuesta para la pregunta 4533'
    ],

    [
        'question_id' =&gt; 4534,
        'question'    =&gt; 'otro ejemplo de pregunta',
        'answer'      =&gt; null
    ]

];

foreach($questionData as $data) {

    echo $data['question'];

    if(! is_null($data['answer'])) {
        echo  ' - respuesta: ' . $data['answer'];
    }

    echo PHP_EOL;

}</code></pre>
<p>El resultado sería algo así:</p>
<pre><code>ejemplo de pregunta - respuesta: respuesta para la pregunta 4533
otro ejemplo de pregunta</code></pre>
<hr />
<p>Moraleja: nunca se esfuercen.</p>
<p>Ya, en serio. Si quiero poner una especie de conclusión es la siguiente: </p>
<p>No hay que paralizarse buscando o esperando a tener la solución perfecta para un problema. Pero siempre hay que buscar la forma de mejorar cómo hacemos las cosas. Conocer las herramientas que te ofrece un lenguaje permite resolver problemas de manera más efectiva y en muchos casos, en una fracción del tiempo. </p>
<p>Haciendo referencia a una historia popular que tal vez ya conozcan: el mérito no está en ajustar un tornillo sino saber cuál ajustar.</p>]]></description>
      <content:encoded><![CDATA[<p>Cuando apenas comienzas a involucrarte en las artes oscuras de la programación, normalmente te conformas con que las cosas funcionen. Pero conforme vas ganando experiencia y aprendes de tus errores, mejoras como programador y tu código es la evidencia... bueno, no en todos los casos.</p>
<blockquote>
<p><em>Pero, pero... eso es muy idealista. ¿Y los <strong>deadlines</strong> qué?  Mejor código funcional que código perfecto que nunca ve la luz del día.</em></p>
</blockquote>
<p>Si bien es cierto que no hay que esperar a dominar un lenguaje para crear aplicaciones con él, debo decir que esto no justifica el ser un desarrollador mediocre. No se trata de ser una documentación viviente y saber de memoria las funciones y el órden de sus parámetros. Aún así, hay que invertir tiempo aprendiendo los fundamentos. </p>
<p>¿A qué viene todo esto? ¡Pensé que nunca lo preguntarían! No, en serio, pensé que nunca lo preguntarían... </p>
<hr />
<p>La siguiente imagen es un ejemplo perfecto de lo que hablo. ¿Lo mejor de todo? Es un fragmento de código real, que existe en una aplicación PHP real.</p>
<p><img src="//i.imgur.com/KT0QeBz.png" class="img-responsive img-thumbnail center-block" /></p>
<p>¿Qué hay de malo en este código? Bueno, empecemos por el hecho de que es bastante confuso para lograr algo muy trivial: buscar si una categoría (<code>$category</code>) existe dentro de un array llamado <code>$catComponents</code>. Nada grave a primera vista; algo que se puede simplificar utilizando alguna función para arrays como <code>array_key_exists()</code>.</p>
<p>Luego, nada como un poco de redundancia para darle sabor a nuestro código. La instrucción <code>else</code> y <code>continue</code> son innecesarias ya que no hay nada más además de la condición <code>if</code>. Si esta no se cumple, el código simplemente continuará con la siguiente iteración del ciclo.</p>
<p>Ahora vayamos un poco hacia arriba para analizar la condición <code>if</code>. Vemos que hay un <code>preg_match()</code> que sirve para comparar una cadena de texto utilizando expresiones regulares, pero se puede apreciar que no hay una expresión regular como tal, simplemente se compara que el valor de <code>$component</code> sea igual al de <code>$category</code>. Para una comparación como esta bastaría con utilizar alguna función como <code>strcmp()</code>. </p>
<p>Algo que también es importante mencionar es que la función <code>preg_match()</code> es una de tantas funciones de PHP que —para bien o mal— devuelven un tipo de dato según sea el caso: un valor entero <code>1</code> si existe alguna coincidencia; un valor entero <code>0</code> si no existe coincidencia alguna y <code>false</code> si ocurre un error (por ejemplo si los caracteres para delimitar la expresión regular también forman parte de ella y no han sido escapados). </p>
<p>En el código de la imagen se comete un error muy común al utilizar este tipo de funciones: olvidar o ignorar que PHP es un lenguaje dinámico, por lo que realizará una conversión implícita. En este caso se utiliza dentro de una condición <code>if</code>, la cual espera expresiones tipo <code>boolean</code>, pero de no encontrar valores de este tipo los convertirá a su equivalente. Si <code>preg_match()</code> encuentra una coincidencia devolverá <code>1</code>, el cual será convertido a <code>true</code>; si no hay coincidencias devolverá <code>0</code>, el cual será convertido a <code>false</code>. Entonces, ¿cuál es el problema? Si ocurre un error la función regresará <code>false</code>, lo cual puede interpretarse como que simplemente no hubo alguna coincidencia.</p>
<p>Pongamos un sencillo ejemplo. En el siguiente fragmento de código comparamos si el valor de <code>$currentPage</code> coincide con el valor <code>about</code>. Los valores son los mismos (hay que recordar que cuando usamos expresiones regulares, los caracteres al inicio y final no forman parte del valor a comparar sino que son los delimitadores de la expresión regular), así que se imprimirá el mensaje <em>existen coincidencias</em>.</p>
<pre><code>// imprime: existen coincidencias
$currentPage = 'about';

if(preg_match('/about/', $currentPage)) {
    echo 'existen coincidencias';
} else {
    echo 'no existen coincidencias';
}</code></pre>
<p>Ahora modifiquemos ligeramente el código y agreguemos una diagonal al principio del valor a comparar, tanto en <code>$currentPage</code> como en <code>preg_match()</code> para que siga existiendo una coincidencia:</p>
<pre><code>$currentPage = '/about';

if(preg_match('//about/', $currentPage)) {
    echo 'existen coincidencias';
} else {
    echo 'no existen coincidencias';
}</code></pre>
<p>Pero si ejecutamos el código se imprime el mensaje <em>no existen coincidencias</em>.</p>
<p>Lo que sucede es que estamos incluyendo la diagonal como delimitador de la expresión regular y como parte de ella. Esto genera un error y hace que <code>preg_match()</code> devuelva <code>false</code> por lo que se ejecuta la instrucción <code>else</code>, la cual también se ejecuta cuando se devuelve <code>0</code> (el cual es convertido implícitamente a <code>false</code>). Cambiemos el código para que haga comparaciones estrictas e imprima el mensaje correcto.</p>
<pre><code>$currentPage = '/about';

$matches = preg_match('//about/', $currentPage);

if($matches === 1) {
    echo 'existen coincidencias';
} elseif ($matches === 0) {
    echo 'no existen coincidencias';
} else {
    echo 'ocurrió un error';
}</code></pre>
<p>Ahora el código imprimirá el mensaje <em>ocurrió un error</em>.</p>
<blockquote>
<p><em>WTF, ¿todo un rant por un bloque de código?</em></p>
</blockquote>
<p>Todavía no llego a mi parte favorita...</p>
<p><img src="//i.imgur.com/Zn9aCjS.jpg" class="img-responsive img-thumbnail center-block" /></p>
<p class="text-center"><i>¡Aún hay más!</i></p>
<p>Quizá la parte que más llamó mi atención fue el uso del operador módulo <code>%</code> en la condición <code>if</code>. Me pregunto para que lo utiliza. Si la condición se cumple, entonces usa una variable llamada <code>$explodeOver</code> y le asigna un elemento del array; en este caso, el elemento que coincide con el índice actual de la iteración más uno.</p>
<p>Tiene que ser una broma... Al parecer usa los índices númericos 0, 2, 4, ... (números divisibles entre 2) para almacenar los &quot;índices&quot; del array, y los índices numéricos 1, 3, 5, ... (números no divisibles entre 2) para almacenar los valores. A esto me refiero cuando digo que debemos conocer por lo menos los fundamentos de un lenguaje. Entre algunas otras razones, para evitar crear código innecesariamente complejo.</p>
<p>El código anterior pudiera haber utilizado el array de categorías de la siguiente manera:</p>
<pre><code>$catComponents = array(
    'article' =&gt; 'blog/articles',
    'tutorial' =&gt; 'techblog/tutorials'
);</code></pre>
<p>Pero al ignorar el uso de los arrays asociativos, tenemos algo así:</p>
<pre><code>$catComponents = array(
    'article',
    'blog/articles',
    'tutorial',
    'techblog/tutorials'
);</code></pre>
<p>Usando un array asociativo es obvia la relación índice - valor; en el segundo caso hay que saber cómo funciona el código para entender dicha relación entre los valores.</p>
<p>Una forma simplificada de lograr lo anterior sería la siguiente. Por ejemplo, definir una función que busque en el array en base al índice y devuelva el valor correspondiente:</p>
<pre><code>function findCategoryURI($category, $categories) {

    if(array_key_exists($category, $categories)){
        return $categories[$category];
    }

    return null;
}</code></pre>
<p>Y un ejemplo de uso sería algo como lo siguiente:</p>
<pre><code>$catComponents = array(
    'article' =&gt; 'blog/articles',
    'tutorial' =&gt; 'techblog/tutorials'
);

$categoryURI = findCategoryURI('article', $catComponents);

if(is_null($categoryURI)) {
    echo 'categoria no existente';
} else {
    echo "la URI de la categoria es $categoryURI";
}</code></pre>
<p>El objetivo es el mismo: buscar un elemento en un array, pero con algunas diferencias. Al usar un array asociativo no tenemos que recurrir a técnicas de <em>vudú</em> como el uso del operador módulo; tampoco es necesario un ciclo ni condiciones, pues simplemente usamos una función para arrays disponible en el lenguaje: <code>array_key_exists()</code>. El resultado: código más simple y claro.</p>
<p>Y hablando de <em>vudú</em>, tengo otro ejemplo de código muy interesante...</p>
<hr />
<h2>Mejor usa un <em>left join</em>, patiño</h2>
<p>A diferencia del código anterior, no tengo una captura de pantalla del siguiente ofensor. Lo bueno (?) es que recuerdo lo suficiente para recrearlo en PHP (el código estaba en Perl, que es casi lo mismo, pero no). </p>
<p>Se trataba de una aplicación para hacer exámenes o <em>quizzes</em>.</p>
<p>Utilizando una estructura simplificada de tablas, supondremos lo siguiente:</p>
<ul>
<li>Hay una tabla de quizzes</li>
<li>Hay una tabla de preguntas. Cada pregunta sólo puede ser asociada con un quiz</li>
<li>Hay una tabla de respuestas. Las respuestas son abiertas. Cada respuesta está asociada con una pregunta. Hay respuestas que pueden estar en blanco.</li>
</ul>
<p>Hay más información que debería ser incluída, como otra tabla donde almacenar las respuestas de un usuario para cada pregunta, pero esto lo dejaremos fuera pues con las tablas siguientes espero poder explicar  el problema del código.</p>
<p><strong>Tabla de quizzes</strong></p>
<table>
<thead>
<tr>
<th style="text-align: center;">id</th>
<th style="text-align: center;">title</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">4323</td>
<td style="text-align: center;">Quiz de prueba</td>
</tr>
</tbody>
</table>
<p><strong>Tabla de preguntas</strong></p>
<table>
<thead>
<tr>
<th style="text-align: center;">id</th>
<th style="text-align: center;">quiz_id</th>
<th style="text-align: center;">title</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">4533</td>
<td style="text-align: center;">4323</td>
<td style="text-align: center;">ejemplo de pregunta</td>
</tr>
<tr>
<td style="text-align: center;">4534</td>
<td style="text-align: center;">4323</td>
<td style="text-align: center;">otro ejemplo de pregunta</td>
</tr>
</tbody>
</table>
<p><strong>Tabla de respuestas </strong></p>
<table>
<thead>
<tr>
<th style="text-align: center;">id</th>
<th style="text-align: center;">question_id</th>
<th style="text-align: center;">answer</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center;">467</td>
<td style="text-align: center;">4533</td>
<td style="text-align: center;">respuesta para la pregunta 4533</td>
</tr>
<tr>
<td style="text-align: center;">524</td>
<td style="text-align: center;">3211</td>
<td style="text-align: center;">respuesta para pregunta XXXX</td>
</tr>
<tr>
<td style="text-align: center;">348</td>
<td style="text-align: center;">1125</td>
<td style="text-align: center;">respuesta para la pregunta XXXX</td>
</tr>
</tbody>
</table>
<p>Las tablas muestran un quiz con dos preguntas asociadas. Y en la tabla de respuestas solo hay una respuesta asociada a este quiz: la que tiene el <strong><em>id</em></strong> <strong>467</strong>, correspondiente a la pregunta <strong>4533</strong>. Los registros con <strong><em>id</em></strong> <strong>524</strong> y <strong>348</strong> pertenecen a preguntas de otros quizzes, y la pregunta con <strong><em>id</em></strong> <strong>4534</strong> no tiene respuesta.</p>
<p>La aplicación en cuestión tenía una sección para mostrar un quiz que se deseara responder. Primero tomaba el <strong><em>id</em></strong> del quiz y en base a esto buscaba las preguntas y respuestas asociadas. No voy a mostrar código de conexión a base de datos ni nada por el estilo, únicamente mostraré arrays que simulen datos obtenidos de una base de datos.</p>
<p>Entonces, el código encargado de esta tarea hacía algo como lo siguiente:</p>
<pre><code>&lt;?php
// este array se obtenía realizando una consulta a la tabla 
// de quizzes, en base al quiz_id
$quizQuestions = [

    [
        'id'      =&gt; 4533,
        'quiz_id' =&gt; 4323,
        'title'   =&gt; 'ejemplo de pregunta'
    ],

    [
        'id'      =&gt; 4534,
        'quiz_id' =&gt; 4323,
        'title'   =&gt; 'otro ejemplo de pregunta'
    ]

];

// este array era una consulta que obtenía todos los registros
// de la tabla de respuestas. Sí, todos los registros
$answers = [

    [
        'id'          =&gt; 467,
        'question_id' =&gt; 4533,
        'answer'       =&gt; 'respuesta para la pregunta 4533'
    ],

    [
        'id'          =&gt; 524,
        'question_id' =&gt; 3211,
        'answer'       =&gt; 'respuesta para pregunta XXXX'
    ],

    [
        'id'          =&gt; 348,
        'question_id' =&gt; 1125,
        'answer'       =&gt; 'respuesta para pregunta XXXX'
    ]

];

// Se imprimen las preguntas y respuestas del quizz
// En la aplicación esto era un formulario con campos de texto
foreach($quizQuestions as $question) {
    echo $question['title'] . ' - respuesta: ';

    foreach($answers as $answer) {
        if($answer['question_id'] == $question['id']) {
            echo $answer['answer'];
        }
    }

    echo PHP_EOL;
}</code></pre>
<p>Veamos. El código primero hace una consulta a la tabla de preguntas y obtiene aquellas correspondientes al quiz que desea mostrar; el resultado de la consulta se almacena en un array. Nada inusual, pero es a partir de aquí cuando todo comienza a desmoronarse.</p>
<p>Luego tenemos otro array que almacena todas las respuestas habidas y por haber. Cero discriminación, ¡todas las respuestas son bienvenidas!</p>
<p>Luego tenemos un ciclo <code>foreach</code> que recorre el array de preguntas del quiz y muestra el título de la pregunta. Después... qué tenemos aquí, ¡un ciclo anidado! Este segundo ciclo, por cada pregunta, recorre TODO el array de respuestas (que ya de por si contiene TODOS los registros de la tabla de respuestas) comparando el campo <strong><em>id</em></strong> de la pregunta con el índice <strong><em>id_question</em></strong> de la respuesta para determinar si la pregunta de la iteración actual tiene una respuesta almacenada, y de ser así, muestra la respuesta.</p>
<p>A veces creo que alguien le dijo a la persona o personas encargadas de este código: </p>
<blockquote>
<p><em>Necesito que muestren las preguntas y respuestas de un quiz, pero quiero que sea complejo e ineficiente. Sean creativos, den rienda suelta a su imaginación. ¡Azoten las teclas como si no hubiera un mañana, hijos!</em></p>
</blockquote>
<p>Creo que jamás sabré las razones verdaderas, pero mi teoría es la siguiente. Nuestros amigos se dieron cuenta que al relacionar dos tablas con el tipo de join más común del mundo (inner join) se muestran los registros de ambas <strong>únicamente</strong> si hay registros correspondientes. Esto les presentó un problema: <strong><em>cuando existieran preguntas sin responder, no existiría un registro en la tabla de respuestas para dicha pregunta</em></strong>. ¿Resultado? La información de la pregunta no se mostraría.</p>
<p>Había que encontrar la manera de mostrar el título de cada pregunta y su respuesta (en caso de haberla). Encontraron la manera, aunque no la mejor.</p>
<p>Si en lugar de eso se hubiera utilizado un <strong> LEFT JOIN</strong>, más que simplificar el código, se hubiera logrado algo más eficiente, que no tuviera que obtener la tabla completa de respuestas y recorrer dicho array por cada pregunta.</p>
<p>El siguiente código muestra un ejemplo de cómo puede haber sido una alternativa. Las preguntas sin respuesta correspondiente devuelven un valor <code>null</code> los campos seleccionados, por lo que simplemente hay que verificar si el valor es distinto a <code>null</code> para desplegar la respuesta.</p>
<pre><code>/*
Internamente, la consulta pudo haber sido algo como lo siguiente:

SELECT q.id as question_id, q.title as question, a.answer as answer
  FROM questions q LEFT JOIN answers a
    ON q.id = a.question_id
 WHERE q.quiz_id = 4323;

 Lo que arrojaría los resultados como los de $questionData
 */

$questionData = [

    [
        'question_id' =&gt; 4533,
        'question'    =&gt; 'ejemplo de pregunta',
        'answer'      =&gt; 'respuesta para la pregunta 4533'
    ],

    [
        'question_id' =&gt; 4534,
        'question'    =&gt; 'otro ejemplo de pregunta',
        'answer'      =&gt; null
    ]

];

foreach($questionData as $data) {

    echo $data['question'];

    if(! is_null($data['answer'])) {
        echo  ' - respuesta: ' . $data['answer'];
    }

    echo PHP_EOL;

}</code></pre>
<p>El resultado sería algo así:</p>
<pre><code>ejemplo de pregunta - respuesta: respuesta para la pregunta 4533
otro ejemplo de pregunta</code></pre>
<hr />
<p>Moraleja: nunca se esfuercen.</p>
<p>Ya, en serio. Si quiero poner una especie de conclusión es la siguiente: </p>
<p>No hay que paralizarse buscando o esperando a tener la solución perfecta para un problema. Pero siempre hay que buscar la forma de mejorar cómo hacemos las cosas. Conocer las herramientas que te ofrece un lenguaje permite resolver problemas de manera más efectiva y en muchos casos, en una fracción del tiempo. </p>
<p>Haciendo referencia a una historia popular que tal vez ya conozcan: el mérito no está en ajustar un tornillo sino saber cuál ajustar.</p>]]></content:encoded>
      <guid>https://jborbon.me/por-que-debes-conocer-tus-herramientas</guid>
      <pubDate>Mon, 23 May 2016 12:34:52 -0500</pubDate>
    </item>
    <item>
      <title><![CDATA[ventajas injustas]]></title>
      <link>https://jborbon.me/ventajas-injustas</link>
      <description><![CDATA[<p>Hablemos de ventajas &quot;injustas&quot; (las comillas son importantes).</p>
<p>Eso es lo que son para la gente experta en inventar excusas. Seguramente les ha pasado que están hablando con alguien y en algún punto de la conversación la otra persona utiliza alguna de estas frases:</p>
<p>—Me gustaría hacer <em>&lt;inserte cualquier cosa que requiera tiempo y/o esfuerzo&gt;</em> pero no tengo tiempo</p>
<p>— Es que tú eres bien inteligente; eso ya se trae de nacimiento</p>
<p>—Si tuviera más tiempo haría un montón de cosas</p>
<p>—Si tuviera tu inteligencia lo que no habría hecho ya</p>
<p>A todas estas frases solo puedo responder mentalmente: <em><strong>Bullshit!</strong></em></p>
<p>En primer lugar me parece un poco ofensivo. Tal vez exagero, pero suena como si uno hubiera nacido con una virtud o don que le facilita demasiado las cosas, por lo que no tiene que esforzarse en absoluto. Es como si insinuaran que es &quot;hacer trampa en la vida&quot;.
Sí creo en el talento, pero no creo que por sí solo te vaya a llevar muy lejos.</p>
<p><em>¡Oh, la vida es tan injusta!</em></p>
<p>En segundo lugar, es práctica común para los expertos en <a href="https://es.wikipedia.org/wiki/Racionalizaci%C3%B3n">racionalización</a>. Ya saben, dar explicaciones lógicas de por qué hacen o no hacen las cosas.</p>
<p>—Siempre llegas tarde</p>
<p>—Es que había mucho tráfico... no sonó el despertador... </p>
<p>Es común que la gente solo vea el resultado final, por eso atribuyan ciertas habilidades a motivos hereditarios o cosas por el estilo. </p>
<p>Cuando vemos a un guitarrista prodigio tocar un solo —que de no ser porque lo estamos presenciando diríamos que es imposible—, podemos pensar en automático: <em>ha de ser bien chingón nacer con talento para tocar un instrumento</em>. Solo estamos viendo el resultado de muchas horas de práctica, muchas horas de frustración... pero al final el querer ser buenos en lo que hacen siempre prevalece.</p>
<p>Si todavía no te he matado de aburrimiento y aún sigues leyendo, pondré un pequeño ejemplo personal. </p>
<p>Considero que tengo un buen nivel de inglés (los colores: one, two, three...). Pero algunas personas que me conocen lo atribuyen a mi &quot;inteligencia&quot;.
La verdad es que no me considero inteligente; simplemente le dedico tiempo a las cosas que quiero aprender o en las que deseo mejorar. Si quieres ser bueno en algo tienes que dedicarle tiempo, practicar, vencer la frustración (y vaya que la habrá) y seguir aprendiendo.</p>
<p>Nunca he ido a cursos de inglés. No porque piense que no sirvan de nada, sino que en mi familia por mucho tiempo no tuvimos lo que llaman solvencia económica.</p>
<p>Aprendí en la escuela y por cuenta propia desde la secundaria. Desde entonces me gustaba el idioma, aunque deberían ver mis tareas de primer año (spoiler alert: eran una mierda). Eso nunca me detuvo, y ahora que lo pienso no fue por probarme a mi mismo que podía aprender; disfrutaba aprender y nunca lo vi como una obligación. Creo que esa es una razón por la que a mucha gente se le dificulta aprender inglés: lo ven como una obligación, un obstáculo para poder conseguir algo (pasar de año, conseguir un empleo...). </p>
<p>Hay una pequeña anécdota en el libro <em>On Writing</em> de Stephen King en la que cuenta como su hijo Owen decidió que iba a aprender a tocar el Saxófón. Lo metieron a clases  pero después de siete meses se dieron cuenta que el Saxofón no era lo suyo. No porque fuera malo o no tuviera coordinación, de hecho King cuenta que su hijo dominaba las notas y escalas que tenía que practicar en clase. El motivo fue que se dieron cuenta que a Owen no le gustaba tanto como creía. Únicamente tocaba el Sax durante la clase y cuando terminaba, lo guardaba en el estuche y no lo volvía a tocar hasta la siguiente clase.</p>
<p>Tampoco se trata de obsesionarse con ello, pero te tiene que gustar aunque sea un poco; de lo contrario es posible que lo termines odiando.</p>
<p>En lo personal, lo que más me ha servido para aprender es leer y escuchar material en inglés. Pero no cualquier tipo de material. Es probable que muchos no estén de acuerdo, pero en mi opinión, escuchar música en inglés no ayuda tanto como parece. Es mejor escuchar audiolibros o podcasts. Es frustrante cuando vas empezando y se te escapan muchas palabras (o de plano no entiendes ni madres), pero no hay que darse por vencido; aunque parezca que no hay progreso, sí lo hay. Gradual, pero lo hay.</p>
<p>Ya han pasado como 15 años desde que empecé a aprender. No soy un experto ni hablo como si hubiera nacido en Estados Unidos, pero me permite hacer muchas cosas que de lo contrario no serían posibles; sobretodo en el aspecto profesional. </p>
<hr />
<p>La próxima vez que sean testigos de las habilidades de alguien, piensen que detrás de eso hay más que simple talento y ventajas &quot;injustas&quot;.</p>
<p>No pretendía que el post sonara a seminario de autosuperación, así que con su permiso, voy a darme un baño con agua hirviendo antes de comenzar a sonar como César Lozano o alguno de esos güeyes. </p>
<p><img src="//i.imgur.com/A9RfZZJ.gif" class="img-responsive img-thumbnail center-block" /></p>]]></description>
      <content:encoded><![CDATA[<p>Hablemos de ventajas &quot;injustas&quot; (las comillas son importantes).</p>
<p>Eso es lo que son para la gente experta en inventar excusas. Seguramente les ha pasado que están hablando con alguien y en algún punto de la conversación la otra persona utiliza alguna de estas frases:</p>
<p>—Me gustaría hacer <em>&lt;inserte cualquier cosa que requiera tiempo y/o esfuerzo&gt;</em> pero no tengo tiempo</p>
<p>— Es que tú eres bien inteligente; eso ya se trae de nacimiento</p>
<p>—Si tuviera más tiempo haría un montón de cosas</p>
<p>—Si tuviera tu inteligencia lo que no habría hecho ya</p>
<p>A todas estas frases solo puedo responder mentalmente: <em><strong>Bullshit!</strong></em></p>
<p>En primer lugar me parece un poco ofensivo. Tal vez exagero, pero suena como si uno hubiera nacido con una virtud o don que le facilita demasiado las cosas, por lo que no tiene que esforzarse en absoluto. Es como si insinuaran que es &quot;hacer trampa en la vida&quot;.
Sí creo en el talento, pero no creo que por sí solo te vaya a llevar muy lejos.</p>
<p><em>¡Oh, la vida es tan injusta!</em></p>
<p>En segundo lugar, es práctica común para los expertos en <a href="https://es.wikipedia.org/wiki/Racionalizaci%C3%B3n">racionalización</a>. Ya saben, dar explicaciones lógicas de por qué hacen o no hacen las cosas.</p>
<p>—Siempre llegas tarde</p>
<p>—Es que había mucho tráfico... no sonó el despertador... </p>
<p>Es común que la gente solo vea el resultado final, por eso atribuyan ciertas habilidades a motivos hereditarios o cosas por el estilo. </p>
<p>Cuando vemos a un guitarrista prodigio tocar un solo —que de no ser porque lo estamos presenciando diríamos que es imposible—, podemos pensar en automático: <em>ha de ser bien chingón nacer con talento para tocar un instrumento</em>. Solo estamos viendo el resultado de muchas horas de práctica, muchas horas de frustración... pero al final el querer ser buenos en lo que hacen siempre prevalece.</p>
<p>Si todavía no te he matado de aburrimiento y aún sigues leyendo, pondré un pequeño ejemplo personal. </p>
<p>Considero que tengo un buen nivel de inglés (los colores: one, two, three...). Pero algunas personas que me conocen lo atribuyen a mi &quot;inteligencia&quot;.
La verdad es que no me considero inteligente; simplemente le dedico tiempo a las cosas que quiero aprender o en las que deseo mejorar. Si quieres ser bueno en algo tienes que dedicarle tiempo, practicar, vencer la frustración (y vaya que la habrá) y seguir aprendiendo.</p>
<p>Nunca he ido a cursos de inglés. No porque piense que no sirvan de nada, sino que en mi familia por mucho tiempo no tuvimos lo que llaman solvencia económica.</p>
<p>Aprendí en la escuela y por cuenta propia desde la secundaria. Desde entonces me gustaba el idioma, aunque deberían ver mis tareas de primer año (spoiler alert: eran una mierda). Eso nunca me detuvo, y ahora que lo pienso no fue por probarme a mi mismo que podía aprender; disfrutaba aprender y nunca lo vi como una obligación. Creo que esa es una razón por la que a mucha gente se le dificulta aprender inglés: lo ven como una obligación, un obstáculo para poder conseguir algo (pasar de año, conseguir un empleo...). </p>
<p>Hay una pequeña anécdota en el libro <em>On Writing</em> de Stephen King en la que cuenta como su hijo Owen decidió que iba a aprender a tocar el Saxófón. Lo metieron a clases  pero después de siete meses se dieron cuenta que el Saxofón no era lo suyo. No porque fuera malo o no tuviera coordinación, de hecho King cuenta que su hijo dominaba las notas y escalas que tenía que practicar en clase. El motivo fue que se dieron cuenta que a Owen no le gustaba tanto como creía. Únicamente tocaba el Sax durante la clase y cuando terminaba, lo guardaba en el estuche y no lo volvía a tocar hasta la siguiente clase.</p>
<p>Tampoco se trata de obsesionarse con ello, pero te tiene que gustar aunque sea un poco; de lo contrario es posible que lo termines odiando.</p>
<p>En lo personal, lo que más me ha servido para aprender es leer y escuchar material en inglés. Pero no cualquier tipo de material. Es probable que muchos no estén de acuerdo, pero en mi opinión, escuchar música en inglés no ayuda tanto como parece. Es mejor escuchar audiolibros o podcasts. Es frustrante cuando vas empezando y se te escapan muchas palabras (o de plano no entiendes ni madres), pero no hay que darse por vencido; aunque parezca que no hay progreso, sí lo hay. Gradual, pero lo hay.</p>
<p>Ya han pasado como 15 años desde que empecé a aprender. No soy un experto ni hablo como si hubiera nacido en Estados Unidos, pero me permite hacer muchas cosas que de lo contrario no serían posibles; sobretodo en el aspecto profesional. </p>
<hr />
<p>La próxima vez que sean testigos de las habilidades de alguien, piensen que detrás de eso hay más que simple talento y ventajas &quot;injustas&quot;.</p>
<p>No pretendía que el post sonara a seminario de autosuperación, así que con su permiso, voy a darme un baño con agua hirviendo antes de comenzar a sonar como César Lozano o alguno de esos güeyes. </p>
<p><img src="//i.imgur.com/A9RfZZJ.gif" class="img-responsive img-thumbnail center-block" /></p>]]></content:encoded>
      <guid>https://jborbon.me/ventajas-injustas</guid>
      <pubDate>Tue, 17 May 2016 14:16:55 -0500</pubDate>
    </item>
  </channel>
</rss>
