quinta-feira, 1 de julho de 2010

Imagens e outros downloads com acesso restrito em PHP... como mostrar?

Quem sempre mostrou imagens usando simplesmente a URL da mesma em um tag img, quando precisa restringir o acesso para elas, muitas vezes fica confuso em como fazer isso. A receita é bem simples:

1 - coloque a imagem fora do diretório que é servido pelo apache.

2 - crie um script que faz o controle de acesso

3 - se o usuário estiver autorizado, então devemos ler a imagem e mandar direto pro usuário, para isso podemos usar o método fpassthru() que já vai jogando a imagem direto pro buffer de saída, gerando um ganho de performance.

4 - depois disso é só fazer uma referência para aquele script, algo como < src="'mostraImagem.php?id="444'">

simples né? olhaí um exemplo de como fazer isso:


$targetPath = $caminhoDaMinhaImagem . '.jpg'; // presumindo que tem esta extensão, é claro.
$fp = fopen($targetPath, 'rb'); // abre para ler um binário
header("Content-Type: image/jpg"); // manda um header dizendo que é um jpg
header("Content-Length: " . filesize($targetPath)); // da a dica do tamanho da img pro browser
fpassthru($fp); // manda
// se você tiver dentro de algum framework, você tem que dar um jeito de não mandar mais nada, um método "bruto", mas prático, é simplesmente die();


Dê uma olhada melhor na documentação dessa função, la tem alguns exemplos muito úteis:

http://php.net/manual/en/function.fpassthru.php

quarta-feira, 30 de junho de 2010

Doctrine - Operações em lote ou "batch updates" - algums dicas

O Doctrine não é uma ferramenta feita para operações em lote, ele é um ORM. Mas tem alguns momentos que já temos o método na nossa camada de negócio e queremos chama-lo um monte de vezes para executar alguma manutenção no banco de dados ou algo assim.

Nestes momentos nem sempre é necessário reescrever tudo em um sql otimizado, em casos mais simples ou não tão críticos, podemos usar o que já temos. Porém, fazer esse tipo de operação no Doctrine requer alguns cuidados que temos aprendido com o tempo. Coloco aqui algumas dicas para quem quiser pular a fase do aprendizado pelo erro. Mais abaixo também coloco um exemplo de código para o tão famigerado e entretanto tão usado copy and paste.

Dicas:

1 - Faça um looping iniciando e "commitando" a transação. Isso aumenta em muito a velocidade de insert/update no banco. A quantidade de registros atualizados a cada iteração vai ter que ser definida na tentativa e erro, mas quanto mais simples a operação, maior a quantidade de itens que podem ser incluidos. Vale perceber que mesmo que a transação funcione, se forem muitos itens, o resultado final pode ficar mais lento mesmo assim.

2 - Sempre chame o método free() passando o parâmetro true para seus objetos instanciados pelo doctrine para que ele libere a memória usada por aquele objeto. Eu chamo também um unset() logo em seguida.

3 - Se você usar uma action no symfony (isso é, se é que você usa o symfony), ele tem uma mania de querer repetir a operação depois que termina. Por algum motivo que ainda não descobri, ele faz um redirect ou forward para a mesma action, então eu coloco aquele die() no final. Sei que ideal seria uma task rodando do shell, mas faço assim pq acho mais simples, principalmente para operações que vão rodar uma vez só. Se for algo que vai ser ativado pelo cron, aí não tem como fugir das tasks.

Para recortar e colar (divirta-se):


function executeRecalculateAllRatings() {
set_time_limit(3600);
$min = 0;
$increment = 1000;
$last = 50000;
$count = 0;
while ($min <= $last) { echo "<>NEW TRANSACTION - $min <>";
Doctrine_Manager::connection()->beginTransaction();
$comments = CommentTable::build()->findByIdRange($min, $min + $increment);
foreach($comments as $comment) {
$res = RatingLogTable::build()->calculateCommentRatingTotal($comment['id']);
$comment->setRating($res[0],$res[1]);
$comment->save();
$comment->free(true);
unset($comment);
echo $count++ . ' - ';
}
$min = $min + $increment;
Doctrine_Manager::connection()->commit();
}
die();
}

quarta-feira, 23 de junho de 2010

Git Submodules - um recurso para gerenciar dependências no lugar de svn externals.

Submodules & GIT - usando bibliotecas externas.

Quem está acostumado em gerenciar suas bibliotecas em svn usando o recurso externals, quando considera usar o GIT, geralmente tem dúvida a respeito de como trabalhar. Já escrevi aqui um artivo a respeito de svn externals e agora que estou migrando pro git então escrevo este aqui também para quem quiser me acompanhar.

Dois recursos normalmente surgem quando pesquisamos a respeito do assunto:
  • submodules
  • subtree merge strategy
Vamos tratar aqui do primeiro, porém quem quiser pesquisar o segundo, veja a comparação entre submodules e subtree merge strategy na documentação.

O que são submodules?

Na documentação diz que submodules ou submódulos permitem repositórios externos serem adicionados dentro de um subdiretório dedicado da arvore do nosso projeto, sempre apontando para um commit específico.

Bom... uma parte de cada vez:

repositórios externos
são outros repositórios em git.

diretórios dedicado
o diretório onde o submódulo fica tem sua arvore específica, inclusive a pasta .git com suas próprias configurações. Não pode ser feito merge com a arvore do repositório pai.

commit específico
quando adicionamos um submódulo a uma arvore, estamos adicionando um commit específico. Isso quer dizer que atrelamos ele a uma versão (um SHA). Toda vez que alguém baixar este projeto e pedir o submódulo ele vai receber esta versão mesmo que o repositório original esteja bem mais adiantado.

Isso é legal. Quero um pra mim. Como eu faço para criar um submódulo?

você deve usar um comando semelhante à este aqui (nessa hora o melhor mesmo é consultar o manual do git):

git submodule add [URL do repositório] [pasta de destino]
git submodule add http://github.com/brunoreis/pspec.git extlibs/pspec


com isso ele vai criar um repositório para o pspec na pasta extlibs/pspec. Vale a pena dar uma olhada nesta pasta, dentro do diretório .git, e ver o que tem nos arquivos texto de configuração. Com certeza vai auxiliar a encaixar as peças do quebra-cabeças. Ah, e também olhe o .git da raiz do seu projeto.

Bom, então é só isso? Meu submódulo está criado, posso mandar meu projeto para o github e ficar famoso porque uso submódulos e o pspec para especificar e testar meu software?

Muita hora nessa calma! Ainda temos algumas coisas a fazer antes. Ao criar o submodule, como já dissemos antes, atrelamos ele a um SHA específico. Os arquivos do repositório, a princípio, não ficam dentro do nosso repositório, mas o SHA sim. Temos então que adicionar o subdiretório criado à arvore original para que ele possa ser integrado ao nosso repositório.

Fazemos assim:
git add extlibs/pspec
git commig -m "agora sim com pspec"

Agora está lá... e se quiser subir para o repo de origem, não esqueça de fazer um push.

Isso quer dizer que quem fizer checkout no meu projeto já vai baixar o submódulo?

Não. Como já falei antes, você mandou apenas o SHA. Assim quando você fizer checkout vai trazer apenas uma referência à aquele submodulo, sem baixar o conteúdo do mesmo. Então após fazer checkout do seu repo, você ainda vai ter que fazer o seguinte:
git submodules init
git submodules update

Com isso ele vai trazer o conteúdo do(s) submódulos de acordo com o SHA de cada um.

E como eu faço pra saber se um projeto que baixei tem submódulos?

essa é fácil, é só rodar
git submodules

ele mostra os submódulos e também o SHA de cada um.

Beleza, mas agora eu sei que tem algo novo que eu preciso em um submódulo, como eu atualizo ele?

Simples, como o submódulo tem lá sua arvore e seu .git, etc.. porém este repositório está atrelado a um commit (sha) específico. Então precisamos fazer um checkout do branch que você quer, ex:

git checkout master

depois disso você pode usar

git pull

para atualizar.

Ah, e lembre-se de que depois que você fez isso, você tem que adicionar novamente o submodule ao repositório e fazer um commit (da atualização do SHA) para que os outros possam ter essa atualização.

Ao fazer essa atualização não coloque uma barra depois do nome do submodule. Se você fizer isso vai bagunçar tudo. Ou seja, use:
submodule add extlibs/pspec


mas NÃO USE

submodule add extlibs/pspec/ <--------------- Essa barra faz @#$@%@!!


Ta bom, ta bom....Hum.... e....

Se eu quiser atualizar todos de uma vez?

É só fazer assim:

git submodule foreach "git pull"

Esse comando eu achei muito interessante. Ele roda por todos os submódulos e executa o comando que for passado para ele, no noso caso o pull. Não esqueça de mandar esta alteração adicionando os submodules novamente à arvore principal.

Beleza, mas agora que estou no meu projeto, vi que tem um bug no repositório do submódulo. Tem como eu arrumar isso sem fazer checkout do sub em outro diretório?

Sim. Você pode entrar no dir do submódulo, fazer um checkout, alterar, fazer um commit e mandar novamente para o repo original.

Se você fizer isso e encontrar um erro dizendo que o original não aceita commits deste, leia este post a respeito do uso de "bare repositories".

Ta Bom.

Espero que você tenha sucesso usando os submódulos. Eles me parecem bem mais simples e dinâmicos que os externals do svn. Ainda estou aprendendo o git, então deve ter algo aqui que pode ser feito de uma maneira melhor ou então algo que você não entendeu. Peço que você acrescente seu comentário que se eu puder, respondo. Assim agregamos mais informações para os próximos não terem que ficar pesquisando as mesmas coisas que nós.

Sucesso!

--------------
Ah...

Algumas referências citadas e usadas aqui:

http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html

http://book.git-scm.com/5_submodules.html

https://git.wiki.kernel.org/index.php/GitSubmoduleTutorial

http://stackoverflow.com/questions/804545/what-is-this-git-warning-message-when-pushing-changes-to-a-remote-repository

sexta-feira, 18 de junho de 2010

PHP - Para fazer refactoring faz diferença se um método é públicos ou privados?

Para muitos o fato de um atributo ou método ser público ou privado pode nada ter a ver com refactoring de código. Afinal de contas isso tem mais a ver com o fato dessa classe poder ser acessada de fora ou não do objeto em questão. Então, a princípio, é melhor deixar aquele método público (padrão do php), afinal de contas deixando ele público, ele poderá ser usado por muitas outras classes, correto?

ERRADO!

Toda vez que você cria um método e declara ele como sendo público você está aumentando a interface do seu objeto. Com isso o seu sistema fica mais complexo.

E o que isso tem a ver com refactoring?

Para quem refatora código normalmente (todos fazem isso, correto? ), isso é bem fácil de entender. Se eu tenho um método público, estou dizendo que este método pode estar sendo chamado de qualquer lugar no sistema. Ou seja, se eu precisar alterar o meu método, vou precisar revisar o sistema inteiro para saber se alguém usa aquele método. E para uma linguagem dinâmica como o php, isso não é nenhum pouco fácil de fazer usando uma IDE.

Tudo que é dificil de fazer parece que atrai aquela conhecida de muitos programadores: preguiça! E a preguiça gera "ctrl+c ctrl+v". Resultado: código duplicado, dificuldade de fazer refactoring, problemas na implementação de novas regras. Mais erros, mais custo de manutenção, etc... Você deve conhecer esta novela!

Já se a pessoa que declarou o método colocou uma simples palavra antes dele (protected ou private), aí fica bem mais fácil de saber se o método está sendo usado no sistema, afinal de contas ele só poderá ser usado naquela classe ou filhas. Assim a preguiça fica no canto dela, o método é refatorado e o código fica limpo, fácil de mover, etc... espero que conheça esta história também! Se não conhece, a hora é agora. Sucesso!

sábado, 29 de maio de 2010

Que tal uma validação de dados "universal" para a comunidade php?

Validação de dados é um tema que sempre gera alguma controvérsia. Alguns acham que ela tem que ser feita na action, outros em uma camada de serviço ou de aplicação e outros no modelo. A maioria das pessoas também concorda que ela deva ser feita no browser. Neste texto pretendo examinar um pouco este assunto e sugerir a necessidade de se ter um componente de validação de dados para php como um projeto independente de qualquer framework ou camada.

Esses dias li um artigo interessante que diz que ao mesmo tempo que a comunidade ruby está caminhando para um único padrão forte de framework com muita convenção sobre conficuração, a comunidade php está caminhando para retirar a "mágica" dos códigos focando em padrões de projeto e OO, ter mais de um framework robusto e buscar ter interoperabilidade entre os frameworks. Achei isso muito bom. Pela minha experiência vejo que esses comportamentos "mágicos" dos frameworks costumam embaralhar muito a cabeça dos programadores mais jóvens.

Nesse rumo também vemos que algumas ferramentas estão amadurecendo muito e buscando encontrar o seu foco principal, removendo tudo aquilo que foge a esse foco. O Doctrine 2, por exemplo, que é um projeto que venho estudando, não vem com componente de validação integrado na versão 2. No início fiquei bem chateado com isso pois gostava muito de definir já algumas restrições no yml e já ter essa validação prontinha pra mim. Mais depois consegui perceber que o foco de um ORM não é esse e acho que se cada ferramente se manter no seu foco ela tem mais sucesso de prosperar.

Além disso se examinarmos um pouco mais, como já falei antes, dependendo da arquitetura uns acham melhor ter a validação no modelo, outros no JS, outros na action, etc... Não quero nesse texto ficar discutindo o que é melhor. Eu mesmo acho que isso varia de sistema pra sistema. Mas se percebemos bem essas necessidades vemos que a comunidade se beneficiará muito de ter uma ferramenta de validação de dados como sendo uma biblioteca completamente isolada. Assim ela poderá ser utilizada com qualquer framework e em qualquer camada. Claro que podem ser definidos adaptadores para integrar essa ferramenta em um lugar ou outro, mas o cerne dela deve ser "puro".

O meu pensamento é que se tivermos um arquivo de definição de dados, ele possa ser lido e usado para validar tanto um entidade na camada de modelo quanto um formulário no javascript. Com isso criamos um padrão de formato de dados que pode ser uma ótima contribuição nessa filosofia de ter ferramentas que podem ser usados nos diversos frameworks e camadas.

Algumas coisas eu acho interessante que essa ferramenta tenha:
  • Extensibilidade. Esse é um dos pontos principais. Deve ser muito fácil acrescentar um novo tipo de dados e também a classe ou método para validar este tipo. Além disso pode ser útil ter um sistema para registrar listeners que recebem eventos durante o processo de validação extendendo ou sobreescrevendo algum comportamento.
  • Facilidade de configuração. Geralmente as mensagens de erro lançados por estas ferramentas já atendem nos casos mais simples. Então é necessário poder trocar as mensagens de erro de cada validação e também poder capturar um erro e sobreescrever a mensagem para casos específicos.
  • Trabalhar com objetos/arrays multidimensionais. Esse é um dos pontos onde a maioria das ferramentas do "mercado de software livre" não atende. Acho importanto poder dizer que dentro do dado que eu quero validar pode ter um array e que cada item "nome" desse array pode ter no máximo 15 caracteres.
  • Ter um formato que pode ser compartilhado entre servidor e browser. É um fato que o servidor tem que ter validação para termos os dados íntegros. Também é um fato que uma validação no JS melhora a experiência do usuário e diminui o trafego entre cliente e servidor. E muitas vezes a validação nos dois pontos é a mesma. Por isso é importante que a definição da validação possa ser usada pelo browser em uma api semelhante à usada no servidor.
São poucos formatos que atendem a todas essas demandas. Um formato que achei bem interessante para o que já desenvolvi até agora é o Json Schema:

http://groups.google.com/group/json-schema/web/json-schema-proposal-working-draft

http://tools.ietf.org/html/draft-zyp-json-schema-02

Mas com certeza existem outros. O que quero ressaltar aqui é exatamente a necessidade da existência de uma ferramenta como essa. Tendo algo assim separado de frameworks, a comunidade pode juntar esforços para contribuir melhorando cada vez mais os algorítmos de validação e ampliando cada vez mais os tipos de dados disponíveis.

quarta-feira, 19 de maio de 2010

Alterando uma informação automáticamente antes do update no doctrine 2. (com annotations)

1 - Definimos que a classe ouve os callbacks:

/**
* @Entity
* @HasLifecycleCallbacks
*/

2 - Definimos um método para ser atualizado antes do update.

/**
* @PrePersist @PreUpdate
*/

function updateUpdatedAt() {
$this->updatedAt = new DateTime();
}


3 - Lembrar de mandar construir os modelos novamente.

sábado, 3 de abril de 2010

Data Repositories - low coupling with persistence layer.

Someday I used to think a method is good if it can be very flexible. But thins article tells a diferent story:

http://codebetter.com/blogs/gregyoung/archive/2009/01/16/ddd-the-generic-repository.aspx