Executando comandos unix pelo PHP

Eu apliquei um sistema unificado de cache no And After, nos blogs do site e na loja Eu Compraria, este sistema tornou o Gir (nosos querido escravo-servidor) muito mais eficiente na realização de suas tarefas.

O sistema de cache utiliza o Code Igniter com memcached e cache de arquivo físico. Isso otimizou muito o carregamento de todas as nossas páginas: "(1.002 Seconds), 70% of sites are slower: segundo a Alexa.

Além de adiantar a entrega das páginas, melhorando a vida dos leitores, reduziu drasticamente o consumo (e consequentemente o custo) de servidor e eliminou os problemas de performance com com o alto consumo de processamento do MySQL (saiba as diferenças entre innoDB e MyISAM).

Automatizando a "limpeza" de cache

A única dificuldade que isto acarretou foi o trabalho de realizar a limpeza de cache de arquivos físicos quando era necessário.

Até hoje eu fazia isso via ssh: logava no servidor e removia os arquivos necessários. Eu poderia implementar um sistema do Code Igniter para fazer isso, mas envolveria percorrer todos os registros do banco e eliminar o cache referente a cada objeto.

Um modo menos custoso e trabalhoso de fazer isso é automatizar a remoção dos arquivos físicos através do próprio PHP, e uma forma de fazer isso é com o comando shell_exec.

PHP shell_exec

O comando do PHP shell_exec permite você executar algum comando do terminal unix diretamente pelo PHP, retornando como uma string qualquer coisa que este comando imprima no terminal.

Atenção: este comando pode expor seu servidor a problemas de segurança.

No meu caso eu queria apagar os arquivos físicos de cache, um exemplo de sintaxe para o meu caso:

shell_exec('rm /var/www/andafter.org/htdocs/cache/publicacoes/*');

 

Vale lembrar que seu usuário www deve ter as permissões necessárias para executar os comandos. Abriu um novo leque de possibilidades do que é possível fazer com o PHP depois de conhecer este comando. 🙂

Pong em HTML5 Canvas

Recentemente eu escrevi um turorial de Canvas (HTML5) para iniciantes, que comecei a escrever para aprender Canvas.

Comecei a buscar formas divertidas de aprender canvas e como games em HTML5 me chamaram a atenção desde a primeira vez que li sobre o assunto, porque não aprender canvas desenvolvendo algum game? 🙂

É também uma forma de mostrar o que é possível com HTML5 e expandir as idéias para as aplicações que podem ser feitas com um pouco de criatividade.

Pong em Canvas

Vou explicar o desenvolvimento do Pong em HTML5.

Para facilitar a manutenção procurei desenvolver todo o game com orientação a objetos em quase tudo, então recomendo leitura sobre o assunto caso você não se sinta confortável com esse tipo de código.

O primeiro passo é criar a estrutura do game:

function Pong(){
    _this = this;
}

Todo o game é estruturado em basicamente 2 objetos de dados e algumas funções (métodos) auxiliares que fazem a manipulação dos dados, animação e regras do jogo.

 

Os objetos do game

Players

Cada player é um objeto onde é armazenado as informações do jogador, como pontuação, id, localização do seu "pad" e regra de movimentação (se ele deve ir para a direita ou esquerda).

this.player = function(){

    this.score = 0,
    this.left = false,
    this.right = false;    

    this.paddle = {
        x : 0,
        y : (_this.HEIGHT / 2)-60,
        w : 7,
        h : 120
    };

}

 

 

Bola

A bola é outro objeto do game, nela ficam armazenados sua posição (x,y) e o raio utilizado para o desenho.

this.bola = {
      x : 8,
      y : 50,
      r : 6
}

 

Métodos do Pong

Estruturei uma série de funções para facilitar o desenho e aplicação das regras do game. Acredito que o código ficou bastante legível e você pode ler ele diretamente no game.

 

init()

Chamada no início do jogo esta função define o contexto do canvas, os valores iniciais dos objetos (players, bola) e armazena também o tamanho do canvas, é utilizado para posicionar os pads e a bola. A função também cria os 2 jogadores e posiciona seus "pads" no local correto.

As variáveis "wind" e "gravity" são as duas forças que atuam na bola (horizontal e verticalmente).

 

                this.init = function(selector){

                    _this.ctx = $("#canvas")[0].getContext("2d"),

                    _this.WIDTH = $("#canvas")[0].width,

                    _this.HEIGHT = $("#canvas")[0].height,

                    _this.gravity = 4,

                    _this.wind = 10,

                    _this.p1 = new _this.player(),

                    _this.p1.id = 1,

                    _this.p2 = new _this.player(),

                    _this.p2.id = 2,

                    _this.p2.paddle.x = (_this.WIDTH-7);

                    

                    $(document).keydown(_this.onKeyDown);

                    $(document).keyup(_this.onKeyUp);

                    

                }

 

 

 
_this.ctx = $("#canvas")[0].getContext("2d"),
 
_this.WIDTH = $("#canvas")[0].width,
 
_this.HEIGHT = $("#canvas")[0].height,
 
_this.gravity = 4,
 
_this.wind = 10,
 
_this.p1 = new _this.player(),
 
_this.p1.id = 1,
 
_this.p2 = new _this.player(),
 
_this.p2.id = 2,
 
_this.p2.paddle.x = (_this.WIDTH-7);
 
 
 
$(document).keydown(_this.onKeyDown);
 
$(document).keyup(_this.onKe

Funções de desenho

Os métodos circle, rect e clean são apenas atalhos para os métodos de desenho do Canvas, utilizados para a cada frame redesenhar a bola e os pads em suas novas posições.

 

                this.circle = function(x,y,r){

                    _this.ctx.fillStyle = "rgba(0,0,255,.9)";

                    _this.ctx.beginPath();

                    _this.ctx.arc(x, y, r, 0, Math.PI*2, true);

                    _this.ctx.closePath();

                    _this.ctx.fill();

                }

                

                this.rect = function(x,y,w,h){

                    _this.ctx.fillStyle = "rgba(255,0,0,.6)";

                    _this.ctx.beginPath();

                    _this.ctx.rect(x,y,w,h);

                    _this.ctx.closePath();

                    _this.ctx.fill();

                }

                

                this.clear = function(){

                    _this.ctx.clearRect(0, 0, _this.WIDTH,_this.HEIGHT);

                }

 

 

Interação com teclado

Como fazer um objeto se movimentar no canvas utilizando o teclado? Na função init do game defini que a ação "keyup" e "keydown" do jQuery iriam disparar um método do game. 

A cada ação de Keydown é feito uma verificação de qual tecla foi pressionada. Se a tecla pressionada foi a direcional esquerda ou a direcional direita (veja a tabela de keycodes para javascript) o objeto do player 1 é alterado, modificando a flag "left" ou a flag "right" para true.

O Keydown faz a ação inversa, tornando as flags "false" dependendo de qual tecla foi liberada do teclado.

 

 

_this.onKeyDown = function(evt) {

    switch(evt.keyCode){

        /* seta direita */

        case 39:

            _this.p1.right = true;

            break;

        /* seta esquerda */

        case 37:

            _this.p1.left = true;

            break;

        /* espaco */

        case 32:

            _this.play();

            break;

    }

  

}

 

/* Keyup events */

_this.onKeyUp = function(evt) {

    switch(evt.keyCode){

        /* seta direita */

        case 39:

            _this.p1.right = false;

            break;

        /* seta esquerda */

        case 37:

            _this.p1.left = false;

            break;

    }

}

 

 

 

As regras e animação do jogo

Neste ponto nós já temos os objetos e players, bola, e todas as informações do canvas e forças que vão atuar no nosso jogo. Também temos os controles configurados, ao pressionar as teclas nosso objeto player é alterado.

Agora precisamos "dar vida" ao game, para isso iremos redesenhar o nosso canvas a cada 25 milisegundos. Primeiro vamos a lógica:

A cada 25 milisegundos iremos:

  1. Calcular e desenhar a bola em sua nova posição (de acordo com os valores de wind e gravity, que são as forças que atuam na bolinha)
  2. Verificar se o pad do player 1 deve ir para cima ou para baixo (variáveis left e right indicando que as teclas estão pressionadas)

    1. Em caso positivo, desenhar o pad na nova posição
  3. Se a bolinha bater em cima ou em baixo do canvas, inverter o valor de "gravity" para que a bolinha continue dentro do nosso canvas
  4. Verificar se é gol (se a bola acertou a lateral esquerda ou direita do canvas sem atingir os pads)

 

Estas são as regras do game convertidas para "bom português", agora vamos para o código:

 

this.animate = function (){

    /* limpa e redesenha a bola */

    _this.clear();

    _this.circle(_this.bola.x,_this.bola.y,_this.bola.r);

    

    var speed = 8;

    

    /* player 1 */

    if (_this.p1.right && _this.p1.paddle.y < (_this.HEIGHT – _this.p1.paddle.h)) _this.p1.paddle.y += speed;

    else if (_this.p1.left && _this.p1.paddle.y > 0) _this.p1.paddle.y -= speed;

    _this.rect(_this.p1.paddle.x,_this.p1.paddle.y,_this.p1.paddle.w,_this.p1.paddle.h);

    

    /* saida lateral */

    if (_this.bola.y + _this.gravity > _this.HEIGHT || _this.bola.y + _this.gravity < 0){

        _this.gravity = -_this.gravity;

    }

    

    if(_this.bola.x + _this.wind> _this.WIDTH){

        if(_this.bola.y > _this.p2.paddle.y && _this.bola.y < (_this.p2.paddle.y + _this.p2.paddle.h)){

           _this.wind = -_this.wind;

           _this.gravity = _this.randomize();

        }else{

           _this.p1.score++;

           $('#score-1').html(_this.p1.score);

            _this.bola.x = (_this.WIDTH / 2),

            _this.bola.y = (_this.HEIGHT / 2);

            _this.wind = -_this.wind;

           _this.play();

        }

    }else if(_this.bola.x + _this.wind < 0){

        if(_this.bola.y > _this.p1.paddle.y && _this.bola.y < (_this.p1.paddle.y + _this.p1.paddle.h)){

           _this.wind = -_this.wind;

           _this.gravity = _this.randomize();

        }else{

           _this.p2.score++;

           $('#score-2').html(_this.p2.score);

            _this.bola.x = (_this.WIDTH / 2),

            _this.bola.y = (_this.HEIGHT / 2);

            _this.wind = -_this.wind;

           _this.play();

        }

    }

    

    

    _this.bola.y += _this.gravity;

    _this.bola.x += _this.wind;

}

 

O código acima também já está marcando a pontuação e manipulando o HTML para exibição dos pontos. Retirei a parte do player 2 para facilitar, ele é um bot e está explicado abaixo:

 

Player 2 (NPC)

Ainda não trabalhei muito no bot, estava ansioso para compartilhar e colocar o Pong no ar e não consegui deixar exatamente como eu gostaria. Ele "persegue" a bolinha a partir de certo ponto da nossa quadra (do elemento canvas).

 

    /* bot */

    var delay = 300;

    if(_this.bola.y < (_this.p2.paddle.y + (_this.p2.paddle.h / 3)) & _this.p2.paddle.y > 0 && _this.bola.x > delay){

        _this.p2.paddle.y -= speed;

    }else if(_this.bola.y > (_this.p2.paddle.y – (_this.p2.paddle.h / 3)) && _this.p2.paddle.y < (_this.HEIGHT – _this.p2.paddle.h) && _this.bola.x > delay){

        _this.p2.paddle.y += speed;

    }

    _this.rect(_this.p2.paddle.x,_this.p2.paddle.y,_this.p2.paddle.w,_this.p2.paddle.h);

 

 

Espero que este post desperte curiosidade e idéias! Sugestões, críticas e dúvidas nos comentários.

HTML5 Canvas – Tutorial para iniciantes

O que é Canvas HTML5?

 HTML5 trouxe diversas novas tags, além das novidades nas tags input de formulário e das tags semânticas como article e section, uma das grandes mudanças implementadas foi a tag canvas.

Canvas é uma nova tag que permite você trabalhar e manipular elementos gráficos (raster). A tag canvas é um "board" de desenho no HTML, nele você pode desenhar linhas, elementos, manipular pixel a pixel, carregar e manipular imagens externas (rotacionar, alterar cor, brilho, etc.).

É uma evolução gigante pois permite a manipulação em tempo real do que está sendo impresso como imagem no computador do cliente.

Um exemplo de uso são jogos, gráficos e interfaces ainda mais interativas.

 

Quais navegadores dão suporte ao Canvas?

Praticamente todos os navegadores modernos dão suporte ao Canvas. Confira a tabela abaixo:

 

  IE Firefox Chrome
14 versions back     4.0: Supported
13 versions back     5.0: Supported
12 versions back   2.0: Supported 6.0: Supported
11 versions back   3.0: Supported 7.0: Supported
10 versions back   3.5: Supported 8.0: Supported
9 versions back   3.6: Supported 9.0: Supported
8 versions back   4.0: Supported 10.0: Supported
7 versions back   5.0: Supported 11.0: Supported
6 versions back   6.0: Supported 12.0: Supported
5 versions back   7.0: Supported 13.0: Supported
4 versions back 5.5: Not supported 8.0: Supported 14.0: Supported
3 versions back 6.0: Not supported (but has polyfill available) 9.0: Supported 15.0: Supported
2 versions back 7.0: Not supported (but has polyfill available) 10.0: Supported 16.0: Supported
Previous version 8.0: Not supported (but has polyfill available) 11.0: Supported 17.0: Supported 3.0: Supported
Current 9.0: Supported 12.0: Supported 18.0: Supported
Near future 10.0: Supported 13.0: Supported 19.0: Supported
Farther future   14.0: Supported 20.0: Supported

Veja a tabela completa e atualizada no Can I Use

 

Entendendo melhor o canvas

Neste tutorial inicial vou explicar alguns métodos do canvas e seu funcionamento. O elemento canvas é todo manipulado por javascript, e trabalha com contextos. Pelo javascript você terá acesso a diversos métodos para desenhar nesta tela, no contexto 2D você utiliza um plano cartesiano (x e y) para definir a posição de onde vai trabalhar, e através de métodos específicos pode traçar formas, linhas e definir tamanhos e cores utilizadas.

Para fazer uma animação por exemplo, você faz o seu primeiro desenho e através do javascript define um time que irá limpar o seu contexto e redesenhar os objetos em sua nova posição. Isso também server para jogos em HTML5 ou interação com o usuário: quando acontecer a interaçãovocê limpa o contexto e redesenha sua cena com os objetos manipulados em suas novas posições.

 

Vamos a prática!

Programando para Canvas

Coloque o elemento canvas no seu HTML e use um identificador para facilitar a manipulação do javascript.

<canvas id="myCanvas"></canvas>

 

Vamos agora selecionar o nosso canvas e definir o contexto em que iremos trabalhar, aqui já entra o javascript:

window.onload = function() {
  var canvas = document.getElementById("myCanvas");
  var context = canvas.getContext("2d");
};

 

Isso defina que vamos trabalhar com o contexto 2D do canvas.

 

Desenhando uma linha com Canvas

Agora que você já tem o seu contexto definido, vamos desenhar uma linha no nosso gráfico, no javascript:

var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
context.beginPath();
context.moveTo(100, 150); //define o ponto inicial do desenho
context.lineTo(450, 50); //define a posição de in
context.lineWidth = 5; // define a largura da linha
context.strokeStyle = "#ff0000"; //define a cor da linha
context.stroke();

 

Pronto, temos uma linha que começa no ponto (100,150) e vai até (450,50).

Desenhando um retângulo com canvas

Agora vamos desenhar um retângulo em nosso canvas, para isso no javascript temos o método rect, que funciona da seguinte forma:

context.rect(x, y, width, height);

O exemplo do código completo:

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.beginPath();
context.rect(188, 50, 200, 100); // desenha o retângulo
context.fillStyle = '#8ED6FF'; // define o preenchimento do retângulo
context.fill(); // Preenche o retângulo
context.lineWidth = 5; // define a largura da linha do contorno
context.strokeStyle = 'black'; // define a cor do contorno
context.stroke(); // desenha o contorno

 

Desenhando um círculo com canvas

Para desenhar círculos (e arcos) temos o método arc, que funciona da seguinte forma:

context.arc(x, y, radius, startAngle, endAngle, antiClockwise);

No exemplo acima podemos desenhar arco com qualquer angulo de início e fim, para desenhar círculos completos podemos usar o seguinte:

context.arc(x, y, radius, 0 , 2 * Math.PI, false);

Fica mais fácil e temos que nos preocupar apenas com o ponto cartesiano (x,y) e com o tamanho do raio do círculo. Abaixo um exemplo completo:

var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var centerX = canvas.width / 2; // pega o centro do canvas (x)
var centerY = canvas.height / 2;  // pega o centro do canvas (y)
var radius = 70; 

context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false); // desenha o círculo no centro do canvas com raio = 70
context.fillStyle = "#8ED6FF"; // define a cor de preenchimento
context.fill(); // desenha o preenchimento
context.lineWidth = 5; // define a expessura da borda
context.strokeStyle = "black"; // define a cor da borda
context.stroke(); // desenha a borda

 

Esse é um tutorial introdutório ao Canvas, ele parece mais complicado do que realmente é. Para entender um pouco melhor o funcionamento do canvas e suas animações criei um Pong em HTML5 Canvas, mas ainda não tive tempo de finalizar. O código está com a leitura fácil, vale a pena a leitura. 😉

 

Recomendo a leitura (em inglês) do html5 canvas tutorials, de onde os exemplos deste post foram retirados.

 

Este post te ajudou de alguma forma? Retribua, divulgue o link deste artigo em seu blog, twitter ou facebook e nos ajude a compartilhar conhecimento.

Programador especialista – a inércia em TI

Resolvi escrever sobre generalismo e especialização em desenvolvimento pois acho que é um debate interessante e os dois lados tem seus prós e contras. Meu perfil de gostar de aprender e desenvolver coisas diferentes me faz gostar mais do generalismo: me formei em design, sou desenvolvedor front-end no iG e gosto muito de desenvolvimento back-end, que exercito no And After, Eu Compraria e outros projetos pessoais.

Desde que entrei no iG estou em contato direto com NodeJS (leia como instalar o NodeJS no Ubuntu). Já tinha ouvido falar, tinha achado esquisito o conceito de javascript no back-end mas nunca tinha trabalhado com o Node, apesar da curiosidade.

Fiquei curioso e resolvi, antes de sair escrevendo código e aprender "na marra", me aprofundar mais em NodeJS, para isso corri para os tutoriais e li alguns ótimos livros:

  • What is Node?
  • The Node Beginner Book
  • Hands on NodeJS
  • Mastering NodeJS

Não terminei de ler todos os livros ainda, e também não fiz nenhuma aplicação em node, mas os livros me despertaram a curiosidade do debate desenvolvedor generalista vs desenvolvedor especialista. 

O Daniel Sperb já escreveu sobre formação generalista no design, e resolvi iniciar o debate no desenvolveimtno, o que é melhor: conhecer de forma um pouco mais rasa diversas linguagens e métodos ou se especializar em uma única linguagem ou método?

Brett defende a idéia (e me convenceu disso) de que é melhor conhecer diversas metodologias, linguagens e frameworks do que ser um especialista em uma única linguagem, método ou framework. Segue uma citação do livro:

"It's simply this: use different solutions for different problems. Even better, use the right solution for a particular problem"

É simplesmente isso: use diferentes soluções para diferentes problemas. Ainda melhor, use a solução certa para um problema específico.

Brett McLaughin, What is Node?

Eu comecei programando em PHP, fui para o ASP por causa de um emprego, voltei a programar PHP e aprendi o framework Code Igniter (que eu adoro) e graças ao Well estou aprendendo CakePHP agora, um outro framework PHP. Também estou estudando NodeJS, mas na verdade no iG sou desenvolvedor front-end: HTML5, CSS e javascript (com jQuery).

Especialista: tão focado em uma solução que pode não
visualizar nada em sua visão periférica.

Não defendo a idéia de apenas "arranhar" em uma dúzia de linguagens e não saber nenhuma direito, mas acho muito válido ter a mente aberta a novas tecnologias e idéias se aprofundar um pouco e dominar o básico de linguagem, e aí partir para fazer a mesma coisa em outra (ou em outro framework). Sei qunão é possível (e nem deve ser saudável) fazer isso com todas ou a maioria das linguagens, mas com algumas.

No CodeIgniter hoje eu me sinto a vontade para desenvolver qualquer coisa – mas o Well me apresentou algumas facilidades do Cake (bake e outras artimanhas para preguiçosos) que não sei se existe no CI, e essa é uma forma de "abrir a cabeça"  a novas idéias, talvez meu próximo projeto seja em Cake e não em CI, porque não?

A inércia no mundo de TI

A verdade é que, no geral, somos preguiçosos e reagimos negativamente a mudanças. Quem é empreendedor não pode agir desta forma, e os profissionais pró-ativos também não deveriam. As mudanças também são oportunidades: estar a frente tecnologicamente, aprender novas tecnologias, se promover, reduzir custos. 

A mudança pode trazer muitos benefícios, mas para isso você deve combater a inércia de deixar de fazer as coisas exatamente do mesmo modo que você fazia antes.

PHP resolve tudo? Talvez, com uma gambiarra aqui ou ali. E isso serve para qualquer outra linguagem, sei que um especialista faz o que quiser com a linguagem que domina, talvez com alguma deselegância no código aqui ou ali, mas será funcional.

"There's a certain inertia in not just web design, but all of programming. That inertia can be stated axiomatically like this: the more you learn, and become good at a certain approach or technique or language, the likely are you to use that (…) but this inertia often causes you to use a tool because you know ir, rather than beacause it's the right tool".

Brett McLaughin em What is Node?

É, de certa forma, natural esta inércia citada pelo Brett, mas é o motivo pelo qual as gambiarras acontecem.

 

Dificuldades do generalismo

Acho que o desenvolvedor generalista ainda tende a ser um pouco menos valorizado do que o especialista na "avaliação padrão" de junior, pleno e senior que o mercado faz. Não sei o quanto é valorizado alguem que domine diversas linguagens e metodologias e que não queira partir para a burocracia gerencial e queira continuar com a "mão na massa".

Outra dificuldade que vejo para o generalismo é se manter atualizado com as diversas tecnologias. Estudar e estar familiarizado com uma tecnologia é relativamente fácil: ler alguns livros, escrever algum código para testar e montar um projeto. Em menos de um ano, se você tem alguma experiência com outras linguagens, você já "se dá bem" com uma linguagem ou framework, mas e se manter atualizado em duas ou três linguagens? Acho que isso requer um pouco mais de estudo do que um programador que só le os últimos releases do PHP e do MySQL, por exemplo (com todo respeitos aos especialistas que se revoltarão com este texto).

Vantagens do especialista

Neste post explorei as vantagens que vejo no generalismo, quando você precisar de chegar em um ponto de conhecimento que o generalismo não proporcionou, você certamente vai penar um pouco mais que o especialista – e se isso se tornar constante em um projeto talvez seja hora de rever se não compensa contratar um especialista, no caso de não existir outra ferramenta (linguagem, framework, etc) que você domine que sirva para resolver o seu problema.

Sei que o especialista também tem suas vantagens sobre o generalista. Provavelmente a qualidade de código será um pouquinho melhor, talvez ele saiba explorar melhor ferramentas e bibliotecas específicas para cada situação, resultando em uma performance mais avançada, ou numa economia de servidor.

Para situações muito específicas ou de extrema performance, provavelmente um especialista também saiba como explorar melhor de forma exaustiva uma linguagem.

 

Quero a opinião dos leitores, você prefere ser um especialista ou generalista? E para contratar, acha mais vantagem contratar um especialista ou generalista? Porque?

 

Leia também:

PHP – Função com número de parâmetros indefinido

Para criar uma função que recebe um número variável de parâmetros precisaremos conhecer as funções built-in do PHP, func_num_args, func_get_args, e func_get_arg.

– func_num_args() – Retorna a quantidade de argumentos passados para a função
– func_get_args() – Retorna array de argumentos passados para a função
– func_get_arg(int $pos) – Retorna o argumento da posição informada
 
Exemplo:
 
function hello() {
	if (func_num_args() > 0) {
		$args = func_get_args();
		foreach($args as $arg){
			echo $arg;
		}
	} else {
		echo "Você não passou nenhum param";
	}
}
hello("andafter",".","org"); // Aparecerá "andafter.org"
hello(); // Aparecerá "Você não passou nenhum param"