Sobre mim

Trabalho com programação há cerca de 5 anos, sempre interessado nas linguagens WEB (PHP, Ruby, Java, JS, etc). Atualmente desenvolvo em PHP com o CodeIgniter, utilizando metodologias ágeis, na Fortes Informática.

Páginas

5 June 2009 - 17:28Múltiplas conexões no CodeIgniter: Cuidado!

Não estou escrevendo aqui para explicar como se utilizar múltiplas conexões no CodeIgniter, para isso, acredito que a documentação esteja suficientemente clara (http://codeigniter.com/user_guide/database/index.html).
Vim falar dos perigos de utilizar conexão a mais de um banco de dados no CI:

Recentemente, comecei a ter problemas em um portal de grande acesso. O servidor começava a ficar cada vez mais lento, até crashar com um erro do MySQL 1040 (too many connections [Muitas conexões]). Abri o “Process List” do MySQL para investigar (Você pode acessar via phpmyadmin, outras IDEs ou direto pela query “show processlist”).

Não foi difícil perceber o problema: Existiam dezenas ou centenas de conexões com tempos cada vez maiores e com o status “Sleep”.

Sleep significa que existe uma conexão com o banco, porém o usuário (no caso minha aplicação PHP) não está mandando nem recebendo nenhum dado, simplesmente está parado esperando que algo aconteça. Se você não utiliza conexão persistente (O que era meu caso), isso pode acontecer por uma série de razões:
- Alguma de suas páginas está demorando muito para ser carregada (Ou algo no código está gerando um loop infinito).
- Algum Web Service externo está muito lento ou não está online e não foi setado um timeout adequado.
- Existem muitas conexões ao Banco, enviando ou recebendo dados, o que gera uma fila de espera para as novas conexões que ficam aguardando.

De cara, a solução mais fácil (O que infelizmente não foi meu caso), é que eu estivesse utilizando conexão persistente nas configurações do meu banco. Para checar isso, basta abrir system/application/config/database.php e verificar se a configuração “pconnect” está setada para TRUE.

Nesse ponto alguns podem estar se perguntando: Opa, mas pera aí.. Conexão persistente não é aquela que é encerrada apenas explicitamente? Pois não sendo persistente, existe essa nota, direto da documentação do PHP:
Nota: A conexão com o servidor será fechada assim que a execução do script terminar, a menos que tenha sido fechada anteriormente usando-se explicitamente mysql_close().

Pois bem, as vezes essa nota não se faz verdadeira, e com certeza foi pensando nisso que desenvolveram o seguinte código no final do arquivo system/codeigniter/CodeIgniter.php:

if (class_exists('CI_DB') AND isset($CI->db))
{
	$CI->db->close();
}

“Garantindo” assim, que no final da execução de qualquer página, a conexão com o banco seja explicitamente encerrada.

Êpa!! agora complicou mais ainda né? Minha conexão não está persistente, e ainda assim o CI fecha a conexão ao término da execução do script. Como pode existirem conexões não encerradas?

Queria saber em que arquivo essas conexões não fechadas estavam sendo abertas. Então, modifiquei a função db_connect dentro de system/database/drivers/mysql/mysql_driver.php para o seguinte:

function db_connect()
{
    if ($this->port != '')
    {
        $this->hostname .= ':'.$this->port;
    }
 
    $conexao = @mysql_connect($this->hostname, $this->username, $this->password, TRUE);
 
    $msg = "[".mysql_thread_id($conexao)."] CONEXÃO INICIADA NO ENDEREÇO: ".$_SERVER['PHP_SELF']." PARA {$this->hostname}
    ";
 
    $fp = fopen("./system/application/public/conexao.txt", "a");
    fwrite($fp, $msg);
    fclose($fp);
 
    return $conexao;
}

Com essa mosificação, comecei a gravar um log em TXT com todas as conexões abertas pelo CodeIgniter, identificando o ID de conexão com o banco, o arquivo em que essa conexão foi iniciada e para qual host a conexão apontava. Monitorando novamente o “Proccess List” e agora o log, comparei o ID das conexões que estavam com status “Sleep” a muito tempo e cheguei ao Controller onde todas as conexões não fechadas estavam sendo abertas.

Esse Controller não tinha nenhuma consulta demorada e muito menos um loop infinito. A única particularidade dele é que abria conexão a mais de um banco:

$DB2 = $this->load->database('banco2', TRUE);

O segundo parâmetro, TRUE, identifica que o objeto de conexão será retornado, ao invés de substituído no $this->db. É aí que está o problema:
O CI cria uma nova conexão, mas não a encerra, pois a conexão encerrada ao fim do script, é a carregada dentro do $this->db.

Se você leu a função mostrada a pouco, deve ter reparado num quarto parâmetro no mysql_connect:

$conexao = @mysql_connect($this->hostname, $this->username, $this->password, TRUE);

Segundo a documentação do PHP:
Se uma segunda chamada é feita a mysql_connect() com os mesmos argumentos, não é estabelecida uma nova conexão, mas ao invés, o identificador da conexão que já esta aberta é retornado. O parâmetro new_link modifica este funcionamento e faz mysql_connect() sempre abrir uma nova conexão, mesmo que mysql_connect() seja chamado antes com os mesmos parâmetros.

Em geral, classes de database iniciam e fecham a conexão dentro da função query(). Sendo assim, não é necessário deixar esse parâmetro como TRUE, pois nunca teremos mais de uma conexão simultânea, por mais que utilizemos mais de um database.

Resultado:
O PHP realmente não garante que todas as conexões sejam fechadas ao término do script;
O CodeIgniter fecha manualmente a conexão. Porém, ao gerar uma nova conexão e retorná-la como objeto, essa não será fechada ao término da execução;

Solução:
Ideal mesmo seria o CodeIgniter gravar em um array, na sua instância, o resource de todas as conexões abertas durante o script. Para então fechar uma por uma ao término da execução. Mas enquanto ele não faz isso:

Não retorne objetos de conexão: Encerre a conexão atual, destrua o objeto $this-db e retire o parâmetro “TRUE” do método load->database()).
Sendo assim, carregue um database, faça as queries necessárias e recarregue o database default, caso ainda vá executar alguma query nele.

Exemplo:

$this->db->close();
$this->db = '';
$this->load->database('banco2');
 
$x = $this->db->query("alguma query em outro banco");
 
$this->db->close();
$this->db = '';
$this->load->database('default');
 
$y = $this->db->query("alguma query no banco default");

.

Ahhh, duas observações:
Criei uma solicitação no Bug Tracker do CodeIgniter sugerindo tal mudança.
Quanto mais mexo no core do CodeIgniter, mais me apaixono por esse framework ;)

Até a próxima!

3 Comments | Tags: CodeIgniter, Database, PHP

11 March 2009 - 12:33Integrando PHPUnit ao CodeIgniter

Irei mostrar como consegui rodar os testes unitários no framework CodeIgniter com o PHPUnit, através de um plugin chamado CIUnit.

Não irei abordar o porque de utilizar técnicas de Desenvolvimento Orientado a Testes, Extreme Programming, etc, e nem detalhar o funcionamento do PHPUnit, que é muito similar ao JUnit. Caso seja seu primeiro contato com essas tecnologias, indico a leitura desse post.

Em primeiro lugar, precisamos instalar o PHPUnit, para isso basta executar na linha de comando “pear install phpunit/PHPUnit”, ou instalar manualmente. Como disse, não vou detalhar a instalação do PHPUnit, pois creio que fuja do tema. Caso queira, siga os passos de instalação mostrados aqui.

Feito isso, vamos a instalação do CIUnit:

1. faca o download da ultima versão do CIUnit, neste link, e abra o arquivo. Iremos encontrar a seguinte estrutura dentro do ZIP baixado:
fooStack/
tests/
índex.html
CodeIgniter.php

2. Copie a pasta fooStack para dentro do seu diretorio SeuProjeto/system/application/config/

3. Copie a pasta tests para dentro do seu diretório SeuProjeto/system/application/

4. Substitua o arquivo CodeIgniter.php no diretório SeuProjeto/system/codeigniter/ pelo contido no ZIP

5. Abra o arquivo SeuProjeto/system/application/config/database.php e substitua a linha que começa com:

$active_group =

por:

$env_used = 'default'; //Onde “default” deve ser o nome da sua base de produção
if(defined('CIUnit_Version')){
  $env_used .= '_test';
}
$active_group = $env_used;

Essa modificação faz com que todos os seus testes sejam executados utilizando as configurações do database de tests ao invés do de produção. Perceba que todos os dados são deletados quando executamos um teste. Sendo assim, muito cuidado pra não esquecer esse passo, ou você poderá perder dados de produção.

6. No mesmo arquivo do passo 5, crie as configurações do seu database de testes, usando o mesmo nome colocado a cima (default_test, no nosso casso). Para isso você pode simplesmente copiar as configurações do seu database de produção e modificar apenas o nome do database. Para isso, adicione essas linhas ao fim do seu arquivo:

// Configurações do dabase de testes
$db['default_test'] = $db['default'];
$db['default_test']['database'] = "projeto_test";

7. Crie o novo database com o sufixo _test (projeto_test, por exemplo) contendo a mesma estrutura do seu database de produção, essa base, como foi dito, será usada pra popular seus testes quando estes acessarem um banco de dados.

Pronto! agora para executar os testes, abra a linha de comando e digite:

  • Para executar todos os testes: phpunit AllTests.php
  • Para executar um grupo de testes (Todos os models, por exemplo): phpunit ModelAllTests.php
  • Para executar um teste especifico: phpunit nomeDoTeste.php

Lembre-se que você precisa colocar o path na pasta onde o seu teste se encontra (Para testar todos os models, por exemplo, teríamos que colocar o path em: SeuProjeto/system/application/tests/models/).

Não se esqueça de adicionar o path do seu phpunit aos paths do Windows, para isso você pode executar na linha de comando:

SET PATH=%PATH%;C:\php\pear (Onde C:\php\pear deve ser o diretório onde encontra-se o seu phpunit.bat).

No Comments | Tags: CodeIgniter, Metodologias Ágeis, PHP

27 January 2009 - 16:22A trilha para tornar-se um ZCE

Salve todos,
Esse é o primeiro de muitos posts onde irei relatar, passo a passo, minha caminhada para conseguir a certificação de desenvolvedor PHP da Zend (O chamado ZCE). A medida que tiver novas informações sobre meus estudos, ou algum fato que considere relevante, venho aqui expor. Pretendo assim que esta série de posts sirva de “guia” para todos que desejem tirar a certificação. Vale lembrar que ainda não sou certificado e esses posts acontecerão ao longo do meu estudo. Sendo assim, não tenho como garantir que tal percurso funciona antes de eu mesmo fazer a prova, que pretendo realizar no meio de Março.

Em primeiro lugar, é fundamental pensar em qual material usaremos para estudar:
Estou usando como guia o livro “Zend PHP 5 Certification Study Guide“, vendido no site da zend em PDF ou conseguido facilmente pela internet.  O livro é dividido em áreas de conhecimento idênticas a da prova, isso facilita nossa estratégia de estudo (citada a baixo).

Além dele, o “The Zend PHP Certification Practice Test Book” é composto unicamente por questões baseadas na prova, ajudando no estudo. Cada capítulo (também divididos por áreas iguais aos da prova) contêm 15 questões, com exceção dos capítulos 1, 2, 5 e 6, que contêm 20 por serem os capítulos fundamentais no estudo.

Por último, e talvez ainda mais importante, recomendo fortemente a compra dos simulados da prova de certificação, eles seguem o mesmo modelo da prova, dando a noção exata de como são abordadas as questões, além do tempo de prova (90 minutos), quantidade de questões (70), tipo de resposta (múltipla escolha ou aberta) e etc. Realmente vale a pena pagar por pelo menos 5 simulados (Eu comprei os 10… hehehe). A Zend afirma que os simulados se aproximam tanto da prova quanto se é possível, além de trazer questões de nível mais complexo. Sendo assim, se você se der bem nos simulados, dificilmente terá problemas na prova.

Agora que já conhecemos o material para estudos, vamos para a estratégia. Sim!! estratégia… Talvez a arma mais importante para se tirar uma certificação seja realmente a estratégia. Sem ela não será possível organizar seus estudos e muito menos passar na prova.

A estratégia que estou adotando é baseada nas dicas do Fernando Chucre, que obteve sua certificação na segunda tentativa (Quando resolveu comprar os simulados). Ela parte do pre-suposto que você vai adquirir o material citado a cima.

Antes de mais nada, faça um simulado! Você pode pensar “Que?? mas eu nem comecei a estudar e já vou testar a prova?” Sim, começar fazendo o simulado é muito importante para deixar os pés no chão, além de já lhe dar uma idéia geral de como será a prova e portanto, como deve ser estudada. Após comprar os simulados, você receberá um link para realizar as provas, depois é só seguir as instruções.

No final da prova, você receberá uma tabela com cada área de conhecimento e sua pontuação (excellent, pass ou fail). A partir daí faremos nossa planilha que será crucial em nossa estratégia. Segue a baixo a foto de como fazer sua planilha:

tabela_certificado

Como vocês podem ver, fui horrível no meu primeiro simulado (realizado hoje), mas como eu disse, isso me fez por os pés no chão pra meter a cabeça nos estudos, pois eu considerava que já conhecia bastante PHP e esperava ir bem melhor no teste. Olhando pra tabela, podemos verificar todas as áreas de conhecimento cobradas na prova (para ver os detalhes de cada área cobrada, clique aqui). Para cada exame que realizar, irei preencher os valores correspondentes e assim dar continuidade a minha estratégia.

E voltando para ela… Agora que realizamos o primeiro exame e montamos nossa tabela, vamos iniciar a leitura do guia lendo o primeiro capítulo inteiro. Após terminar a leitura de cada capítulo, iremos ao livro prático realizar o teste das 15 (ou 20) questões referentes ao capítulo lido e conferir as respostas, revisando os possíveis erros pelo próprio guia ou pela documentação do PHP. Vamos continuar esse procedimento por todos os capítulos do guia.

Após terminar de ler todo o guia e resolver todas as questões do livro prático, iremos realizar nosso segundo simulado do exame. Preenchendo novamente na nossa tabelinha. E agora vem o ponto crucial de nossa estratégia:  Com a tabela preenchida, iremos reler no guia todos os capítulos onde obtivermos “Fail”, de preferência salve todas as questões do simulado (Com printscreen mesmo) e revise as que você acha (ou sabe) que errou pela documentação do PHP.

Imagino que no terceiro ou quarto simulado, você não vai receber mais nenhum “Fail”. Mesmo assim, caso não se sinta a vontade para realizar a prova, continue o procedimento citado a cima nas áreas onde você recebeu um “Pass” e não Excellent. Devemos lembrar ainda de dar uma atenção especial em caso de regressão em alguma área (Exemplo: Obtive “Pass” em OOP e no exame seguinte voltei ao “Fail”).

Quando estiver com uma boa pontuação nos exames, é só marcar a data do seu voucher e correr pro abraço ;)

2 Comments | Tags: Certificação, PHP

4 December 2008 - 12:07Transferência entre selects múltiplos usando JQuery (Parte II)

Salve todos,
Depois de muito tempo sem escrever, volto a este blog entregue as traças para completar a transferência entre selects de múltipla seleção.

Na primeira parte, vimos como transferir itens entre os dois selects, agora vamos salvar os valores marcados (ou seja, transferidos para o select da direita).

Em primeiro lugar, precisamos selecionar todos os itens do lado direito antes de enviar o formulário. Isso porque o HTML apenas submete os itens marcados e no nosso caso todos os itens do lado direito devem ser enviados, estando selecionados ou não.
Para isso, vamos usar mais uma vez da simplicidade do JQuery criando uma função que será chamada antes de submetermos o formulário:

function envia() {
 
 $('#lista2 option').attr("selected","true");
 
}

Pronto, a função a cima irá selecionar todas as opções da lista 2. Agora basta chamar a função no envio do formulário adicionando o onSubmit=”envia();” na tag <form> das listas. Após submeter o formulário, as opções selecionadas já estarão disponíveis através de um array. Podemos então percorrer este array e utilizar o resultado da forma como quisermos. A seguir vai um pequeno exemplo de como podemos montar uma query de inserção no banco de dados:

foreach ($_POST['lista2'] as $valor) {
 
 $query = "INSERT INTO tabela (selecionados) VALUES ($valor)";
 
}

Agora é com vocês!

No Comments | Tags: JavaScript e JQuery, PHP

18 February 2008 - 16:00Transferência entre selects múltiplos usando JQuery (Parte I)

Nessa primeira parte iremos mostrar como transferir itens entre dois selects usando JQuery, e o comparativo com JavaScript puro. Na segunda parte abordaremos como receber os valores deste Select Multiplo via PHP.

Primeiro, devemos incluir a biblioteca do JQuery, que você já deve ter baixado caso tenha lido o artigo anterior ( http://jquery.com/ ), no nosso caso, ele fica dentro de uma pasta chamada dll na raiz do diretório:

<script language="javascript">
type="text/javascript" src="../dll/jquery.js">
</script>

Agora devemos criar a função que fará a transferência entre os Selects:

<script language="javascript">
function transfereItem(idList, idList2){
	$("#"+idList2).append($("#"+idList+" option[@selected]"));
}
</script>

A função a cima recebe o id das duas listas de seleção e adiciona na lista 2 todas as opções marcadas na lista 1 através da função append do JQuery. Repare também que com um simples option[@selected] conseguimos pegar todas as opções selecionadas da lista 1 (Você pode aprender mais sobre como “pegar” objetos através do JQuery clicando aqui:).

Antes de seguir com o código, irei fazer uma demonstração de como poderíamos criar a mesma função através de JavaScript puro. Repare na diferença no número de linhas:

<script language="javascript">
function transfereItem(idList, idList2){
	var transferir = new Array();
	var contador = 0;
    	var oldList = document.getElementById(idList); 
	var opcoes = oldList.getElementsByTagName("option"); 
	var newList = document.getElementById(idList2); 
 
	// Pega as opcoes selecionadas
	for (var i = 0; i < opcoes.length; i++) { 
		if (opcoes[i].selected == true) {
			transferir[contador] = opcoes[i];
			contador++;
		}
	}
 
	// Passa para a nova lista
	for (var i = 0; i < transferir.length; i++) { 
		newList.appendChild(transferir[i]);
	}
}
</script>

Impressionado com a diferença? Agora vamos continuar com nossos selects criando os mesmos:
- Primeira lista com algumas opções para teste:

<select name="lista1" id="lista1" size="10" multiple="multiple">
<option value="1">Um</option>
<option value="2">Dois</option>
<option value="3">Três</option>
<option value="4">Quatro</option>
<option value="5">Cinco</option>
<option value="6">Seis</option>
</select>

- Agora o segundo select para fazer a transferência:

<select name="lista2[]" id="lista2" size="10" multiple="multiple"></select>

Estamos quase terminando, agora só precisamos criar as setas para transferir os elementos entre os dois Selects, repare que também poderíamos fazer a transferência ao realizar um duplo clique em cima de um elemento através do evento “ondblclick”.

<a href="#" onclick="transfereItem('lista1', 'lista2');"> &lt; </a>
 
<a href="#" onclick="transfereItem('lista2', 'lista1');"> &lt; </a>

Pronto, os selects com transferência já estarão funcionando. Observe que passei dentro da função chamada no link o ID contido nos dois selects, indicando de qual para qual select os elementos irão ser transferidos.
Você pode ver o exemplo do código funcionando clicando aqui

6 Comments | Tags: JavaScript e JQuery

7 February 2008 - 18:55Iniciando o uso do JQuery.

Meu primeiro Post vem falar sobre uma das minhas descobertas mais felizes na área tecnológica nos últimos tempos:
Trata-se de uma FrameWork feita para JavaScript que, com certeza, vai lhe poupar muito tempo em desenvolvimento nessa linguagem, incluindo o tratamento de AJAX.

Existem muitas FrameWorks pra JS, porém algo que me chamou muita atenção no JQuery foi a possibilidade de realizar várias ações em um elemento e a possibilidade de se realizar uma ação em muitos elementos sem a necessidade de se criar um loop.

Abaixo vão algumas comparações entre o Prototype (Outra grande FrameWork), o Jquery e JavaScript puro:
- Adicionando uma classe e exibindo uma div:

- JQuery:

$('#idDiv').addClass('ativo').show();

- Prototype:

$('idDiv').addClassName('ativo').show();

- JavaScript:

var div = document.getElementById('idDiv');
div.className = 'ativo';
div.display = 'block';

 

Notamos a diferença entre o Javascript e as frameworks, mas e se quiséssemos aplicar essa classe e mostrar todas as divs da página? Faríamos algo mais ou menos assim:

- JQuery:

$("div").addClass('ativo').show();

- Prototype:

$A(document.getElementsByTagName('div')).each(
	function(elemento) {
		elemento.addClassName('ativo').show();
	}
);

- Javascript:

var divs = document.getElementsByTagName("div");
for (var i = 0; i ++; divs.length; i++) {
	div[i].className = 'ativo';
	div[i].display = 'block';
}

 

Agora fica claro a vantagem do Jquery em relação ao Prototype: Em quanto no prototype precisamos realizar as ações dentro de um Loop, no Jquery resolvemos todo o problema em uma linha.

Além disso, existem mais alguns pontos que me chamaram a atenção sobre o Jquery:
- Tamanho da FrameWork (aproximadamente 28kbs compactado)
- Documentação muito simples de ser usada (http://docs.jquery.com/)
- Comunidade Brasileira comprometida com o produto (http://www.jquerybrasil.com/)
- Livre para download e desenvolvimento.

Espero ter despertado interesse sobre o JQuery, pois uma vez que começamos a usá-lo, até esquecemos do velho JavaScript puro.

2 Comments | Tags: JavaScript e JQuery