<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Dr. Danguer y Mr. Daniel &#187; PHP</title>
	<atom:link href="http://vida.danguer.com/category/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://vida.danguer.com</link>
	<description>Un blog más de un freelancer</description>
	<lastBuildDate>Wed, 26 Oct 2011 15:54:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Subir archivos a S3 directo con AJAX</title>
		<link>http://vida.danguer.com/2011/10/26/subir-archivos-a-s3-directo-con-ajax/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=subir-archivos-a-s3-directo-con-ajax</link>
		<comments>http://vida.danguer.com/2011/10/26/subir-archivos-a-s3-directo-con-ajax/#comments</comments>
		<pubDate>Wed, 26 Oct 2011 15:54:05 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[base64]]></category>
		<category><![CDATA[Base64 Binary]]></category>
		<category><![CDATA[BlobBuilder]]></category>
		<category><![CDATA[Canvas]]></category>
		<category><![CDATA[Crossdomain]]></category>
		<category><![CDATA[File]]></category>
		<category><![CDATA[FileReader]]></category>
		<category><![CDATA[S3]]></category>

		<guid isPermaLink="false">http://vida.danguer.com/?p=151</guid>
		<description><![CDATA[<a href="http://vida.danguer.com/2011/10/26/subir-archivos-a-s3-directo-con-ajax/" title="Subir archivos a S3 directo con AJAX"></a><p>Una gran característica de S3 es que puedes subir directamente a tu bucket a través de una <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/API/index.html?RESTObjectPOST.html">acción post</a>, el flujo es muy simple, solo necesitas generar algunos parámetros de la ubicación donde se almacenará y los permisos/meta-información, con el <a href="http://aws.amazon.com/sdkforphp/">SDK de Amazon para PHP</a>, puedes hacerlo fácilmente con:</p>
<pre name="code" class="php">require_once 'sdk.class.php';
	$s3_uploader = new S3BrowserUpload();
	$params = $s3_uploader-&#62;generate_upload_parameters($bucket, $expires, array(
		'acl' =&#62; AmazonS3::ACL_PUBLIC,
		'key' =&#62; $path_file,
		'Content-Type' =&#62; $mime,
	));</pre>
<p>El resultado de <code>$params</code> contendrá un arreglo cuyo índice <code>$params['input']</code> necesita ser pasado a valores ocultos de una forma y finalmente necesitas agregar un tipo <code>file</code> donde el usuario seleccionará el archivo (al final de los otros parámetros):</p>
<pre>&#60;input type="file" name="file" /&#62;</pre>
<p>También puedes especificar la dirección URL donde regresará el navegador después que el usuario haya subido el archivo.</p>
<p>Esto trabaja muy bien para subir archivos de &#8220;forma tradicional&#8221;, pero ¿cómo lo harías si quisieras tomar ventaja de las nuevas API&#8217;s de HTML5 como almacenar una cadena en un archivo; o obtener la imagen de un canvas y almacenarlo directo en S3 sin enviarlo primero a un servidor; o hacer una miniatura en el cliente y subirla directo a S3?</p>
<p>El primer problema aquí es que no puedes hacer llamadas crossdomain (interdominio), debido a que S3 no tiene la característica de agregar una cabecera de <a href="https://forums.aws.amazon.com/thread.jspa?threadID=34281"><code>Access-Control-Origin</code></a> que permitiría enviar el archivo de forma directa, afortunadamente con <a href="https://developer.mozilla.org/en/DOM/window.postMessage"><code>postMessage</code></a> puedes subir un archivo HTML y uno javascript a tu cuenta S3 y funcionará mediante un &#8220;proxy&#8221;.</p>
<p>El otro problema es que <code>postMessage</code> solo permite tipos que puedan ser clonados, por seguridad <code>File</code>/<code>FileReader</code> no pueden ser clonados por lo que necesitas pasar el contenido del archivo directamente en lugar del objeto.</p>
<p>Este ejemplo te permitirá</p>
<ol>
<li>Seleccionar una imagen</li>
<li>Reducirla a la mitad de tamaño mediante</li></ol><p>&#8230;</p>]]></description>
			<content:encoded><![CDATA[<a href="http://vida.danguer.com/2011/10/26/subir-archivos-a-s3-directo-con-ajax/" title="Subir archivos a S3 directo con AJAX"></a><p>Una gran característica de S3 es que puedes subir directamente a tu bucket a través de una <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/API/index.html?RESTObjectPOST.html">acción post</a>, el flujo es muy simple, solo necesitas generar algunos parámetros de la ubicación donde se almacenará y los permisos/meta-información, con el <a href="http://aws.amazon.com/sdkforphp/">SDK de Amazon para PHP</a>, puedes hacerlo fácilmente con:</p>
<pre name="code" class="php">require_once 'sdk.class.php';
	$s3_uploader = new S3BrowserUpload();
	$params = $s3_uploader-&gt;generate_upload_parameters($bucket, $expires, array(
		'acl' =&gt; AmazonS3::ACL_PUBLIC,
		'key' =&gt; $path_file,
		'Content-Type' =&gt; $mime,
	));</pre>
<p>El resultado de <code>$params</code> contendrá un arreglo cuyo índice <code>$params['input']</code> necesita ser pasado a valores ocultos de una forma y finalmente necesitas agregar un tipo <code>file</code> donde el usuario seleccionará el archivo (al final de los otros parámetros):</p>
<pre>&lt;input type="file" name="file" /&gt;</pre>
<p>También puedes especificar la dirección URL donde regresará el navegador después que el usuario haya subido el archivo.</p>
<p>Esto trabaja muy bien para subir archivos de &#8220;forma tradicional&#8221;, pero ¿cómo lo harías si quisieras tomar ventaja de las nuevas API&#8217;s de HTML5 como almacenar una cadena en un archivo; o obtener la imagen de un canvas y almacenarlo directo en S3 sin enviarlo primero a un servidor; o hacer una miniatura en el cliente y subirla directo a S3?</p>
<p>El primer problema aquí es que no puedes hacer llamadas crossdomain (interdominio), debido a que S3 no tiene la característica de agregar una cabecera de <a href="https://forums.aws.amazon.com/thread.jspa?threadID=34281"><code>Access-Control-Origin</code></a> que permitiría enviar el archivo de forma directa, afortunadamente con <a href="https://developer.mozilla.org/en/DOM/window.postMessage"><code>postMessage</code></a> puedes subir un archivo HTML y uno javascript a tu cuenta S3 y funcionará mediante un &#8220;proxy&#8221;.</p>
<p>El otro problema es que <code>postMessage</code> solo permite tipos que puedan ser clonados, por seguridad <code>File</code>/<code>FileReader</code> no pueden ser clonados por lo que necesitas pasar el contenido del archivo directamente en lugar del objeto.</p>
<p>Este ejemplo te permitirá</p>
<ol>
<li>Seleccionar una imagen</li>
<li>Reducirla a la mitad de tamaño mediante un canvas</li>
<li>Obtener los datos binarios codificados en base64</li>
<li>Pasar mediante un <code>postMessage</code> el contenido</li>
<li>Decodificar los datos en base64</li>
<li>Pasarlo mediante AJAX a tu cuenta de S3</li>
</ol>
<p>Esto es simple debido a que se están utilizando características estándar; pero desafortunadamente como HTML5 es un estándar en progreso, muchos navegadores aún no lo soportan, por el momento lo soportan Firefox 4+ and Chrome 11+</p>
<p>Primero revisaremos la parte que será almacenada en tu bucket de S3 y servirá como proxy, tendremos estos tres archivos:</p>
<ol>
<li><code>index.html</code><br />
Contendrá solo código para cargar el javascript y nada más</li>
<li><code>base64-binary.js</code><br />
Es un archivo útil que permite decodificar la imagen en base64 a un <code>ArrayBuffer</code> que será convertido en un <code>Blob</code> y será enviado como un &#8220;archivo&#8221; en una acción POST</li>
<li><code>upload.js</code><br />
Este archivo contendrá la lógica que recibirá el mensaje y convertirá la imagen codificada en base64 con parámetros POST y lo convertirá en una petición AJAX</li>
</ol>
<p>Puedes ver todos los archivos aquí:<br />
<a href="https://github.com/danguer/blog-examples/tree/master/html5/s3/direct-upload/s3-files">https://github.com/danguer/blog-examples/tree/master/html5/s3/direct-upload/s3-files</a></p>
<p>La parte principal de <code>upload.js</code> es:</p>
<pre name="code" class="javascript">window.addEventListener("message", function(event) {
	var data = event.data; //obtener los datos del mensaje

	//subir el archivo mediante blob
	var separator = 'base64,';
	var index = data.content.indexOf(separator);
	if (index != -1) {
		var bb = new BlobBuilder();

		//decodificar los datos de Base64 y convertirlos a un ArrayBuffer
		var barray = Base64Binary.decodeArrayBuffer(data.content.substring(index+separator.length));
	        bb.append(barray); 

	        var blob = bb.getBlob();

	        //pasar los parámetros POST mediante FormData
	        var formdata = new FormData();

		for (var param_key in data.params) {
			formdata.append(param_key, data.params[param_key]);
		}
		formdata.append("file", blob, "myblob.png"); //agregar como Blob

		//finalmente enviar los datos mediante AJAX
		var xhr = new XMLHttpRequest();
		xhr.open("POST", data.url, true);
		xhr.send(formdata);
	}
}, false);</pre>
<p>Como puedes ver solamente estamos decodificando el base64 y convirtiéndolo a un <code>Blob</code>, pasando todos los parámetros mediante <code>FormData</code> y enviándolos mediante AJAX.</p>
<p>Como este archivo estará almacenado en S3 no habrá ningún problema de crossdomain, pero necesitas poder crear un <code>Blob</code> y un <code>FormData</code> para que funcione.</p>
<p>&nbsp;</p>
<p>En el script para cargar el archivo tendremos un poco más de código, primero necesitamos código para generar los parámtros de S3, esto se puede hacer fácilmente en PHP con::</p>
<pre name="code" class="php">&lt;?php
	//iniciar aws-sdk
	require_once 'sdk.class.php';

	//la respuesta será en JSON
	header('Content-Type: application/json');	

	$bucket = 'danguer-blog'; //tu bucket en s3
	$bucket_path = 'upload-html5/tmp'; //"directorio" donde se guardarán los archivos
	$filename = isset($_POST['name'])?$_POST['name']:null;
	$filemime = isset($_POST['mime'])?$_POST['mime']:null;

	//manejar errores
	if (empty($filename)) {
		print json_encode(array(
			'error' =&gt; 'debe enviar el nombre',
		));
		return;
	}

	if (empty($filemime)) {
		print json_encode(array(
			'error' =&gt; 'debe enviar el mime',
		));
		return;
	}

	if (strpos($filename, '..') !== false) {
		print json_encode(array(
			'error' =&gt; 'no se aceptan rutas relativas',
		));
		return;
	}

	$expires = '+15 minutes'; //el token solo será válido por 15 minutos
	$path_file = "{$bucket_path}/{$filename}";
	$mime = $filemime; //mandar el contenido mime para ayudar a los navegadores

	//obtener los parámetros de S3 para subir directamente
	$s3_uploader = new S3BrowserUpload();
	$params = $s3_uploader-&gt;generate_upload_parameters($bucket, $expires, array(
		'acl' =&gt; AmazonS3::ACL_PUBLIC,
		'key' =&gt; $path_file,
		'Content-Type' =&gt; $mime,
	));

	print json_encode(array(
		'error' =&gt; '',
		'url' =&gt; "http://{$params['form']['action']}",
		'params' =&gt; $params['inputs']
	));
	return;</pre>
<p>&nbsp;</p>
<p>Después necesitamos presentar la entrada del archivo y crear un iframe que cargue los archivos S3 que hemos subido:</p>
<pre name="code" class="html">	&lt;!-- hiden frame --&gt;
	&lt;iframe id="postMessageFrame" src="&lt;?=$url_iframe?&gt;"&gt;
	&lt;/iframe&gt;

	&lt;h3&gt;Subir archivos&lt;/h3&gt;
	&lt;input type="file" accept="image/*" onchange="uploadFile(this.files)"&gt;</pre>
<p>Y el código javascript es un poco más, debido a que una vez que el usuario seleccione un archivo, necesitamos cambiarle el tamaño</p>
<pre name="code" class="javascript">
function resizeImage(file, mime) {
	var canvas = document.createElement("canvas");
        var ctx = canvas.getContext("2d");
        var canvasCopy = document.createElement("canvas");
        var ctxCopy = canvasCopy.getContext("2d");

	var reader = new FileReader();
        reader.onload = function() {
	    var image = new Image();
	    image.onload = function() {
                //escalar la imagen a la mitad
		canvasCopy.width = image.width;
	        canvasCopy.height = image.height;
	        ctxCopy.drawImage(image, 0, 0);

	        canvas.width = image.width * 0.5;
	        canvas.height = image.height * 0.5;
	        ctx.drawImage(canvasCopy, 0, 0, canvasCopy.width, canvasCopy.height, 0, 0, canvas.width, canvas.height);

	        //convertirlo en imagen y obtener el binario codificado en base64
	        //para enviarlo mediante postMessage
	        var url = canvas.toDataURL(mime, 0.80);
	        uploadImage(file, url)
	    }

        image.src = reader.result;
    };

	//leer contenidos
	reader.readAsDataURL(file);
}</pre>
<p>Como puedes ver estamos abriendo el archivo seleccionado, lo dibujamos en un canvas, lo copiamos en otro canvas a la mitad de su tamaño y obtenemos su representación binaria en base64 con el mismo mimetype del original, finalmente llamaremos para subir la imagen:</p>
<pre name="code" class="javascript">
function uploadImage(file, dataURL) {
	//cargar firma para S3
	var signature_params = {
		mime: file.type,
		name: file.name
	};

	$.post(url_ajax_signature, signature_params, function(response) {
		//enviar mediante postMessage
		var windowFrame = document.getElementById('postMessageFrame').contentWindow ;
		var data = {
			params: response.params,
			url: response.url,
			content: dataURL
		}

		//enviar los datos de la firma de S3 y los datos binarios en base64
		windowFrame.postMessage(data, 'http://&lt;?=$url_iframe_host?&gt;');
	}, 'json');
}</pre>
<p>Primero obtenemos los parámetros POST para S3 mediante AJAX del archivo PHP y después enviamos esos datos mediante <code>postMessage</code> al iframe en S3 para que pueda procesarlo y cargarlo a S3 sin necesidad de primero enviar el archivo al servidor y de ahi cargarlo a S3, con esto podemos cargar directamente a S3 desde el navegador del cliente y si es necesario podemos verificar los contenidos para evitar que se suba archivos maliciosos.</p>
<p>Todo el código se puede ver aquí:</p>
<p><a href="https://github.com/danguer/blog-examples/tree/master/html5/s3/direct-upload">https://github.com/danguer/blog-examples/tree/master/html5/s3/direct-upload</a></p>
]]></content:encoded>
			<wfw:commentRss>http://vida.danguer.com/2011/10/26/subir-archivos-a-s3-directo-con-ajax/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Obtener video webm de Youtube (antes FLV) en PHP</title>
		<link>http://vida.danguer.com/2011/10/20/obtener-video-webm-de-youtube-antes-flv-en-php/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=obtener-video-webm-de-youtube-antes-flv-en-php</link>
		<comments>http://vida.danguer.com/2011/10/20/obtener-video-webm-de-youtube-antes-flv-en-php/#comments</comments>
		<pubDate>Thu, 20 Oct 2011 16:52:23 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[FLV]]></category>
		<category><![CDATA[webm]]></category>
		<category><![CDATA[YouTube]]></category>

		<guid isPermaLink="false">http://vida.danguer.com/?p=142</guid>
		<description><![CDATA[<a href="http://vida.danguer.com/2011/10/20/obtener-video-webm-de-youtube-antes-flv-en-php/" title="Obtener video webm de Youtube (antes FLV) en PHP"></a><p>Hace tiempo había publicado el código para obtener el código de FLV de Youtube (<a href="http://vida.danguer.com/2009/01/09/obtener-video-flv-de-youtube/">http://vida.danguer.com/2009/01/09/obtener-video-flv-de-youtube/</a>), esto ya no funciona debido a que el archivo cambió y que Youtube ya no proporciona ahora un archivo FLV, ahora proporciona un archivo webm que es una extensión de un archivo Matroska, esto es debido a su iniciativa de soportar HTML5 para reproducir sus video.</p>
<p>El código para descargar el archivo webm es igual de fácil, pero no se puede reproducir con los reproductores &#8220;tradicionales&#8221; de flash, después de descargar el archivo pueden utilizar <a href="http://www.videolan.org/vlc/">VLC</a> o para web pueden utilizar el <a href="http://www.longtailvideo.com/support/jw-player/jw-player-for-html5/12121/webm-video-example">jwplayer</a> (para reproducirlo deben cambiar la extensión a <strong>.webm</strong>)</p>
<p>Más información de webm: <a href="http://www.webmproject.org/">http://www.webmproject.org/</a></p>
<pre name="code" class="php">&#60;?php
//video id by default
$video_id = 'v6xU96KLBL4';

if (isset($_REQUEST['video_id']))
        $video_id = trim($_REQUEST['video_id']);

//get vars from video
$url_info = 'http://www.youtube.com/get_video_info?video_id='.$video_id;
$info = file_get_contents($url_info);

$vars = array();
parse_str($info, $vars);

if ($vars['status'] == 'ok') {
	$vars_fmt = array();
	parse_str($vars['url_encoded_fmt_stream_map'], $vars_fmt);

	print "WEBM: {$vars_fmt['url']}";
} else
	print "Error: {$vars['reason']}";</pre>
<p>Puedes descargar el código en github: <a href="https://github.com/danguer/blog-examples/blob/master/php/youtube/get-flv.php">https://github.com/danguer/blog-examples/blob/master/php/youtube/get-flv.php</a></p>
]]></description>
			<content:encoded><![CDATA[<a href="http://vida.danguer.com/2011/10/20/obtener-video-webm-de-youtube-antes-flv-en-php/" title="Obtener video webm de Youtube (antes FLV) en PHP"></a><p>Hace tiempo había publicado el código para obtener el código de FLV de Youtube (<a href="http://vida.danguer.com/2009/01/09/obtener-video-flv-de-youtube/">http://vida.danguer.com/2009/01/09/obtener-video-flv-de-youtube/</a>), esto ya no funciona debido a que el archivo cambió y que Youtube ya no proporciona ahora un archivo FLV, ahora proporciona un archivo webm que es una extensión de un archivo Matroska, esto es debido a su iniciativa de soportar HTML5 para reproducir sus video.</p>
<p>El código para descargar el archivo webm es igual de fácil, pero no se puede reproducir con los reproductores &#8220;tradicionales&#8221; de flash, después de descargar el archivo pueden utilizar <a href="http://www.videolan.org/vlc/">VLC</a> o para web pueden utilizar el <a href="http://www.longtailvideo.com/support/jw-player/jw-player-for-html5/12121/webm-video-example">jwplayer</a> (para reproducirlo deben cambiar la extensión a <strong>.webm</strong>)</p>
<p>Más información de webm: <a href="http://www.webmproject.org/">http://www.webmproject.org/</a></p>
<pre name="code" class="php">&lt;?php
//video id by default
$video_id = 'v6xU96KLBL4';

if (isset($_REQUEST['video_id']))
        $video_id = trim($_REQUEST['video_id']);

//get vars from video
$url_info = 'http://www.youtube.com/get_video_info?video_id='.$video_id;
$info = file_get_contents($url_info);

$vars = array();
parse_str($info, $vars);

if ($vars['status'] == 'ok') {
	$vars_fmt = array();
	parse_str($vars['url_encoded_fmt_stream_map'], $vars_fmt);

	print "WEBM: {$vars_fmt['url']}";
} else
	print "Error: {$vars['reason']}";</pre>
<p>Puedes descargar el código en github: <a href="https://github.com/danguer/blog-examples/blob/master/php/youtube/get-flv.php">https://github.com/danguer/blog-examples/blob/master/php/youtube/get-flv.php</a></p>
]]></content:encoded>
			<wfw:commentRss>http://vida.danguer.com/2011/10/20/obtener-video-webm-de-youtube-antes-flv-en-php/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Iniciando sesión con Google+ y OAuth</title>
		<link>http://vida.danguer.com/2011/10/19/iniciando-sesion-con-google-y-oauth/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=iniciando-sesion-con-google-y-oauth</link>
		<comments>http://vida.danguer.com/2011/10/19/iniciando-sesion-con-google-y-oauth/#comments</comments>
		<pubDate>Wed, 19 Oct 2011 17:38:56 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Google Plus]]></category>
		<category><![CDATA[OAuth]]></category>
		<category><![CDATA[Sesion]]></category>

		<guid isPermaLink="false">http://vida.danguer.com/?p=133</guid>
		<description><![CDATA[<a href="http://vida.danguer.com/2011/10/19/iniciando-sesion-con-google-y-oauth/" title="Iniciando sesión con Google+ y OAuth"></a><p>Un escenario común es iniciar una sesión mediante una cuenta externa como Google+, Twitter o Facebook.<br />
En este código mostraré como hacerlo con Google+ API</p>
<p>Primero necesitas registrar el API en <a title="Google Api Console" href="https://code.google.com/apis/console/" target="_blank">https://code.google.com/apis/console/</a>, registrar el servicio de Google+ API y crear una nueva aplicación, en el campo de &#8220;Redirect URI&#8221; necesitas escribir la ruta del script y lo siguiente: &#8220;<code>googleplus.php?op=redirect</code>&#8220;, comentaré el porqué después; después que veas el código podrás separarlo en rutas o scripts más elegantes.</p>
<p>En la aplicación generada, necesitarás los campos de  Redirection URI y Client ID</p>
<p>Google OAuth funciona de la siguiente manera:</p>
<ol>
<li>Generar una URL usando los parámetros de los servicios que quieres acceder (<code>scope</code>) y tu Client ID y Redirection URI</li>
<li>Necesitas redireccionar al usuario a esta URL</li>
<li>Después que el usuario ha ingresado, el sistema te redireccionará a la URL que proporcionaste con parametros hash (<code>googleplus.php?op=redirect#access_token=....</code>), así que no verás esto en el servidor; puedes pasarlo a tu servidor mediante AJAX o en este caso mediante una petición GET</li>
</ol>
<p>En el código el parámetro &#8220;<code>?op=redirect</code>&#8221; mostrará una página blanca con un código en javascript para convertir el hash en una petición <code>GET</code>; esto pemitirá obtener en el servidor el parámetro <code>access_token</code> que usarás para verificar mediante Google+ si es válido.</p>
<p>Esta parte del código lo hace:</p>
<pre name="code" class="php">
&#60;?php
        $access_token = $_GET['access_token'];

	//verificar si es real
	$data = @file_get_contents("https://www.googleapis.com/plus/v1/people/me?access_token={$access_token}");
	if ($data) {
		print $data;
	} else {
		print "Token no valido!";
	}</pre>
<p>El código no está preparado para manejar errores (lo mostrará como un parámetro <code>$_GET['error']</code>) así que necesitas manejar eso en tu caso.</p>
<p>Ligas:</p>
<ul>
<li><a href="https://github.com/danguer/blog-examples/blob/master/php/oauth/googleplus.php" target="_blank">https://github.com/danguer/blog-examples/blob/master/php/oauth/googleplus.php</a> (Código para iniciar sesión)</li>
<li><a href="https://code.google.com/apis/accounts/docs/OAuth2.html" target="_blank">https://code.google.com/apis/accounts/docs/OAuth2.html</a> (Implementación de Google OAuth)</li>
<li><a href="https://developers.google.com/+/api/" target="_blank">https://developers.google.com/+/api/</a> (Google+ API)</li>
</ul>
]]></description>
			<content:encoded><![CDATA[<a href="http://vida.danguer.com/2011/10/19/iniciando-sesion-con-google-y-oauth/" title="Iniciando sesión con Google+ y OAuth"></a><p>Un escenario común es iniciar una sesión mediante una cuenta externa como Google+, Twitter o Facebook.<br />
En este código mostraré como hacerlo con Google+ API</p>
<p>Primero necesitas registrar el API en <a title="Google Api Console" href="https://code.google.com/apis/console/" target="_blank">https://code.google.com/apis/console/</a>, registrar el servicio de Google+ API y crear una nueva aplicación, en el campo de &#8220;Redirect URI&#8221; necesitas escribir la ruta del script y lo siguiente: &#8220;<code>googleplus.php?op=redirect</code>&#8220;, comentaré el porqué después; después que veas el código podrás separarlo en rutas o scripts más elegantes.</p>
<p>En la aplicación generada, necesitarás los campos de  Redirection URI y Client ID</p>
<p>Google OAuth funciona de la siguiente manera:</p>
<ol>
<li>Generar una URL usando los parámetros de los servicios que quieres acceder (<code>scope</code>) y tu Client ID y Redirection URI</li>
<li>Necesitas redireccionar al usuario a esta URL</li>
<li>Después que el usuario ha ingresado, el sistema te redireccionará a la URL que proporcionaste con parametros hash (<code>googleplus.php?op=redirect#access_token=....</code>), así que no verás esto en el servidor; puedes pasarlo a tu servidor mediante AJAX o en este caso mediante una petición GET</li>
</ol>
<p>En el código el parámetro &#8220;<code>?op=redirect</code>&#8221; mostrará una página blanca con un código en javascript para convertir el hash en una petición <code>GET</code>; esto pemitirá obtener en el servidor el parámetro <code>access_token</code> que usarás para verificar mediante Google+ si es válido.</p>
<p>Esta parte del código lo hace:</p>
<pre name="code" class="php">
&lt;?php
        $access_token = $_GET['access_token'];

	//verificar si es real
	$data = @file_get_contents("https://www.googleapis.com/plus/v1/people/me?access_token={$access_token}");
	if ($data) {
		print $data;
	} else {
		print "Token no valido!";
	}</pre>
<p>El código no está preparado para manejar errores (lo mostrará como un parámetro <code>$_GET['error']</code>) así que necesitas manejar eso en tu caso.</p>
<p>Ligas:</p>
<ul>
<li><a href="https://github.com/danguer/blog-examples/blob/master/php/oauth/googleplus.php" target="_blank">https://github.com/danguer/blog-examples/blob/master/php/oauth/googleplus.php</a> (Código para iniciar sesión)</li>
<li><a href="https://code.google.com/apis/accounts/docs/OAuth2.html" target="_blank">https://code.google.com/apis/accounts/docs/OAuth2.html</a> (Implementación de Google OAuth)</li>
<li><a href="https://developers.google.com/+/api/" target="_blank">https://developers.google.com/+/api/</a> (Google+ API)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://vida.danguer.com/2011/10/19/iniciando-sesion-con-google-y-oauth/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Usando php para analizar logs de apache</title>
		<link>http://vida.danguer.com/2011/10/13/usando-php-para-analizar-logs-de-apache/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=usando-php-para-analizar-logs-de-apache</link>
		<comments>http://vida.danguer.com/2011/10/13/usando-php-para-analizar-logs-de-apache/#comments</comments>
		<pubDate>Thu, 13 Oct 2011 16:59:35 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[apache2]]></category>
		<category><![CDATA[logs]]></category>
		<category><![CDATA[syslog]]></category>

		<guid isPermaLink="false">http://vida.danguer.com/?p=129</guid>
		<description><![CDATA[<a href="http://vida.danguer.com/2011/10/13/usando-php-para-analizar-logs-de-apache/" title="Usando php para analizar logs de apache"></a><p>Apache tiene una característica especial que es enviar la salida de sus logs a través de una tubería (pipe). Esto evita configurar syslog para reenviar los registros a un servidor en php.</p>
<p>Los cambios en apache son realmente simples, solo necesitas colocar algo así en la configuración:</p>
<pre>LogFormat "%v %A %D \"%r\" %&#62;s %O \"%{Referer}i\" \"%{User-Agent}i\"" milog
CustomLog "&#124;/usr/bin/php5 [PATH_TO_SCRIPT]/apache-stdin.php log" milog
ErrorLog "&#124;/usr/bin/php5 [PATH_TO_SCRIPT]/apache-stdin.php error"</pre>
<p>En apache-stdin.php el código es realmente simple, solo necesitas hacer:</p>
<pre name="code" class="php">&#60;?php
$fp = fopen('php://stdin', 'r');
do {
	//leer una línea de apache, si no tiene datos se bloqueará hasta obtenerla
	$data = fgets($fp);
	$data = trim($data); //quitar espacios y "enters" del mensaje

	if (empty($data)) {
		break; //no envia más datos, así que terminar el script
	}

	//procesar el mensaje
} while(true);

fclose($fp);</pre>
<p>Como puedes ver es simplemente leer una línea y procesarla.</p>
<p>He escrito un script de ayuda para esto, que puedes descargar aqui:<br />
<a href="https://github.com/danguer/blog-examples/blob/master/php/syslog/apache-stdin.php">https://github.com/danguer/blog-examples/blob/master/php/syslog/apache-stdin.php</a></p>
<p>A este script le puedes pasar un parámetro adicional para especificar si es un registro normal o un registro de error; será igual que en la configuración de apache que puse anteriormente.</p>
<p>También puedes configurar el script para especificar el format de registro que estás usando en apache para obtener una descripción simple; el formato debe ser literal como:</p>
<pre name="code" class="php">&#60;?php
$format = '%v %A %D \"%r\" %&#62;s %O \"%{Referer}i\" \"%{User-Agent}i\"';</pre>
<p>Con esto, el script generará un arreglo como:</p>
<pre name="code" class="php">array(
'hostname' =&#62; 'danguer.com',
'local_ip' =&#62; 127.0.0.1,
'time_ms' =&#62; 0.0002,
'first_line_request' =&#62; 'GET / HTTP/1.1',
'status_last' =&#62; 200,
'bytes_sent' =&#62; 2048
);</pre>
<p>Con esto puedes usarlo para almacenarlo en un archivo (es lo que hace sin cambios el script), insertarlo a una base de datos, usar simpledb, etc.</p>
]]></description>
			<content:encoded><![CDATA[<a href="http://vida.danguer.com/2011/10/13/usando-php-para-analizar-logs-de-apache/" title="Usando php para analizar logs de apache"></a><p>Apache tiene una característica especial que es enviar la salida de sus logs a través de una tubería (pipe). Esto evita configurar syslog para reenviar los registros a un servidor en php.</p>
<p>Los cambios en apache son realmente simples, solo necesitas colocar algo así en la configuración:</p>
<pre>LogFormat "%v %A %D \"%r\" %&gt;s %O \"%{Referer}i\" \"%{User-Agent}i\"" milog
CustomLog "|/usr/bin/php5 [PATH_TO_SCRIPT]/apache-stdin.php log" milog
ErrorLog "|/usr/bin/php5 [PATH_TO_SCRIPT]/apache-stdin.php error"</pre>
<p>En apache-stdin.php el código es realmente simple, solo necesitas hacer:</p>
<pre name="code" class="php">&lt;?php
$fp = fopen('php://stdin', 'r');
do {
	//leer una línea de apache, si no tiene datos se bloqueará hasta obtenerla
	$data = fgets($fp);
	$data = trim($data); //quitar espacios y "enters" del mensaje

	if (empty($data)) {
		break; //no envia más datos, así que terminar el script
	}

	//procesar el mensaje
} while(true);

fclose($fp);</pre>
<p>Como puedes ver es simplemente leer una línea y procesarla.</p>
<p>He escrito un script de ayuda para esto, que puedes descargar aqui:<br />
<a href="https://github.com/danguer/blog-examples/blob/master/php/syslog/apache-stdin.php">https://github.com/danguer/blog-examples/blob/master/php/syslog/apache-stdin.php</a></p>
<p>A este script le puedes pasar un parámetro adicional para especificar si es un registro normal o un registro de error; será igual que en la configuración de apache que puse anteriormente.</p>
<p>También puedes configurar el script para especificar el format de registro que estás usando en apache para obtener una descripción simple; el formato debe ser literal como:</p>
<pre name="code" class="php">&lt;?php
$format = '%v %A %D \"%r\" %&gt;s %O \"%{Referer}i\" \"%{User-Agent}i\"';</pre>
<p>Con esto, el script generará un arreglo como:</p>
<pre name="code" class="php">array(
'hostname' =&gt; 'danguer.com',
'local_ip' =&gt; 127.0.0.1,
'time_ms' =&gt; 0.0002,
'first_line_request' =&gt; 'GET / HTTP/1.1',
'status_last' =&gt; 200,
'bytes_sent' =&gt; 2048
);</pre>
<p>Con esto puedes usarlo para almacenarlo en un archivo (es lo que hace sin cambios el script), insertarlo a una base de datos, usar simpledb, etc.</p>
]]></content:encoded>
			<wfw:commentRss>http://vida.danguer.com/2011/10/13/usando-php-para-analizar-logs-de-apache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Instalando php-fpm y apache2</title>
		<link>http://vida.danguer.com/2011/10/10/instalando-php-fpm-y-apache2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=instalando-php-fpm-y-apache2</link>
		<comments>http://vida.danguer.com/2011/10/10/instalando-php-fpm-y-apache2/#comments</comments>
		<pubDate>Mon, 10 Oct 2011 17:17:37 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[.htaccess]]></category>
		<category><![CDATA[apache2]]></category>
		<category><![CDATA[apache2-mpm]]></category>
		<category><![CDATA[php-fpm]]></category>

		<guid isPermaLink="false">http://vida.danguer.com/?p=124</guid>
		<description><![CDATA[<a href="http://vida.danguer.com/2011/10/10/instalando-php-fpm-y-apache2/" title="Instalando php-fpm y apache2"></a><p>He leído muchos tutoriales sobre como instalar <code>nginx</code> y <code>php-fpm</code>.<br />
Sigo usando apache2 más que nada debido por la facilidad del archivo <code>.htaccess</code> que permite hacer uso de mod_rewrite y proteger las rutas con un password (así como usar otros módulos como <code>mod_svn</code>, etc)</p>
<p>No he encontrado una manera 100% transparente como <code>ngninx</code>, pero detallo lo que uso para trabajar con  <code>php-fpm</code> y <code>apache2</code>. Pasos en debian:</p>
<ul>
<li><code>echo "deb http://packages.dotdeb.org stable all" &#62;&#62; /etc/apt/sources.list</code></li>
<li><code>apt-get update</code></li>
<li><code>apt-get install libapache2-mod-fastcgi apache2-mpm-worker php5-fpm php5-common php5-cli</code></li>
<li>hacer todo los <code>apt-get</code> de todos los módulos <code>php5-*</code>  que quieras</li>
<li>Agregar lo siguiente a un nuevo archivo: <code>/etc/apache2/mods-enabled/fpm.load</code>
<pre>AddType application/x-httpd-fastphp5 .php .phtml
Action application/x-httpd-fastphp5 /fast-cgi-fake-handler</pre>
</li>
<li>en <code>/etc/php5/fpm/pool.d/www.conf</code> cambiar:
<pre>listen = 127.0.0.1:9000</pre>
<p>por:</p>
<pre>; listen = 127.0.0.1:9000
 listen = /var/run/php-fpm.socket</pre>
<p>Esto habilitará el socket de unix que debe ser más eficiente</li>
<li><code>/etc/init.d/php-fpm restart</code></li>
<li><code>/etc/init.d/apache2 restart</code></li>
</ul>
<p>&#160;</p>
<p>Con esto podrás usar php-fpm a través de una url &#8220;ficticia&#8221;: <code>/fast-cgi-fake-handler</code> si quieres cambiar tu archivo <code>/etc/apache2/sites-enabled/000-default</code> se vería de esta manera:</p>
<pre>FastCGIExternalServer /var/www/fast-cgi-fake-handler -socket /var/run/php-fpm.socket
DocumentRoot /var/www</pre>
<p>El sistema cambiará la url <code>/index.php</code> por: <code>/fast-cgi-fake-handler/index.php</code> que será enviada a php-fpm mediante fastcgi</p>
<p>Esto tiene dos problemas:</p>
<ol>
<li>Necesitas especificar <code>FastCGIExternalServer</code> de la forma <code>${DOCUMENT_ROOT}/fast-cgi-fake-handler</code> en todos tus hosts virtuales para que funcione</li>
<li>Zend framework y otros que utilicen el patron de consumir cualquier url y pasársela a un script <code>index.php</code>  no funcionarán porque generarán un ciclo infinito; pero hay una solución sencilla en tu código <code>.htaccess</code> escribe después de <code>RewriteEngine On</code>:
<pre>RewriteRule ^fast-cgi-fake-handler/ - [L,NC]</pre>
<p>Esto evitará procesar todas las ligas que contengan &#8220;<code>fast-cgi-fake-handler</code>&#8221; como su inicio; o claro puedes usar  <code>RewriteCond</code> para evitar esto.</li>
</ol>
]]></description>
			<content:encoded><![CDATA[<a href="http://vida.danguer.com/2011/10/10/instalando-php-fpm-y-apache2/" title="Instalando php-fpm y apache2"></a><p>He leído muchos tutoriales sobre como instalar <code>nginx</code> y <code>php-fpm</code>.<br />
Sigo usando apache2 más que nada debido por la facilidad del archivo <code>.htaccess</code> que permite hacer uso de mod_rewrite y proteger las rutas con un password (así como usar otros módulos como <code>mod_svn</code>, etc)</p>
<p>No he encontrado una manera 100% transparente como <code>ngninx</code>, pero detallo lo que uso para trabajar con  <code>php-fpm</code> y <code>apache2</code>. Pasos en debian:</p>
<ul>
<li><code>echo "deb http://packages.dotdeb.org stable all" &gt;&gt; /etc/apt/sources.list</code></li>
<li><code>apt-get update</code></li>
<li><code>apt-get install libapache2-mod-fastcgi apache2-mpm-worker php5-fpm php5-common php5-cli</code></li>
<li>hacer todo los <code>apt-get</code> de todos los módulos <code>php5-*</code>  que quieras</li>
<li>Agregar lo siguiente a un nuevo archivo: <code>/etc/apache2/mods-enabled/fpm.load</code>
<pre>AddType application/x-httpd-fastphp5 .php .phtml
Action application/x-httpd-fastphp5 /fast-cgi-fake-handler</pre>
</li>
<li>en <code>/etc/php5/fpm/pool.d/www.conf</code> cambiar:
<pre>listen = 127.0.0.1:9000</pre>
<p>por:</p>
<pre>; listen = 127.0.0.1:9000
 listen = /var/run/php-fpm.socket</pre>
<p>Esto habilitará el socket de unix que debe ser más eficiente</li>
<li><code>/etc/init.d/php-fpm restart</code></li>
<li><code>/etc/init.d/apache2 restart</code></li>
</ul>
<p>&nbsp;</p>
<p>Con esto podrás usar php-fpm a través de una url &#8220;ficticia&#8221;: <code>/fast-cgi-fake-handler</code> si quieres cambiar tu archivo <code>/etc/apache2/sites-enabled/000-default</code> se vería de esta manera:</p>
<pre>FastCGIExternalServer /var/www/fast-cgi-fake-handler -socket /var/run/php-fpm.socket
DocumentRoot /var/www</pre>
<p>El sistema cambiará la url <code>/index.php</code> por: <code>/fast-cgi-fake-handler/index.php</code> que será enviada a php-fpm mediante fastcgi</p>
<p>Esto tiene dos problemas:</p>
<ol>
<li>Necesitas especificar <code>FastCGIExternalServer</code> de la forma <code>${DOCUMENT_ROOT}/fast-cgi-fake-handler</code> en todos tus hosts virtuales para que funcione</li>
<li>Zend framework y otros que utilicen el patron de consumir cualquier url y pasársela a un script <code>index.php</code>  no funcionarán porque generarán un ciclo infinito; pero hay una solución sencilla en tu código <code>.htaccess</code> escribe después de <code>RewriteEngine On</code>:
<pre>RewriteRule ^fast-cgi-fake-handler/ - [L,NC]</pre>
<p>Esto evitará procesar todas las ligas que contengan &#8220;<code>fast-cgi-fake-handler</code>&#8221; como su inicio; o claro puedes usar  <code>RewriteCond</code> para evitar esto.</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://vida.danguer.com/2011/10/10/instalando-php-fpm-y-apache2/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Mantener cambios en branches de SVN</title>
		<link>http://vida.danguer.com/2010/09/28/mantener-cambios-en-branches-de-svn/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mantener-cambios-en-branches-de-svn</link>
		<comments>http://vida.danguer.com/2010/09/28/mantener-cambios-en-branches-de-svn/#comments</comments>
		<pubDate>Tue, 28 Sep 2010 18:04:12 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Trabajo]]></category>

		<guid isPermaLink="false">http://vida.danguer.com/?p=102</guid>
		<description><![CDATA[<a href="http://vida.danguer.com/2010/09/28/mantener-cambios-en-branches-de-svn/" title="Mantener cambios en branches de SVN"></a><p>Cuando se desarrolla en un branch de SVN el mayor problema es mantener actualizados los archivos que estamos modificando con los cambios que se hacen en la rama principal (<code>trunk</code>) de SVN. Para hacer más fácil esto, he creado un  script que checa los archivos cambiados en nuestro branch y checa con los archivos modificados del principal, la manera de invocarlo es simple:</p>
<pre>
php changes-branch.php BASE_SVN PROYECTO BRANCH
</pre>
</p>
<p>Así por ejemplo si nuestro svn está en: <code>http://svn.example.com/proyecto</code> y nuestro branch es: <code>http://svn.example.com/proyecto/branches/cambios</code> debemos invocar el programa como:</p>
<pre>php changes.branch.php http://svn.example.com proyecto cambios</pre>
</p>
<p>El sistema mostrará cuales archivos han tenido cambios desde nuestra última actualización de nuestro branch, así como la salida del diff para ver los cambios.</p>
<p>El script también tiene una opción que les permite ver todos los cambios desde que se inició el branch, simplemente agregar un cuarto parámetro, por ejemplo:</p>
<pre>php changes-branch.php BASE_SVN PROYECTO BRANCH true</pre>
</p>
<p>El código completo está aqui: <a href="https://github.com/danguer/blog-examples/blob/master/php/svn/changes-branch.php">https://github.com/danguer/blog-examples/blob/master/php/svn/changes-branch.php</a></p>
]]></description>
			<content:encoded><![CDATA[<a href="http://vida.danguer.com/2010/09/28/mantener-cambios-en-branches-de-svn/" title="Mantener cambios en branches de SVN"></a><p>Cuando se desarrolla en un branch de SVN el mayor problema es mantener actualizados los archivos que estamos modificando con los cambios que se hacen en la rama principal (<code>trunk</code>) de SVN. Para hacer más fácil esto, he creado un  script que checa los archivos cambiados en nuestro branch y checa con los archivos modificados del principal, la manera de invocarlo es simple:</p>
<pre>
php changes-branch.php BASE_SVN PROYECTO BRANCH
</pre>
</p>
<p>Así por ejemplo si nuestro svn está en: <code>http://svn.example.com/proyecto</code> y nuestro branch es: <code>http://svn.example.com/proyecto/branches/cambios</code> debemos invocar el programa como:</p>
<pre>php changes.branch.php http://svn.example.com proyecto cambios</pre>
</p>
<p>El sistema mostrará cuales archivos han tenido cambios desde nuestra última actualización de nuestro branch, así como la salida del diff para ver los cambios.</p>
<p>El script también tiene una opción que les permite ver todos los cambios desde que se inició el branch, simplemente agregar un cuarto parámetro, por ejemplo:</p>
<pre>php changes-branch.php BASE_SVN PROYECTO BRANCH true</pre>
</p>
<p>El código completo está aqui: <a href="https://github.com/danguer/blog-examples/blob/master/php/svn/changes-branch.php">https://github.com/danguer/blog-examples/blob/master/php/svn/changes-branch.php</a></p>
]]></content:encoded>
			<wfw:commentRss>http://vida.danguer.com/2010/09/28/mantener-cambios-en-branches-de-svn/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Cuidado al usar Zend_Date</title>
		<link>http://vida.danguer.com/2010/01/30/cuidado-al-usar-zend_date/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=cuidado-al-usar-zend_date</link>
		<comments>http://vida.danguer.com/2010/01/30/cuidado-al-usar-zend_date/#comments</comments>
		<pubDate>Sun, 31 Jan 2010 00:14:20 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Trabajo]]></category>
		<category><![CDATA[Fechas]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[Zend_Date]]></category>

		<guid isPermaLink="false">http://vida.danguer.com/?p=96</guid>
		<description><![CDATA[<a href="http://vida.danguer.com/2010/01/30/cuidado-al-usar-zend_date/" title="Cuidado al usar Zend_Date"></a><p>En un proyecto, estaba utilizando Zend_Date para manejo de horas en diferentes zonas horarias, cuando lo estaba desarrollando empecé a tener un error porque olvidé que la fecha se actualiza cuando cambias un parámetro y el problema surge cuando cambias el mes o día y se encuentra con una fecha inexistente aunque en el código parezca correcta, por ejemplo, supongamos que estamos en el mes de febrero y queremos crear una fecha de marzo (asignaré la fecha para reproducir el error, aunque el error es cuando se utiliza la fecha actual)</p>
<pre name="code" class="php">include_once("Zend/Date.php");
$fecha_febrero = mktime(0, 0, 0, 2, 1, 2010);
$fecha = new Zend_Date($fecha_febrero);
$fecha-&#62;setDay(31);
$fecha-&#62;setMonth(3);
print "Fecha Marzo: ".date("d/m/Y", $fecha-&#62;getTimestamp());</pre>
<p>Uno esperaría que el resultado fuera: <strong>31/03/2010</strong></p>
<p>Pero en su lugar mostrará <strong>03/03/2010</strong></p>
<p>Esto es porque al hacer <code>$fecha-&#62;setDay(31)</code> el sistema intenta crear la fecha: <strong>31/02/2010</strong> que no existe, por lo que &#8220;recorre&#8221; los días restantes para quedar en <strong>03/03/2010</strong>, al asignar el mes, no tiene efecto.</p>
<p>La solución es simplemente hacerlo en orden inverso:</p>
<pre name="code" class="php">include_once("Zend/Date.php");
$fecha_febrero = mktime(0, 0, 0, 2, 1, 2010);
$fecha = new Zend_Date($fecha_febrero);
$fecha-&#62;setMonth(3);
$fecha-&#62;setDay(31);
print "Fecha Marzo: ".date("d/m/Y", $fecha-&#62;getTimestamp());</pre>
<p>Eso parecería la solución ideal (y es la recomendada por la documentación), pero no es totalmente cierta, si por ejemplo estamos en enero 31 del 2010 e intentamos asignar una fecha de febrero, siempre obtendremos una fecha de marzo:</p>
<pre name="code" class="php">include_once 'Zend/Date.php';
$fecha_enero = mktime(0, 0, 0, 1, 31, 2010);
$fecha = new Zend_Date($fecha_enero);
$fecha-&#62;setMonth(2);
$fecha-&#62;setDay(10);
print "Fecha Febrero: ".date("d/m/Y", $fecha-&#62;getTimestamp());</pre>
<p>Uno esperaría que mostrara la fecha:<br />
<strong>10/02/2010</strong></p>
<p>Pero en realidad muestra<br />
<strong>10/03/2010</strong></p>
<p>Esto porque cuando asignamos el mes, el sistema intenta crear la fecha 31/03/2010 que una vez más es incorrecta, por lo que crea la fecha 03/03/2010, pero al asignar la fecha, el resultado es&#8230;</p>]]></description>
			<content:encoded><![CDATA[<a href="http://vida.danguer.com/2010/01/30/cuidado-al-usar-zend_date/" title="Cuidado al usar Zend_Date"></a><p>En un proyecto, estaba utilizando Zend_Date para manejo de horas en diferentes zonas horarias, cuando lo estaba desarrollando empecé a tener un error porque olvidé que la fecha se actualiza cuando cambias un parámetro y el problema surge cuando cambias el mes o día y se encuentra con una fecha inexistente aunque en el código parezca correcta, por ejemplo, supongamos que estamos en el mes de febrero y queremos crear una fecha de marzo (asignaré la fecha para reproducir el error, aunque el error es cuando se utiliza la fecha actual)</p>
<pre name="code" class="php">include_once("Zend/Date.php");
$fecha_febrero = mktime(0, 0, 0, 2, 1, 2010);
$fecha = new Zend_Date($fecha_febrero);
$fecha-&gt;setDay(31);
$fecha-&gt;setMonth(3);
print "Fecha Marzo: ".date("d/m/Y", $fecha-&gt;getTimestamp());</pre>
<p>Uno esperaría que el resultado fuera: <strong>31/03/2010</strong></p>
<p>Pero en su lugar mostrará <strong>03/03/2010</strong></p>
<p>Esto es porque al hacer <code>$fecha-&gt;setDay(31)</code> el sistema intenta crear la fecha: <strong>31/02/2010</strong> que no existe, por lo que &#8220;recorre&#8221; los días restantes para quedar en <strong>03/03/2010</strong>, al asignar el mes, no tiene efecto.</p>
<p>La solución es simplemente hacerlo en orden inverso:</p>
<pre name="code" class="php">include_once("Zend/Date.php");
$fecha_febrero = mktime(0, 0, 0, 2, 1, 2010);
$fecha = new Zend_Date($fecha_febrero);
$fecha-&gt;setMonth(3);
$fecha-&gt;setDay(31);
print "Fecha Marzo: ".date("d/m/Y", $fecha-&gt;getTimestamp());</pre>
<p>Eso parecería la solución ideal (y es la recomendada por la documentación), pero no es totalmente cierta, si por ejemplo estamos en enero 31 del 2010 e intentamos asignar una fecha de febrero, siempre obtendremos una fecha de marzo:</p>
<pre name="code" class="php">include_once 'Zend/Date.php';
$fecha_enero = mktime(0, 0, 0, 1, 31, 2010);
$fecha = new Zend_Date($fecha_enero);
$fecha-&gt;setMonth(2);
$fecha-&gt;setDay(10);
print "Fecha Febrero: ".date("d/m/Y", $fecha-&gt;getTimestamp());</pre>
<p>Uno esperaría que mostrara la fecha:<br />
<strong>10/02/2010</strong></p>
<p>Pero en realidad muestra<br />
<strong>10/03/2010</strong></p>
<p>Esto porque cuando asignamos el mes, el sistema intenta crear la fecha 31/03/2010 que una vez más es incorrecta, por lo que crea la fecha 03/03/2010, pero al asignar la fecha, el resultado es un error en el mes.</p>
<p>Aunque esto no es un error, es muy peligroso porque uno puede suponer que la fecha es correcta porque en el código lo parece, la solución sin embargo es trivial, asignar el primer día de un mes que tenga 31 días y asignar los valores en orden inverso (primero año, luego mes y por último día), así el código sin error sería:</p>
<pre name="code" class="php">
include_once 'Zend/Date.php';
$fecha = new Zend_Date(0, 0, 0, 1, 1, date('Y'));
$fecha-&gt;setMonth(2);
$fecha-&gt;setDay(10);
print "Fecha Febrero: ".date("d/m/Y", $fecha-&gt;getTimestamp());
</pre>
<p>Resultado (el esperado)<br />
<strong>10/02/2010</strong></p>
<p>Esto funciona en la mayoría de los casos; aunque si van a manejar diferentes zonas horarias mediante <code>setTimezone()</code> el código anterior tendrá el mismo error en los casos que la zona horaria sea menor a la zona horaria del servidor, por lo que hay que hacer algo más elaborado:</p>
<pre name="code" class="php">
include_once 'Zend/Date.php';
$fecha = new Zend_Date();
//eliminar "errores"
$fecha-&gt;setTimezone('mi_codigo_zona_horaria');
$fecha-&gt;setDay(1);
$fecha-&gt;setMonth(1);

//nuestro código
$fecha-&gt;setMonth(2);
$fecha-&gt;setDay(10);
print "Fecha Febrero: ".date("d/m/Y", $fecha-&gt;getTimestamp());
</pre>
<p>Lo peligroso del comportamiento de Zend_Date es que funcionará con errores durante algunos días/meses dependiendo de la manera en como se programe y es un poco difícil de diagnosticar por su misma naturaleza, con esto se elimina cualquier tipo de errores.</p>
]]></content:encoded>
			<wfw:commentRss>http://vida.danguer.com/2010/01/30/cuidado-al-usar-zend_date/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Tip Rápido: Completación de código en Zend Studio</title>
		<link>http://vida.danguer.com/2009/03/31/tip-rapido-completacion-de-codigo-en-zend-studio/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=tip-rapido-completacion-de-codigo-en-zend-studio</link>
		<comments>http://vida.danguer.com/2009/03/31/tip-rapido-completacion-de-codigo-en-zend-studio/#comments</comments>
		<pubDate>Tue, 31 Mar 2009 23:08:23 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Studio]]></category>

		<guid isPermaLink="false">http://vida.danguer.com/?p=93</guid>
		<description><![CDATA[<a href="http://vida.danguer.com/2009/03/31/tip-rapido-completacion-de-codigo-en-zend-studio/" title="Tip Rápido: Completación de código en Zend Studio"></a><p>Uso mucho el <a title="Zend Studio" href="http://www.zend.com/en/products/studio/" target="_blank">Zend Studio</a>, aunque la primera versión fue desastrosa a partir de la 6.0.1 se han esmerado muchísimo; lo único malo que me he topado es que al hacer un proyecto Zend Framework se vuelve terriblemente lento y que tiene un poco de código extra poco necesario en su plantilla; aunque la ventaja es que ya trae ejemplos de Unit Test Case para los modelos (falta probarlos a detalle =P)</p>
<p>Usualmente al construir mis clases no pongo mucha documentación por función, por tanto Zend Studio no puede &#8220;determinar&#8221; qué tipo de variable regresa esa función. </p>
<p>Para facilitar esto, lo que hago es simplemente agregar este pedazo de código:</p>
<pre name="code" class="php">
    /**
     * @return Zend_Db_Adapter_Abstract
     */
	public static function getDB() {
             /* ... aqui va el código */
         }
 </pre>
<p>Básicamente es el snippet de código para documentación pero usando solamente el tipo de objeto que regresa la función.<br />
Con esto cuando utilices la función, Zend Studio podrá fácilmente mostrarte las propiedades y métodos del objeto.</p>
]]></description>
			<content:encoded><![CDATA[<a href="http://vida.danguer.com/2009/03/31/tip-rapido-completacion-de-codigo-en-zend-studio/" title="Tip Rápido: Completación de código en Zend Studio"></a><p>Uso mucho el <a title="Zend Studio" href="http://www.zend.com/en/products/studio/" target="_blank">Zend Studio</a>, aunque la primera versión fue desastrosa a partir de la 6.0.1 se han esmerado muchísimo; lo único malo que me he topado es que al hacer un proyecto Zend Framework se vuelve terriblemente lento y que tiene un poco de código extra poco necesario en su plantilla; aunque la ventaja es que ya trae ejemplos de Unit Test Case para los modelos (falta probarlos a detalle =P)</p>
<p>Usualmente al construir mis clases no pongo mucha documentación por función, por tanto Zend Studio no puede &#8220;determinar&#8221; qué tipo de variable regresa esa función. </p>
<p>Para facilitar esto, lo que hago es simplemente agregar este pedazo de código:</p>
<pre name="code" class="php">
    /**
     * @return Zend_Db_Adapter_Abstract
     */
	public static function getDB() {
             /* ... aqui va el código */
         }
 </pre>
<p>Básicamente es el snippet de código para documentación pero usando solamente el tipo de objeto que regresa la función.<br />
Con esto cuando utilices la función, Zend Studio podrá fácilmente mostrarte las propiedades y métodos del objeto.</p>
]]></content:encoded>
			<wfw:commentRss>http://vida.danguer.com/2009/03/31/tip-rapido-completacion-de-codigo-en-zend-studio/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>TinyURL API &#8211; Snippet</title>
		<link>http://vida.danguer.com/2009/03/23/tinyurl-api-snippet/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=tinyurl-api-snippet</link>
		<comments>http://vida.danguer.com/2009/03/23/tinyurl-api-snippet/#comments</comments>
		<pubDate>Mon, 23 Mar 2009 18:39:44 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Snippet]]></category>

		<guid isPermaLink="false">http://vida.danguer.com/?p=92</guid>
		<description><![CDATA[<a href="http://vida.danguer.com/2009/03/23/tinyurl-api-snippet/" title="TinyURL API - Snippet"></a><p>Aqui hay un snippet (pedazo de código) para utilizar el API de TinyURL (TinyAPI <img src='http://vida.danguer.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  ), básicamente es llamar a la siguiente dirección:</p>
<pre name="code" class="php">

http://tinyurl.com/api-create.php?url= 
</pre>
<p>y agregar al final la URL que queremos &#8220;achicar&#8221; con TinyURL, la página regresará como único contenido la URL nueva, por ejemplo:<br />
<a href="http://tinyurl.com/api-create.php?url=http://vida.danguer.com">http://tinyurl.com/api-create.php?url=http://vida.danguer.com</a></p>
<p>Da como resultado:<br />
<a href="http://tinyurl.com/dfz9c3">http://tinyurl.com/dfz9c3</a></p>
<p>El snippet para PHP es el siguiente: </p>
<pre name="code" class="php">
function get_tinyurl($url) {
     $url = "http://tinyurl.com/api-create.php?url=".urlencode($url);
     return trim(file_get_contents($url));
}
</pre>
<p> </p>
<p>Demasiado facil, no? solamente hay que invocarlo como:</p>
<pre name="code" class="php">
get_tinyurl('http://vida.danguer.com')
</pre>
<p>y obtendremos la nueva URL.</p>
]]></description>
			<content:encoded><![CDATA[<a href="http://vida.danguer.com/2009/03/23/tinyurl-api-snippet/" title="TinyURL API - Snippet"></a><p>Aqui hay un snippet (pedazo de código) para utilizar el API de TinyURL (TinyAPI <img src='http://vida.danguer.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  ), básicamente es llamar a la siguiente dirección:</p>
<pre name="code" class="php">

http://tinyurl.com/api-create.php?url= 
</pre>
<p>y agregar al final la URL que queremos &#8220;achicar&#8221; con TinyURL, la página regresará como único contenido la URL nueva, por ejemplo:<br />
<a href="http://tinyurl.com/api-create.php?url=http://vida.danguer.com">http://tinyurl.com/api-create.php?url=http://vida.danguer.com</a></p>
<p>Da como resultado:<br />
<a href="http://tinyurl.com/dfz9c3">http://tinyurl.com/dfz9c3</a></p>
<p>El snippet para PHP es el siguiente: </p>
<pre name="code" class="php">
function get_tinyurl($url) {
     $url = "http://tinyurl.com/api-create.php?url=".urlencode($url);
     return trim(file_get_contents($url));
}
</pre>
<p> </p>
<p>Demasiado facil, no? solamente hay que invocarlo como:</p>
<pre name="code" class="php">
get_tinyurl('http://vida.danguer.com')
</pre>
<p>y obtendremos la nueva URL.</p>
]]></content:encoded>
			<wfw:commentRss>http://vida.danguer.com/2009/03/23/tinyurl-api-snippet/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Obtener video FLV de YouTube</title>
		<link>http://vida.danguer.com/2009/01/09/obtener-video-flv-de-youtube/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=obtener-video-flv-de-youtube</link>
		<comments>http://vida.danguer.com/2009/01/09/obtener-video-flv-de-youtube/#comments</comments>
		<pubDate>Fri, 09 Jan 2009 21:02:59 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[FLV]]></category>
		<category><![CDATA[YouTube]]></category>

		<guid isPermaLink="false">http://vida.danguer.com/?p=89</guid>
		<description><![CDATA[<a href="http://vida.danguer.com/2009/01/09/obtener-video-flv-de-youtube/" title="Obtener video FLV de YouTube"></a><h3>Actualización</h3>
<p>Debido a que este código no funciona, el nuevo código (aunque el formato del video ya no es FLV, sino WEBM) puede consultarse aquí: <a href="http://vida.danguer.com/2011/10/20/obtener-video-webm-de-youtube-antes-flv-en-php/">http://vida.danguer.com/2011/10/20/obtener-video-webm-de-youtube-antes-flv-en-php/</a></p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>Esta es la nueva manera de obtener el video de YouTube, los parámetros para <code>get_video.php</code> quedan iguales (<code>video_id</code> y <code>t</code>), pero <code>t</code> cambia y ahora no se obtiene al cargar el video, se tiene que llamar a otra URL para obtener esa información.</p>
<p>El método es muy fácil, los pasos son:</p>
<ol>
<li>Llamar a: <br />
<code>http://www.youtube.com/get_video_info.php?video_id=VIDEOID</code><br />
Donde <code>VIDEOID</code> es el id del video que vamos a obtener el archivo <code>FLV</code> (las ligas de YouTube para ver el video son como: <code>http://www.youtube.com/v/VIDEOID</code>)<br />
Tomaremos por ejemplo este:<br />
<code>http://www.youtube.com/get_video_info.php?video_id=HDi9OeJqwG4</code> </li>
<li>El resultado será en texto y tendrá muchas variables como:
<pre name="code" class="php">status=ok&#38;title=Rob+Dougan+-+Furious+Angels&#38;muted=0&#38;avg_rating=4.8496592845&#38;creator=eitch&#38;length_seconds=237&#38;vq=None&#38;fmt_map=&#38;token=OEgsToPDskKxBcum6P4BEStM-z3qkYZW&#38;thumbnail_url=http%3A%2F%2Fi1.ytimg.com%2Fvi%2FHDi9OeJqwG4%2Fdefault.jpg&#38;allow_ratings=True&#38;plid=AARgErHUJPLX2RPWAAAAoABoIAE&#38;track_embed=1</pre>
</li>
<li>De aqui solamente necesitamos obtener el parámetro <code>token</code> y pasarlo al ya conocido <code>get_video.php</code>:<br />
<code>http://www.youtube.com/get_video.php?video_id=HDi9OeJqwG4&#38;t=OEgsToPDskKxBcum6P4BEStM-z3qkYZW</code></li>
</ol>
<p>Con esto ya obtendremos el FLV deseado, en PHP la rutina sería la siguiente:</p>
<pre name="code" class="php">&#60;?php
//video id por defecto
$video_id = 'HDi9OeJqwG4';

if (isset($_REQUEST['video_id']))
        $video_id = trim($_REQUEST['video_id']);

//obtener informacion
$url_info = 'http://www.youtube.com/get_video_info.php?video_id='.$video_id;
$info = file_get_contents($url_info);

$vars = array();
parse_str($info, $vars);

$url_flv = 'http://www.youtube.com/get_video.php?video_id='.$video_id.'&#38;t='.$var
s['token'];

print 'Archivo FLV: '.$url_flv;
?&#62;</pre>
<p>Con esto obtenemos la URL del archivo <code>FLV</code>.</p>
<p>Es importante notar que en Flex/Flash no se podrá cargar diréctamente <code>get_video_info.php</code> por la política del <code>crossdomain</code> de YouTube así que hay que utilizar un proxy.</p>
<p><del datetime="2011-10-20T17:01:28+00:00">El demo lo pueden probar desde:<br />
<a href="http://demo.livesourcing.com/blog_vida/articulos/DanguerArticle_Youtube/">http://demo.livesourcing.com/blog_vida/articulos/DanguerArticle_Youtube/?video_id=VIDEOID</a><br />
Donde VIDEOID es el id del video de YouTube de su preferencia =)</del></p>
<p>El código fuente lo pueden descargar de:<br />
<a href="https://github.com/danguer/blog-examples/blob/master/php/youtube/get-flv.php">https://github.com/danguer/blog-examples/blob/master/php/youtube/get-flv.php</a><br />
 </p>
<p> </p>
<p> </p>
<p> </p>
]]></description>
			<content:encoded><![CDATA[<a href="http://vida.danguer.com/2009/01/09/obtener-video-flv-de-youtube/" title="Obtener video FLV de YouTube"></a><h3>Actualización</h3>
<p>Debido a que este código no funciona, el nuevo código (aunque el formato del video ya no es FLV, sino WEBM) puede consultarse aquí: <a href="http://vida.danguer.com/2011/10/20/obtener-video-webm-de-youtube-antes-flv-en-php/">http://vida.danguer.com/2011/10/20/obtener-video-webm-de-youtube-antes-flv-en-php/</a></p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>Esta es la nueva manera de obtener el video de YouTube, los parámetros para <code>get_video.php</code> quedan iguales (<code>video_id</code> y <code>t</code>), pero <code>t</code> cambia y ahora no se obtiene al cargar el video, se tiene que llamar a otra URL para obtener esa información.</p>
<p>El método es muy fácil, los pasos son:</p>
<ol>
<li>Llamar a: <br />
<code>http://www.youtube.com/get_video_info.php?video_id=VIDEOID</code><br />
Donde <code>VIDEOID</code> es el id del video que vamos a obtener el archivo <code>FLV</code> (las ligas de YouTube para ver el video son como: <code>http://www.youtube.com/v/VIDEOID</code>)<br />
Tomaremos por ejemplo este:<br />
<code>http://www.youtube.com/get_video_info.php?video_id=HDi9OeJqwG4</code> </li>
<li>El resultado será en texto y tendrá muchas variables como:
<pre name="code" class="php">status=ok&amp;title=Rob+Dougan+-+Furious+Angels&amp;muted=0&amp;avg_rating=4.8496592845&amp;creator=eitch&amp;length_seconds=237&amp;vq=None&amp;fmt_map=&amp;token=OEgsToPDskKxBcum6P4BEStM-z3qkYZW&amp;thumbnail_url=http%3A%2F%2Fi1.ytimg.com%2Fvi%2FHDi9OeJqwG4%2Fdefault.jpg&amp;allow_ratings=True&amp;plid=AARgErHUJPLX2RPWAAAAoABoIAE&amp;track_embed=1</pre>
</li>
<li>De aqui solamente necesitamos obtener el parámetro <code>token</code> y pasarlo al ya conocido <code>get_video.php</code>:<br />
<code>http://www.youtube.com/get_video.php?video_id=HDi9OeJqwG4&amp;t=OEgsToPDskKxBcum6P4BEStM-z3qkYZW</code></li>
</ol>
<p>Con esto ya obtendremos el FLV deseado, en PHP la rutina sería la siguiente:</p>
<pre name="code" class="php">&lt;?php
//video id por defecto
$video_id = 'HDi9OeJqwG4';

if (isset($_REQUEST['video_id']))
        $video_id = trim($_REQUEST['video_id']);

//obtener informacion
$url_info = 'http://www.youtube.com/get_video_info.php?video_id='.$video_id;
$info = file_get_contents($url_info);

$vars = array();
parse_str($info, $vars);

$url_flv = 'http://www.youtube.com/get_video.php?video_id='.$video_id.'&amp;t='.$var
s['token'];

print 'Archivo FLV: '.$url_flv;
?&gt;</pre>
<p>Con esto obtenemos la URL del archivo <code>FLV</code>.</p>
<p>Es importante notar que en Flex/Flash no se podrá cargar diréctamente <code>get_video_info.php</code> por la política del <code>crossdomain</code> de YouTube así que hay que utilizar un proxy.</p>
<p><del datetime="2011-10-20T17:01:28+00:00">El demo lo pueden probar desde:<br />
<a href="http://demo.livesourcing.com/blog_vida/articulos/DanguerArticle_Youtube/">http://demo.livesourcing.com/blog_vida/articulos/DanguerArticle_Youtube/?video_id=VIDEOID</a><br />
Donde VIDEOID es el id del video de YouTube de su preferencia =)</del></p>
<p>El código fuente lo pueden descargar de:<br />
<a href="https://github.com/danguer/blog-examples/blob/master/php/youtube/get-flv.php">https://github.com/danguer/blog-examples/blob/master/php/youtube/get-flv.php</a><br />
 </p>
<p> </p>
<p> </p>
<p> </p>
]]></content:encoded>
			<wfw:commentRss>http://vida.danguer.com/2009/01/09/obtener-video-flv-de-youtube/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
	</channel>
</rss>

