<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-672197780778365674</id><updated>2012-02-08T03:27:37.416-08:00</updated><category term='código limpo'/><category term='acesso restrito'/><category term='mock stub'/><category term='phpunit'/><category term='acl'/><category term='pspec'/><category term='código'/><category term='Zend Framework'/><category term='crawl'/><category term='selenium'/><category term='doctrine'/><category term='json schema'/><category term='query'/><category term='qualidade de código'/><category term='arvore'/><category term='injeção de dependência'/><category term='js'/><category term='utf8'/><category term='function'/><category term='recursiva'/><category term='service container'/><category term='SPL'/><category term='imagem'/><category term='diretório'/><category term='js html index índice jquery'/><category term='classe'/><category term='svn:externals'/><category term='hook'/><category term='minificar'/><category term='mysql'/><category term='phing'/><category term='refactoring'/><category term='dir'/><category term='escopo'/><category term='robots.txt'/><category term='empty screen'/><category term='diretorio'/><category term='callback'/><category term='validação'/><category term='memory'/><category term='batch'/><category term='Tree'/><category term='design'/><category term='doctrine 1'/><category term='ZF'/><category term='método'/><category term='BDD PHP'/><category term='plugins'/><category term='preUpdate'/><category term='json'/><category term='prototype'/><category term='enter'/><category term='svn'/><category term='this'/><category term='ambientes'/><category term='url'/><category term='testes'/><category term='javascript'/><category term='dados'/><category term='construtor'/><category term='externals'/><category term='chamada'/><category term='speficication'/><category term='arquitetura para testes'/><category term='input'/><category term='directory'/><category term='listagem'/><category term='programação'/><category term='desenvolvimento'/><category term='string'/><category term='controle de acesso'/><category term='download'/><category term='Doctrine 2'/><category term='BDD'/><category term='função'/><category term='autoload'/><category term='arquivo'/><category term='tdd'/><category term='my.cnf'/><category term='debug'/><category term='symfony produção exceção erro listener event'/><category term='vision'/><category term='php'/><category term='citação'/><category term='tipo'/><category term='gerenciamento'/><category term='gerência'/><category term='jquery'/><category term='testes unitários'/><category term='log'/><category term='search'/><category term='symfony'/><category term='RecursiveDirectoryIterator'/><category term='phpdoctrine'/><category term='doctrine 2.0'/><category term='charset'/><category term='modular'/><title type='text'>Peagapando</title><subtitle type='html'>idéias a respeito de desenvolvimento php, symfony, doctrine, js, testes, engenharia de software, phpunit, integração contínua, etc..</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>36</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-5890367543206740767</id><published>2012-02-08T03:27:00.001-08:00</published><updated>2012-02-08T03:27:37.439-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mock stub'/><title type='text'>Mocks e Stubs - algo mais legível que a biblioteca do PHPUnit</title><content type='html'>Ainda não usei, mas só por ter uma sintaxe mais limpa, vale a pena dar uma olhada.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/mlively/Phake/wiki"&gt;https://github.com/mlively/Phake/wiki&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-5890367543206740767?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/5890367543206740767/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2012/02/mocks-e-stubs-algo-mais-legivel-que.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/5890367543206740767'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/5890367543206740767'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2012/02/mocks-e-stubs-algo-mais-legivel-que.html' title='Mocks e Stubs - algo mais legível que a biblioteca do PHPUnit'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-6671765432641215178</id><published>2012-01-13T03:47:00.000-08:00</published><updated>2012-01-13T03:47:21.887-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Doctrine 2'/><category scheme='http://www.blogger.com/atom/ns#' term='debug'/><title type='text'>Imprimindo entidades do Doctrine 2 - debug melhor que print_r</title><content type='html'>Por conta dos porxys que ficam nas entidades relacionadas, dar um print_r em um obj do doctrine 2 não é nada agradável. Na documentação tem pouca referência de como fazer isso, mas achei o seguinte:&lt;br /&gt;&lt;br /&gt;\Doctrine\Common\Util\Debug::dump($entidade);&lt;br /&gt;&lt;br /&gt;É muito útil.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-6671765432641215178?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/6671765432641215178/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2012/01/imprimindo-entidades-do-doctrine-2.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/6671765432641215178'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/6671765432641215178'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2012/01/imprimindo-entidades-do-doctrine-2.html' title='Imprimindo entidades do Doctrine 2 - debug melhor que print_r'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-8222605579977296958</id><published>2010-12-07T18:26:00.000-08:00</published><updated>2010-12-07T18:36:32.782-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='query'/><category scheme='http://www.blogger.com/atom/ns#' term='doctrine 1'/><category scheme='http://www.blogger.com/atom/ns#' term='debug'/><category scheme='http://www.blogger.com/atom/ns#' term='empty screen'/><category scheme='http://www.blogger.com/atom/ns#' term='doctrine'/><title type='text'>Degub de queries que não mostram erro no Doctrine 1</title><content type='html'>Se você estiver tendo problemas com uma query especifica que morre sem deixar vestigios, sem imprimir nada na tela, pode se beneficiar de mais informações registrando um listener generico para imprimir os eventos e também melhorando o flush para ele realmente mostrar algo. antes de fazer a query, registre o listener na conexão:&lt;br /&gt;&lt;br /&gt;$conn = Doctrine_Manager::connection();&lt;br /&gt;$conn-&gt;setListener(new MyDebugger());&lt;br /&gt;$result = $query-&gt;execute();&lt;br /&gt;&lt;br /&gt;E também declare ele e o flush melhorado:&lt;br /&gt;&lt;br /&gt;class MyDebugger implements Doctrine_Overloadable&lt;br /&gt;{&lt;br /&gt;   public function __call($methodName, $args)&lt;br /&gt;   {&lt;br /&gt;       echo $methodName . ' called !';&lt;br /&gt;       flush2();&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function flush2 (){&lt;br /&gt;   echo(str_repeat(' ',256));&lt;br /&gt;   // check that buffer is actually set before flushing&lt;br /&gt;   if (ob_get_length()){&lt;br /&gt;       @ob_flush();&lt;br /&gt;       @flush();&lt;br /&gt;       @ob_end_flush();&lt;br /&gt;   }&lt;br /&gt;   @ob_start();&lt;br /&gt;}&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-8222605579977296958?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/8222605579977296958/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/12/degub-de-queries-que-nao-imprimem-nada.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/8222605579977296958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/8222605579977296958'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/12/degub-de-queries-que-nao-imprimem-nada.html' title='Degub de queries que não mostram erro no Doctrine 1'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-7071506323646364701</id><published>2010-11-11T04:59:00.000-08:00</published><updated>2010-11-12T03:13:21.548-08:00</updated><title type='text'>Refactoring - é bom mesmo quando vira costume!</title><content type='html'>Neste artigo vou falar do refactoring, explicar porque fazer e dizer algumas regras fundamentais que aprendi com a experiência.&lt;br /&gt;&lt;br /&gt;Quase todo bom costume que queremos colocar na vida da gente, no ínicio é uma peleja para conquistar. Mas também depois que vira um costume mesmo, aí fica difícil é de deixar de fazer. Uma coisa que desejo para todo programador é que ponha no seu dia a dia o custume de fazer faxina no código, o famoso "refactoring".&lt;br /&gt;&lt;br /&gt;Como funciona isso? É simples, você vê um código bagunçado e arruma ele. Claro que não da pra arrumar todo o código bagunçado do mundo, mas ao menos podemos ir arrumando o código no qual vamos precisar trabalhar. A idéia é: &lt;span style="font-weight: bold;"&gt;Já que vamos mexer na sujeira, que seja para limpar logo de vez. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Se vamos começar a mexer em um código, seja quem foi que programou ele, até mesmo nós em um dia que estavamos com dor de barriga, precisamos entender aquele código. Se for um código confuso, daquelas "belezuras" com variáveis misteriosas e diversas estruturas aninhadas com breaks, continues e switchs, temos geralmente duas opções:&lt;br /&gt;&lt;br /&gt;&lt;span&gt;&lt;span style="font-weight: bold;"&gt;Opção #1&lt;/span&gt; -&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;perder muito tempo tentando entender o código&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;perder muito tempo para cada modificação que queremos fazer &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;deixar uma bagunça maior para o próximo que for mexer naquele código.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Assim temos a seguinte equação:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;muito tempo para entender +&lt;br /&gt;muito tempo para mexer * número de modificações&lt;br /&gt;----------------------------------------------------&lt;br /&gt;muito tempo gasto, um código ainda pior e insatisfação&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Opção #2 -&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span&gt;perder muito tempo tentando entender e já fazendo um refactoring&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;gastar pouco tempo para cada modificação&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;deixar um código bom para o próximo que for mexer (que pode ser inclusive eu mesmo).&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Assim temos:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;muito tempo para entender e fazer o refactoring&lt;br /&gt;+ tempo razoável para mexer * número de modificações&lt;br /&gt;--------------------------------------------------------&lt;br /&gt;muito tempo gasto, código limpo e satisfação&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;O fato é que sempre que pegamos um código medonho, teremos que gastar muito tempo nele.&lt;/span&gt; E esse tempo geralmente aumenta proporcionalmente à idade daquele código. Quanto mais antigo, mais WTF, mais tempo gasto. &lt;span style="font-weight: bold;"&gt;Assim se vamos gastar mesmo muito tempo nele, façamos logo um refactoring nele. &lt;/span&gt;&lt;span&gt;&lt;br /&gt;&lt;br /&gt;O progra&lt;/span&gt;mador preguiçoso, ou espertalhão, muitas vezes vê um código desses e acredita que vai fazer apenas uma gambiarrazinha, vai consertar o problema, e ainda vai fazer uma média com o gerente. Conheço esse tipo. Coitado, um dia a casa cai e ele descobre que não sabe programar, pq ficou sempre dando uma de esperto e não teve tempo para aprender a programar.&lt;br /&gt;&lt;br /&gt;Bom, voltando ao refactoring, ele vale para todos os tipos de código. Não só os muito feios. Mesmo que o código seja razoável, ou bom, podemos ir melhorando ele para que fique cada vez mais simples e rápido de dar manutenção. Claro que tem hora que temos que pesar o preço daquela manutenção, mas isso depende de cada caso. Na maioria das vezes a merda fica quando pensamos em fazer algo apenas provisório, "só um protótipozinho", e aquele código fica em produção por muitos anos.&lt;br /&gt;&lt;br /&gt;Mas na maior parte dos casos vale muito o esforço de fazer um refactoring. E com certeza isso não só contribui para o código como também para o desenvolvimento profissional do próprio programador. Como eu falei no início, a idéia é que isso vire um costume, uma virtude. &lt;span style="font-weight: bold;"&gt;A idéia é que seus dedos fiquem coçando para fazer um refactoring quando você olhar para qualquer código.&lt;/span&gt; Existem vários casos onde podemos fazer refactoring e até livros inteiros a respeito do assunto, não vou tratar tudo isso aqui, porque não cabe.&lt;br /&gt;&lt;br /&gt;Mas algumas regras importantes sempre se aplicam e vou concluir este artigo com elas:&lt;br /&gt;&lt;br /&gt;1 - Sempre é bom fazer refactoring antes e depois de mexer no código. Nunca durante. Ou em outras palavras: &lt;span style="font-weight: bold;"&gt;ta fazendo refactoring, não muda o que o código faz&lt;/span&gt;. Essa regra, o espertão sempre acha que pode burlar, pegar um atalho. Muito cuidado! Há atalhos que dão um trabalho enorme para encontrar o caminho de volta.&lt;br /&gt;&lt;br /&gt;2 - Escolha blocos pequenos para fazer refactoring. Teste para saber o que o código faz. Faça o refactoring e teste novamente. Dê passos pequenos. Cada coisa de uma vez. &lt;span style="font-weight: bold;"&gt;Por mais tentador que possa parecer alterar logo tudo que você sabe que precisa, isso geralmente da merda.&lt;/span&gt; Assim quando bater o impulso de alterar logo tudo, conte até dez e lembre-se: uma coisa de cada vez.&lt;br /&gt;&lt;br /&gt;3 - &lt;span style="font-weight: bold;"&gt;Ao fazer refactoring, faça pensando na lógica do cliente, na linguagem do cliente.&lt;/span&gt; Vai nomear um método, pense como o cliente pensa, na linguagem dele. Vai nomear uma variável, pense na linguagem dele (na verdade o ideal é estabelecer um glossário e uma terminologia única entre cliente e equipe de desenvolvimento). Você deve estar pensando que em baixo nível, mexendo na API, isso não importa pq o cliente não conhece isso. Porém em baixo nível, o cliente não é o cliente do software, mas é o cliente da API: o desenvolvedor. Pense nele e se cabível, mantenha também a linguagem única do cliente do software.&lt;br /&gt;&lt;br /&gt;Tem gente que me procura para saber como trabalhar com arquitetura de software, com componentes, herança, injeção de dependência e todas essas coisas. O fato é que a estrada começa sempre nestes pontos que falei neste artigo. Se um programador não desenvolve esses hábitos, dificilmente ele se tornará algo mais.&lt;br /&gt;&lt;br /&gt;Em todas as áreas do desenvolvimento humano, é bom procurar as virtudes a serem desenvolvidas, e no desenvolvimento de software, fazer refactoring certamente é uma delas.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-7071506323646364701?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/7071506323646364701/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/11/refactoring-e-bom-mesmo-quando-vira.html#comment-form' title='4 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/7071506323646364701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/7071506323646364701'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/11/refactoring-e-bom-mesmo-quando-vira.html' title='Refactoring - é bom mesmo quando vira costume!'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-4250924453642736014</id><published>2010-10-22T17:16:00.000-07:00</published><updated>2010-10-22T17:20:29.039-07:00</updated><title type='text'>PhpDescribe agora com mais documentação.</title><content type='html'>O PhpDescribe, ferramenta para escrever especificações ativas de software em uma filosofia semelhante ao do BDD, lançou seu novo site com informações a respeito da &lt;a href="http://www.phpdescribe.org"&gt;ferramenta de teste de software&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;É uma ferramenta muito promissora para quem quer unir a escrita de especificação com a escrita dos testes de software. Além disso é um exemplo de código usando novas funcionalidades do php 5.3.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-4250924453642736014?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/4250924453642736014/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/10/phpdescribe-agora-com-mais-documentacao.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/4250924453642736014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/4250924453642736014'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/10/phpdescribe-agora-com-mais-documentacao.html' title='PhpDescribe agora com mais documentação.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-4929690338609831319</id><published>2010-09-22T06:50:00.000-07:00</published><updated>2010-09-20T08:45:29.452-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='BDD'/><category scheme='http://www.blogger.com/atom/ns#' term='vision'/><category scheme='http://www.blogger.com/atom/ns#' term='speficication'/><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='pspec'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='BDD PHP'/><title type='text'>Trees, forests and applications... please let´s turn tests in real specifications.</title><content type='html'>To understand the tree, you need to look at the forest. To understand a forest you need to look at the trees. Clients normaly want to see the forest, but developers like to develop trees. A big step taken by the agile process is to start moving the developers to see the forest, not only the trees that compose that forest.&lt;br /&gt;&lt;br /&gt;When I was trying to learn how to draw, the good teachers kept telling me to do things from the outer to the inner. TDD and BDD brought a broader vision to this. But I still have a feeling that we need to start from a little bit before and we also need to have a beeter picture of the tree.&lt;br /&gt;&lt;br /&gt;The standart bdd tools, using a strict grammar is nice. But, how do we see the results? Are they results at all?&lt;br /&gt;&lt;br /&gt;No, they are the specifications. For tests we have results and for specification we have what? I think in examples and these can be working or not workin.  So, how do I take a good look at the specifications and see what parts are working, incomplete, not working....&lt;br /&gt;&lt;br /&gt;I have not seen yet this being addressed by any tool. Colored test results from a little piece of specification, as most tools will produce, won´t give me the forest view. Forest have leaves, flowers, stems and other important things to be noticed to understand the forest. I think we should think more in writing specs to be read and understood by humans than by continuous integration tools. They should be extremely easy to navigate, search and write. Images are essential in some place, they need to have a way to be added.&lt;br /&gt;&lt;br /&gt;So I think there is a gap to be filled.... that´s why I´ve started PSpec.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-4929690338609831319?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/4929690338609831319/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/04/trees-forests-and-applications-please.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/4929690338609831319'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/4929690338609831319'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/04/trees-forests-and-applications-please.html' title='Trees, forests and applications... please let´s turn tests in real specifications.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-8863791745467255085</id><published>2010-07-20T04:33:00.000-07:00</published><updated>2010-09-16T23:47:55.138-07:00</updated><title type='text'>A simple symfony application deploy using shell script.</title><content type='html'>This is something that can be used to deploy an app to an staging environment. After that you can sync it somehow, but I prefer to export again to prod to keep the fidelity from the release launched with the repo release.&lt;br /&gt;&lt;br /&gt;It´s nice to see how many things can be done with shell scritp without the need to configure those weird xmls (IMO, of course). It´s just more.... simple.&lt;br /&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;die () {&lt;br /&gt;   echo &gt;&amp;amp;2 "$@"&lt;br /&gt;   exit 1&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// validate the parameters count....&lt;br /&gt;&lt;br /&gt;[ "$#" -eq 1 ] || die "Voce precisa informar o nome da release para ser o nome do diretorio gerado dentro de prod_online"&lt;br /&gt;&lt;br /&gt;// exports the code from a local repo. (if you need from a foreign with ssh, ask that I can publish some info also)&lt;br /&gt;&lt;br /&gt;svn export --force file:///data/svn/reuni-on/trunk /data/web/reuni/prod_online/$1&lt;br /&gt;&lt;br /&gt;// override the config file with one specific to prod environment.&lt;br /&gt;&lt;br /&gt;cp /data/web/reuni/deploy_script/testesSincronia.conf.tmp /data/web/reuni/prod_online/$1/web/testesSincronia/conf.php&lt;br /&gt;&lt;br /&gt;// create dirs that are not so good to be on the repo. &lt;br /&gt;&lt;br /&gt;mkdir /data/web/reuni/prod_online/$1/cache&lt;br /&gt;mkdir /data/web/reuni/prod_online/$1/log&lt;br /&gt;mkdir /data/web/reuni/prod_online/$1/data/offlineParaDownload&lt;br /&gt;&lt;br /&gt;//allow to write into this dirs ( here you will probably want a more restrictive permission)&lt;br /&gt;chmod 777 -R /data/web/reuni/prod_online/$1/cache&lt;br /&gt;chmod 777 -R /data/web/reuni/prod_online/$1/log&lt;br /&gt;chmod 777 -R /data/web/reuni/prod_online/$1/data/offlineParaDownload&lt;br /&gt;&lt;br /&gt;// clear symfony cache&lt;br /&gt;php /data/web/reuni/prod_online/$1/symfony cc&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-8863791745467255085?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/8863791745467255085/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/07/simple-symfony-application-deploy-using.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/8863791745467255085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/8863791745467255085'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/07/simple-symfony-application-deploy-using.html' title='A simple symfony application deploy using shell script.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-5173467069755030687</id><published>2010-07-14T13:56:00.000-07:00</published><updated>2010-09-17T00:04:59.990-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='robots.txt'/><category scheme='http://www.blogger.com/atom/ns#' term='url'/><category scheme='http://www.blogger.com/atom/ns#' term='search'/><category scheme='http://www.blogger.com/atom/ns#' term='crawl'/><title type='text'>Robots.txt url without trailing slashes. - ALERT -</title><content type='html'>To leave a url in the robots.txt without a trailing slash can steal your sleep sometime. This makes the seach engine think you are blocking any match that starts with that url and it can disallow or allow unwanted urls, read these to understand better:&lt;br /&gt;&lt;br /&gt;http://www.google.com/support/webmasters/bin/answer.py?hl=br&amp;amp;answer=156449&lt;br /&gt;&lt;br /&gt;http://www.webmasterworld.com/forum93/892.htm&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-5173467069755030687?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/5173467069755030687/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/07/robotstxt-url-without-trailing-slashes.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/5173467069755030687'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/5173467069755030687'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/07/robotstxt-url-without-trailing-slashes.html' title='Robots.txt url without trailing slashes. - ALERT -'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-2939514646146621303</id><published>2010-07-01T19:16:00.000-07:00</published><updated>2010-07-01T19:31:19.437-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='download'/><category scheme='http://www.blogger.com/atom/ns#' term='imagem'/><category scheme='http://www.blogger.com/atom/ns#' term='controle de acesso'/><category scheme='http://www.blogger.com/atom/ns#' term='acl'/><category scheme='http://www.blogger.com/atom/ns#' term='acesso restrito'/><title type='text'>Imagens e outros downloads com acesso restrito em PHP... como mostrar?</title><content type='html'>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:&lt;br /&gt;&lt;br /&gt;1 - coloque a imagem fora do diretório que é servido pelo apache.&lt;br /&gt;&lt;br /&gt;2 - crie um script que faz o controle de acesso&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;4 - depois disso é só fazer uma referência para aquele script, algo como &lt; src="'mostraImagem.php?id="444'"&gt;&lt;br /&gt;&lt;br /&gt;simples né? olhaí um exemplo de como fazer isso:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;        $targetPath = $caminhoDaMinhaImagem . '.jpg'; // presumindo que tem esta extensão, é claro.&lt;br /&gt;        $fp = fopen($targetPath, 'rb'); // abre para ler um binário&lt;br /&gt;        header("Content-Type: image/jpg"); // manda um header dizendo que é um jpg&lt;br /&gt;        header("Content-Length: " . filesize($targetPath)); // da a dica do tamanho da img pro browser&lt;br /&gt;        fpassthru($fp); // manda&lt;br /&gt;        // 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();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Dê uma olhada melhor na documentação dessa função, la tem alguns exemplos muito úteis:&lt;br /&gt;&lt;br /&gt;http://php.net/manual/en/function.fpassthru.php&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-2939514646146621303?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/2939514646146621303/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/07/imagens-e-outros-downloads-com-acesso.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/2939514646146621303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/2939514646146621303'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/07/imagens-e-outros-downloads-com-acesso.html' title='Imagens e outros downloads com acesso restrito em PHP... como mostrar?'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-1693428407922272544</id><published>2010-06-30T06:27:00.000-07:00</published><updated>2010-06-30T07:02:27.350-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='phpdoctrine'/><category scheme='http://www.blogger.com/atom/ns#' term='batch'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='memory'/><category scheme='http://www.blogger.com/atom/ns#' term='doctrine'/><title type='text'>Doctrine - Operações em lote ou "batch updates" - algums dicas</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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 &lt;span style="font-style: italic;"&gt;copy and paste&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Dicas:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Para recortar e colar (divirta-se):&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function executeRecalculateAllRatings() {&lt;br /&gt;       set_time_limit(3600);&lt;br /&gt;       $min = 0;&lt;br /&gt;       $increment = 1000;&lt;br /&gt;       $last = 50000;&lt;br /&gt;       $count = 0;&lt;br /&gt;       while ($min &lt;= $last) {             echo "&lt;&gt;NEW TRANSACTION - $min &lt;&gt;";&lt;br /&gt;           Doctrine_Manager::connection()-&gt;beginTransaction();&lt;br /&gt;           $comments = CommentTable::build()-&gt;findByIdRange($min, $min + $increment);&lt;br /&gt;           foreach($comments as $comment) {&lt;br /&gt;               $res = RatingLogTable::build()-&gt;calculateCommentRatingTotal($comment['id']);&lt;br /&gt;               $comment-&gt;setRating($res[0],$res[1]);&lt;br /&gt;               $comment-&gt;save();&lt;br /&gt;               $comment-&gt;free(true);&lt;br /&gt;               unset($comment);&lt;br /&gt;               echo $count++ . ' - ';&lt;br /&gt;           }&lt;br /&gt;           $min = $min + $increment;&lt;br /&gt;           Doctrine_Manager::connection()-&gt;commit();&lt;br /&gt;       }&lt;br /&gt;       die();&lt;br /&gt;   }&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-1693428407922272544?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/1693428407922272544/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/06/doctrine-operacoes-em-lote-ou-batch.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/1693428407922272544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/1693428407922272544'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/06/doctrine-operacoes-em-lote-ou-batch.html' title='Doctrine - Operações em lote ou &quot;batch updates&quot; - algums dicas'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-7637225163511488603</id><published>2010-06-23T13:58:00.001-07:00</published><updated>2010-08-24T06:26:24.524-07:00</updated><title type='text'>Git Submodules - um recurso para gerenciar dependências no lugar de svn externals.</title><content type='html'>&lt;span style="font-size:180%;"&gt;&lt;span style="font-style: italic;"&gt;Submodules&lt;/span&gt; &amp;amp; GIT&lt;/span&gt; - usando bibliotecas externas.&lt;br /&gt;&lt;br /&gt;Quem está acostumado em gerenciar suas bibliotecas em svn usando o recurso &lt;span style="font-style: italic;"&gt;externals&lt;/span&gt;, quando considera usar o GIT, geralmente tem dúvida a respeito de como trabalhar. Já escrevi aqui um artivo a respeito de &lt;a href="http://peagapando.blogspot.com/2009/07/gerenciando-plugins-em-um-repositorio.html"&gt;svn externals&lt;/a&gt; e agora que estou migrando pro git então escrevo este aqui também para quem quiser me acompanhar.&lt;br /&gt;&lt;br /&gt;Dois recursos normalmente surgem quando pesquisamos a respeito do assunto: &lt;span style="font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;submodules&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-style: italic;"&gt;subtree merge strategy&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;Vamos tratar aqui do primeiro, porém quem quiser pesquisar o segundo, veja a comparação entre &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/howto/using-merge-subtree.html"&gt;submodules e subtree merge strategy &lt;/a&gt;na documentação.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;O que são submodules?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Bom... uma parte de cada vez:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;repositórios externos&lt;/span&gt;&lt;br /&gt;são outros repositórios em git.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;diretórios dedicado&lt;/span&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;commit específico&lt;/span&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Isso é legal. Quero um pra mim. Como eu faço para criar um &lt;span style="font-style: italic;"&gt;submódulo&lt;/span&gt;?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;você deve usar um comando semelhante à este aqui (nessa hora o melhor mesmo é consultar o &lt;a href="http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html"&gt;manual do git&lt;/a&gt;):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;git submodule add [URL do repositório] [pasta de destino]&lt;br /&gt;git submodule add http://github.com/brunoreis/pspec.git extlibs/pspec&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;com isso ele vai criar um repositório para o pspec na pasta &lt;span style="font-style: italic;"&gt;extlibs/pspec&lt;/span&gt;. 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.&lt;br /&gt;&lt;br /&gt;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 &lt;a href="http://www.blogger.com/git%20submodule%20add%20http://github.com/brunoreis/pspec.git%20extlibs/pspec"&gt;pspec para especificar e testar meu software&lt;/a&gt;?&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Fazemos assim:&lt;br /&gt;&lt;pre&gt;git add extlibs/pspec&lt;br /&gt;git commig -m "agora sim com pspec"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Agora está lá... e se quiser subir para o repo de origem, não esqueça de fazer um push.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Isso quer dizer que quem fizer &lt;span style="font-style: italic;"&gt;checkout&lt;/span&gt; no meu projeto já vai baixar o submódulo?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;pre&gt;git submodules init&lt;br /&gt;git submodules update&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Com isso ele vai trazer o conteúdo do(s) submódulos de acordo com o SHA de cada um.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;E como eu faço pra saber se um projeto que baixei tem submódulos?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;essa é fácil, é só rodar&lt;br /&gt;&lt;pre&gt;git submodules&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ele mostra os submódulos e também o SHA de cada um.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Beleza, mas agora eu sei que tem algo novo que eu preciso em um submódulo, como eu atualizo ele?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;git checkout master&lt;br /&gt;&lt;span style="font-family: monospace;"&gt;&lt;/span&gt;&lt;br /&gt;depois disso você pode usar&lt;br /&gt;&lt;br /&gt;git pull&lt;br /&gt;&lt;br /&gt;para atualizar.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;pre&gt;submodule add extlibs/pspec&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;mas NÃO USE&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;submodule add extlibs/pspec/   &lt;--------------- Essa barra faz @#$@%@!! &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ta bom, ta bom....Hum.... e....&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Se eu quiser atualizar todos de uma vez?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;É só fazer assim:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;git submodule foreach "git pull"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;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 &lt;span style="font-style: italic;"&gt;sub&lt;/span&gt; em outro diretório?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sim. Você pode entrar no dir do submódulo, fazer um checkout, alterar, fazer um commit e mandar novamente para o repo original.&lt;br /&gt;&lt;br /&gt;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 &lt;a href="http://stackoverflow.com/questions/804545/what-is-this-git-warning-message-when-pushing-changes-to-a-remote-repository"&gt;"bare repositories"&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Ta Bom.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Sucesso!&lt;br /&gt;&lt;br /&gt;--------------&lt;br /&gt;Ah...&lt;br /&gt;&lt;br /&gt;Algumas referências citadas e usadas aqui:&lt;br /&gt;&lt;br /&gt;http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html&lt;br /&gt;&lt;br /&gt;http://book.git-scm.com/5_submodules.html&lt;br /&gt;&lt;br /&gt;https://git.wiki.kernel.org/index.php/GitSubmoduleTutorial&lt;br /&gt;&lt;br /&gt;http://stackoverflow.com/questions/804545/what-is-this-git-warning-message-when-pushing-changes-to-a-remote-repository&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-7637225163511488603?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/7637225163511488603/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/06/git-submodules-um-recurso-para.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/7637225163511488603'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/7637225163511488603'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/06/git-submodules-um-recurso-para.html' title='Git Submodules - um recurso para gerenciar dependências no lugar de svn externals.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-557482594940196252</id><published>2010-06-18T08:27:00.000-07:00</published><updated>2010-06-18T11:08:01.830-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='escopo'/><title type='text'>PHP - Para fazer refactoring faz diferença se um método é públicos ou privados?</title><content type='html'>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?&lt;br /&gt;&lt;br /&gt;ERRADO!&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;E o que isso tem a ver com refactoring?&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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!&lt;br /&gt;&lt;br /&gt;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!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-557482594940196252?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/557482594940196252/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/06/php-para-refactoring-faz-diferenca-se.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/557482594940196252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/557482594940196252'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/06/php-para-refactoring-faz-diferenca-se.html' title='PHP - Para fazer refactoring faz diferença se um método é públicos ou privados?'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-4513630392537098566</id><published>2010-05-29T04:35:00.000-07:00</published><updated>2010-05-29T05:11:08.000-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='json schema'/><category scheme='http://www.blogger.com/atom/ns#' term='dados'/><category scheme='http://www.blogger.com/atom/ns#' term='json'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='validação'/><title type='text'>Que tal uma validação de dados "universal" para a comunidade php?</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;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".&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;Algumas coisas eu acho interessante que essa ferramenta tenha:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Extensibilidade.&lt;/span&gt; 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.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Facilidade de configuração.&lt;/span&gt; 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.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Trabalhar com objetos/arrays multidimensionais.&lt;/span&gt; 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. &lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Ter um formato que pode ser compartilhado entre servidor e browser.&lt;/span&gt; É 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.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;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:&lt;br /&gt;&lt;br /&gt;http://groups.google.com/group/json-schema/web/json-schema-proposal-working-draft&lt;br /&gt;&lt;br /&gt;http://tools.ietf.org/html/draft-zyp-json-schema-02&lt;br /&gt;&lt;br /&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-4513630392537098566?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/4513630392537098566/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/05/que-tal-uma-validacao-de-dados.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/4513630392537098566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/4513630392537098566'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/05/que-tal-uma-validacao-de-dados.html' title='Que tal uma validação de dados &quot;universal&quot; para a comunidade php?'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-5927555496225652812</id><published>2010-05-19T06:40:00.000-07:00</published><updated>2010-05-19T09:04:49.274-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='doctrine 2.0'/><category scheme='http://www.blogger.com/atom/ns#' term='hook'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='preUpdate'/><title type='text'>Alterando uma informação automáticamente antes do update no doctrine 2. (com annotations)</title><content type='html'>1 - Definimos que a classe ouve os callbacks:&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* @Entity&lt;br /&gt;* @HasLifecycleCallbacks&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;2 - Definimos um método para ser atualizado antes do update.&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* @PrePersist @PreUpdate&lt;br /&gt;*/&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function updateUpdatedAt() {&lt;br /&gt;     $this-&gt;updatedAt = new DateTime();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;3 - Lembrar de mandar construir os modelos novamente.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-5927555496225652812?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/5927555496225652812/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/05/alterando-uma-informacao.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/5927555496225652812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/5927555496225652812'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/05/alterando-uma-informacao.html' title='Alterando uma informação automáticamente antes do update no doctrine 2. (com annotations)'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-1173065242718327779</id><published>2010-04-03T08:26:00.000-07:00</published><updated>2010-09-17T00:08:43.552-07:00</updated><title type='text'>Data Repositories - low coupling with persistence layer.</title><content type='html'>Someday I used to think a method is good if it can be very flexible. But thins article tells a diferent story:&lt;br /&gt;&lt;br /&gt;http://codebetter.com/blogs/gregyoung/archive/2009/01/16/ddd-the-generic-repository.aspx&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-1173065242718327779?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/1173065242718327779/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/04/data-repositories-low-coupling-with.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/1173065242718327779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/1173065242718327779'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/04/data-repositories-low-coupling-with.html' title='Data Repositories - low coupling with persistence layer.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-3908985842156494599</id><published>2010-03-24T12:18:00.000-07:00</published><updated>2010-03-24T12:20:13.042-07:00</updated><title type='text'>Concordion - parece algo de qualidade para BDD</title><content type='html'>Tem tempo que procuro uma ferramenta útil que una testes unitários e escrita de especificação em uma coisa só. Ou seja, uma ferramenta boa de BDD. Recebi este link de um colega de trabalho e gostei.  Esta pode ser uma boa ferramenta para se ter um port para php:&lt;br /&gt;&lt;br /&gt;http://www.concordion.org/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-3908985842156494599?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/3908985842156494599/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/03/concordion-parece-algo-de-qualidade.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/3908985842156494599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/3908985842156494599'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/03/concordion-parece-algo-de-qualidade.html' title='Concordion - parece algo de qualidade para BDD'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-469008290318184707</id><published>2010-03-18T08:00:00.001-07:00</published><updated>2010-03-18T08:36:10.363-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='código limpo'/><category scheme='http://www.blogger.com/atom/ns#' term='refactoring'/><title type='text'>Refactoring e Clareza de Código - Negação da Negação</title><content type='html'>&lt;span style="font-style: italic;"&gt;Este faz parte de uma série de pequenos posts com os tags código limpo e refactoring que vou estar publicando na medida que vou limpando alguns códigos por aqui.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A negação da negação geralmente atrapalha a leitura:&lt;br /&gt;&lt;pre&gt;if(!$userAllowed) {&lt;br /&gt;//x&lt;br /&gt;}  // aqui estamos negando a negação do if acima...&lt;br /&gt;else {&lt;br /&gt;//y&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;ou seja, se o sistema não tiver acesso, faça x e se ele não não tiver (que é o mesmo que ele ter, mas da um trabalho para ler) faça y. Sei que isso parece simples, principalmente em um código pequeno, mas tudo que diminui a velocidade de leitura deve ser evitado, até mesmo porque em alguns métodos fica difícil não ter um código extenso.&lt;br /&gt;&lt;br /&gt;Normalmente quando um if tem um else, é mais claro se não começamos negando. Porque então o else vai ser a negação da negação.&lt;br /&gt;&lt;br /&gt;Ou então algo assim:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if(!$accesForbidden) {//a} else {//b}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;parece normal, mas da um trabalhinho pra ler. É bem mais fácil ler algo como:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if($hasAccess) {//a} else {//b}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ou no máximo:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if($accesForbidden) {//b} else {//a}&lt;br /&gt;&lt;/pre&gt;apesar de que este segundo caso só acho aceitável quando a variável accessForbiden já existe e tem esse nome justificado em outro contexto.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-469008290318184707?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/469008290318184707/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/03/refactoring-e-clareza-de-codigo.html#comment-form' title='1 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/469008290318184707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/469008290318184707'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/03/refactoring-e-clareza-de-codigo.html' title='Refactoring e Clareza de Código - Negação da Negação'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-4567496052361731665</id><published>2010-03-17T06:17:00.000-07:00</published><updated>2010-03-25T04:28:11.273-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='arquitetura para testes'/><category scheme='http://www.blogger.com/atom/ns#' term='injeção de dependência'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='testes'/><category scheme='http://www.blogger.com/atom/ns#' term='service container'/><category scheme='http://www.blogger.com/atom/ns#' term='classe'/><title type='text'>Desacoplando para Testar  - Injeção de dependência, inversão de controle e outras palavras bonitas.</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Objetivo:&lt;/span&gt; Ensinar uma maneira de se estruturar uma aplicação para ela ser mais fácil de testar. Reduzir o tempo dos testes. Simplificar os testes usando mocks de maneiras não intrusivas.&lt;br /&gt;&lt;br /&gt;Uns dos pontos principais para o teste de software é:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;testar algo coeso, não atrapalhando um teste com funcionalidades de outras bibliotecas&lt;/li&gt;&lt;li&gt;ser rápido&lt;/li&gt;&lt;/ul&gt;Para isso geralmente ouvimos falar muito de mocks e stubs. Neste artigo vamos abordar não só uma maneira de usa-los como também como inserir estas ferramentas de maneira "transparente" no ambiente de testes, deixando o nosso código limpo. Vou começar de uma maneira simples e ir aprofundando no assunto para atender também a quem não está acostumado ao uso de mocks e stubs.&lt;br /&gt;&lt;br /&gt;Não vou entrar de jeito nenhum na discussão do que é mock e o que é um stub. Vou melhorar este termo e traduzir. Digamos que podemos usar um "orêia". Por exemplo, se temos alguma parte do software que acessa um serviço externo, provavelmente isso não vai acontecer em menos de 0,1 segundo e isso é MUITO demorado para um teste. Você não acha? Imagine que ao final de um projeto pequeno para médio você terá em torno de 1000 testes.&lt;br /&gt;&lt;br /&gt;Bom, então como fazemos para o nosso teste não demorar tanto? Usamos um mock, quer dizer stub, ou melhor: um "orêia". O nosso orêia vai ser usado no lugar do serviço original e fingir que executa o serviço. Ele pode até retornar algo estático ou que esteja em memória para simular o componente que ele está substituindo.&lt;br /&gt;&lt;br /&gt;Por exemplo, digamos que no método inserirTexto() usamos o serviço do yahoo de extrair palavras chaves. Para isso usamos uma classe nossa onde encapsulamos a chamada a esse serviço: a classe PalavraChaveExtractorTabajaras. então teremos algo assim:&lt;br /&gt;&lt;pre&gt;function inserirTexto($texto) {&lt;br /&gt;$this-&gt;palavrasChaves = PalavraChaveExtractorTabajaras::extrair($texto);&lt;br /&gt;$this-&gt;gravarNoBanco();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Beleza, então esse método extrai as palavras chaves e grava tudo no banco. Hum... e agora como vamos fazer para chamar esse método de um teste? Imaginem que temos o seguinte teste:&lt;br /&gt;&lt;pre&gt;function testDeveInserirTextoNoBanco() {&lt;br /&gt;$texto = $this-&gt;getTextoFixture();&lt;br /&gt;$conteudo = FabricaDoConteudo::criaUmNovoAiPorFavor();&lt;br /&gt;$conteudo-&gt;inserirTexto($texto);&lt;br /&gt;$conteudoNoBanco = RepositorioDoConteudo::lerPeloTexto($texto);&lt;br /&gt;$this-&gt;assertTrue((boolean)$conteudoNoBanco,"O conteúdo deve ser gravado no banco.");&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Mas como vamos fazer no nosso método inserirTexto para poder dizer que queremos usar o ôreia (Mock)?&lt;br /&gt;&lt;br /&gt;Temos que ter um jeito de fazer com que o sistema use o &lt;span style="font-family:monospace;"&gt;"&lt;/span&gt;PalavraChaveExtractorTabajaras" em um ambiente de produção e use o mock nos testes. Já sei, que tal...&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;if($ambiente == 'test') {&lt;br /&gt;$this-&gt;palavrasChaves = PalavraChaveExtractorTabajarasMock::extrair($texto);&lt;br /&gt;}&lt;br /&gt;else {&lt;br /&gt;$this-&gt;palavrasChaves = PalavraChaveExtractorTabajaras::extrair($texto);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Funcionar funciona, mas ainda não é algo muito bonito. Se eu quiser colocar outro ambiente vou ter que adicionar mais um if. Além disso, neste meu método, o ideal é que eu escreva o código de maneira fluente sem ter que me preocupar com testes. Já pensou ter que colocar estes ifs em todo canto. Vai ficar muito sujo.&lt;br /&gt;&lt;br /&gt;Bom, uma outra alternativa é criar um builder para o extractor assim:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class PalavraChaveExtractorTabajaras {&lt;br /&gt;static function build() {&lt;br /&gt;// retorna de acordo com o ambiente&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Eu teria que alterar a declaração do método "extrair", de estática para dinâmica, mas funciona melhorzinho, mas ainda assim eu acrescento código a minha classe que não tem a ver com ela. A minha classe não deve saber em que ambiente eu estou. Isso deve ficar para as classes das camadas mais altas, da aplicação ou do framework que eu estiver usando.&lt;br /&gt;&lt;br /&gt;E então?&lt;br /&gt;&lt;br /&gt;Então o melhor que já encontrei até agora para poder &lt;span style="font-weight: bold;"&gt;ter estes serviços variando de acordo com ambiente e não precisar saber de nada disso no código que usa o serviço&lt;/span&gt; é passar esta responsabilidade para uma outra classe. Esta classe vai gerenciar esse processo de instanciar e entregar os "serviços" de acordo com o ambiente que estamos. Essa classe geralmente é chamada de serviceContainer e já existem algumas implementações interessantes para ela em php, como por exemplo esta:&lt;br /&gt;&lt;br /&gt;http://components.symfony-project.org/dependency-injection/trunk/book/03-Service-Container&lt;br /&gt;&lt;br /&gt;Apesar de ser muito boa esta implementação, para um projeto que fizemos agora eu preferi escrever algo mais simples e que funcionou tão bem quanto, inclusive com algum ganho de performance, economizando em torno de 1m a cada requisição.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Porque o nome ServiceContainer ?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Porque a classe é um container que contém serviços. Por serviços podemos entender os objetos que implementam serviços como o de palavras chave do nosso exemplo, ou serviços de envio de email, ou até mesmo um repositório de objetos que se responsabiliza por cuidar da persistência de um determinado objeto.&lt;br /&gt;&lt;br /&gt;Assim no nosso código podemos ter algo como:&lt;br /&gt;&lt;pre&gt;function inserirTexto($texto) {&lt;br /&gt;$extrator = SimpleServiceContainer::getInstance()-&gt;getService('keywords');&lt;br /&gt;$this-&gt;palavrasChaves = $extrator-&gt;extrair($texto);&lt;br /&gt;$this-&gt;gravarNoBanco();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;O código fica um pouco mais extenso, mas continua claro. Estou pegando o serviço responsável por processar 'keywords'. Não sei qual serviço ele é, mas tudo bem, polimorfismo é isso aí.&lt;br /&gt;&lt;br /&gt;Desta maneira apenas o container centraliza a responsabilidade de definir em qual ambiente estamos. Também ele vai instanciar o objeto de acordo com o ambiente. Um outro benefício que ganhamos disso é que se precisarmos trocar serviço que usamos poderemos intanciar ele e configurar também de outra maneira que não seja a que usavamos antes.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;O SimpleServiceContainer&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Com base na mesma interface do container do symfony, eu criei esse aí mais simplezinho:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;class SimpleServiceContainer {&lt;br /&gt;&lt;br /&gt;public $parameters;&lt;br /&gt;static $containers = array();&lt;br /&gt;protected $services = array();&lt;br /&gt;&lt;br /&gt;protected $env;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* @return SimpleServiceContainer&lt;br /&gt;*/&lt;br /&gt;static function getContainer($env) {&lt;br /&gt;if(!array_key_exists($env,self::$containers)) {&lt;br /&gt;$class = 'SimpleServiceContainer_'.$env;&lt;br /&gt;if(!class_exists($class)) {&lt;br /&gt;   $class = 'SimpleServiceContainer_all';&lt;br /&gt;}&lt;br /&gt;self::$containers[$env] = new $class($env);&lt;br /&gt;}&lt;br /&gt;return self::$containers[$env];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static function getInstance() {&lt;br /&gt;return self::getContainer(ConfigSystem::get('environment'))&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function init() {}&lt;br /&gt;&lt;br /&gt;protected function __construct($env) {&lt;br /&gt;$this-&gt;env = $env;&lt;br /&gt;$this-&gt;init();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* @return SimpleServiceContainer&lt;br /&gt;*/&lt;br /&gt;function setParameter($name,$value) {&lt;br /&gt;$this-&gt;parameters[$name] = $value;&lt;br /&gt;return $this;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getParameter($name) {&lt;br /&gt;return $this-&gt;parameters[$name];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getService($serviceName) {&lt;br /&gt;if(!isset($this-&gt;services[$serviceName])) {&lt;br /&gt;$this-&gt;createService($serviceName);&lt;br /&gt;}&lt;br /&gt;return $this-&gt;services[$serviceName];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private function createService($serviceName) {&lt;br /&gt;$methodName = 'createService_'.$serviceName;&lt;br /&gt;if(!method_exists($this, $methodName)) {&lt;br /&gt;throw new ServiceBuilderNotConfiguredException(&lt;br /&gt; 'this container does not have a builder for the service called "'&lt;br /&gt; .$serviceName."', the method should be called ".$methodName&lt;br /&gt;);&lt;br /&gt;}&lt;br /&gt;$this-&gt;services[$serviceName] = $this-&gt;$methodName();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public function getInstantiatedServiceNames() {&lt;br /&gt;return array_keys($this-&gt;services);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Por padrão ele usa as definições do SimpleServiceContainer_all. Esse eu tenho que criar obrigatóriamente. Depois eu poso extender dele de acordo com o ambiente que eu quiser. Por exemplo, se eu chamo o ambiente de desenvolvimento de test eu posso criar as minhas classe da seguinte maneira:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class SimpleServiceContainer_all extends SimpleServiceContainer {&lt;br /&gt;&lt;br /&gt;protected function createService_keywords() {&lt;br /&gt;    return PalavraChaveExtractorTabajaras::build();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class SimpleServiceContainer_test extends SimpleServiceContainer_all {&lt;br /&gt;&lt;br /&gt;protected function createService_keywords() {&lt;br /&gt;    return PalavraChaveExtractorTabajarasMock::build();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Assim desta maneira, quando eu crio o meu serviço com:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$extrator = SimpleServiceContainer::getInstance()-&gt;getService('keywords');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Eu vou receber o mock para o ambiente de testes e o normal para quando eu não estiver rodando os meus testes. Pode parecer simples, mas faz toda a diferença.&lt;br /&gt;&lt;br /&gt;Veja:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;No meu código eu não digo como instanciar o serviço, pois o container funciona como uma &lt;span style="font-weight: bold;"&gt;factory&lt;/span&gt;. Assim qualquer outro componente que tenha a mesma interface pode ser substituido por ambiente eu até em um refactoring futuro sem impacto nas camadas que o usam.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Isso &lt;span style="font-weight: bold;"&gt;diminui o acoplamento&lt;/span&gt; das classes que usam o serviço com o serviço em sí.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Quando eu rodo meus testes o sistema automáticamente &lt;span style="font-weight: bold;"&gt;instancia o serviço de acordo com o ambiente de maneira transparente&lt;/span&gt; e me manda um componente que eu posso usar só sabendo a sua interface.&lt;/li&gt;&lt;li&gt;Isso permite eu &lt;span style="font-weight: bold;"&gt;usar mocks&lt;/span&gt; transparentemente nos testes e também em outros ambientes e até &lt;span style="font-weight: bold;"&gt;substituí-los futuramente por outras classes&lt;/span&gt; quando estas forem desenvolvidas. &lt;/li&gt;&lt;li&gt;Eu gosto desta implementação pois também instancio os objetos em &lt;span style="font-weight: bold;"&gt;puro php&lt;/span&gt;, sem a necessidade de um xml ou algo assim.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Os objetos são &lt;span style="font-weight: bold;"&gt;instanciados apenas quando necessito&lt;/span&gt; deles.&lt;/li&gt;&lt;li&gt;Os objetos dos serviços são por padrão &lt;span style="font-weight: bold;"&gt;instanciados uma vez só&lt;/span&gt; e usados em diferentes partes do sistema. Claro que isso pode não ser o comportamento desejado, mas pela simplicidade da implementação é fácil trocar este comportamento.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-4567496052361731665?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/4567496052361731665/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/03/desacoplando-para-testar-injecao-de.html#comment-form' title='2 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/4567496052361731665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/4567496052361731665'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/03/desacoplando-para-testar-injecao-de.html' title='Desacoplando para Testar  - Injeção de dependência, inversão de controle e outras palavras bonitas.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-7078858279565787381</id><published>2010-02-25T07:20:00.000-08:00</published><updated>2010-02-26T11:59:30.024-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='symfony'/><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ambientes'/><category scheme='http://www.blogger.com/atom/ns#' term='minificar'/><title type='text'>Diferentes JS em cada ambiente do Symfony.</title><content type='html'>Quem estiver querendo colocar um site em produção vai provavelmente se deparar com a necessidade de "minificar" os arquivos de javascript. Porém é interessante manter os arquivos não minificados no projeto também para ficar mais fácil de editar e debugar. Porém o view.yml do symfony não permite configurações sensíveis ao ambiente. Assim achei uma solução bem simples em &lt;a href="http://shout.setfive.com/2009/06/09/loading-different-javascript-css-files-in-different-environments/"&gt;http://shout.setfive.com/2009/06/09/loading-different-javascript-css-files-in-different-environments/&lt;/a&gt; que é a seguinte:&lt;br /&gt;&lt;br /&gt;1 - Crie as configurações (um array) para cada ambiente no app.yml com o nome de "javascript_files". &lt;br /&gt;&lt;br /&gt;2 - no view.yml faça referência a estas configurações usando:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;javascripts: [&lt; ? php echo implode(',',sfConfig::get('app_javascript_files');) ? &gt;]  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Depois me disseram que o view.yml está "deprecated", então fiz o seguinte:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Incluí uma partial em todos meus leiautes e nessa parcial usei:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt; ?php if(sfConfig::get('sf_debug')): ? &gt;&lt;br /&gt;   &lt; ?php use_javascript('x') ?&gt;&lt;br /&gt;   &lt; ?php use_javascript('y') ?&gt;&lt;br /&gt;   &lt; ?php use_javascript('z') ?&gt;&lt;br /&gt;&lt; ? else: ? &gt;&lt;br /&gt;   &lt; ?php use_javascript('xyz.min.js') ?&gt;&lt;br /&gt;&lt; ? endif; ? &gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-7078858279565787381?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/7078858279565787381/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/02/diferentes-js-em-cada-ambiente-do.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/7078858279565787381'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/7078858279565787381'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/02/diferentes-js-em-cada-ambiente-do.html' title='Diferentes JS em cada ambiente do Symfony.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-26412580110816171</id><published>2010-02-24T07:28:00.001-08:00</published><updated>2010-02-26T13:07:46.810-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='symfony produção exceção erro listener event'/><title type='text'>Tratando exceções no ambiente de produção.</title><content type='html'>&lt;span style="font-style:italic;"&gt;* ver o primeiro comentário para ajustar um bug de loop eterno na homepage&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Objetivo: &lt;/span&gt;Logar qualquer exceção não tratada no código, mandar um email para a equipe, gravar um log e mostrar uma mensagem para o usuário do tipo: "Estamos em manutenção para melhor servir." enquanto a equipe corre pra consertar o erro.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Como Fazer&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1&lt;/span&gt; Primeiro conectamos um listener ao evento de exception. Inicialmente pensei em capturar isso com um filtro, mas vi que existem algumas exceções que são capturadas que atrapalham o bom funcionamento do framework, como por exemplo a ReflectionException que vai gerar um 404 mais na frente.&lt;br /&gt;&lt;br /&gt;Usando um listener para o evento, capturamos apenas aquilo que o symfony não trata:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class frontendConfiguration extends sfApplicationConfiguration {&lt;br /&gt;public function configure() {&lt;br /&gt;    $this-&gt;dispatcher-&gt;connect(&lt;br /&gt;        'application.throw_exception',&lt;br /&gt;        array('MyExceptionHandler', 'listenToApplicationException')&lt;br /&gt;    );&lt;br /&gt;}&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2&lt;/span&gt; Depois criamos a classe que ser chamada, contendo o método que vai "ouvir" o evento, conforme determinamos acima:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class MyExceptionHandler extends sfFilter {&lt;br /&gt;static public function listenToApplicationException($event)&lt;br /&gt;{&lt;br /&gt;   // pegamos a exception original&lt;br /&gt;   $e = $event-&gt;getSubject();&lt;br /&gt;   // para passar um link na mensagem mostrada ao usuário&lt;br /&gt;   sfLoader::loadHelpers(array('Url'));&lt;br /&gt;   try {&lt;br /&gt;       // manda email e grava um log&lt;br /&gt;       self::notify($e);&lt;br /&gt;   }&lt;br /&gt;   catch(Exception $e) {&lt;br /&gt;       // "Engulindo" propositadamente os&lt;br /&gt;       // erros quando envia email de log&lt;br /&gt;       // para não impedir que a mensagem&lt;br /&gt;       // seja mostrada para o usuário&lt;br /&gt;   }&lt;br /&gt;   if(self::isAjax()) {&lt;br /&gt;       // lembre dos requests de ajax...&lt;br /&gt;       // tratar de acordo com o framework do freguês.&lt;br /&gt;   }&lt;br /&gt;   else {&lt;br /&gt;       sfContext::getInstance()-&gt;getUser()-&gt;setFlash(&lt;br /&gt;           'feedback',&lt;br /&gt;           sprintf(&lt;br /&gt;               Messages::UNEXPECTED_ERROR,&lt;br /&gt;               url_for('@support')&lt;br /&gt;           )&lt;br /&gt;       );&lt;br /&gt;       sfContext::getInstance()-&gt;getController()-&gt;redirect('@homepage');&lt;br /&gt;   }&lt;br /&gt;   // este evento específico chama cada listener até que um responda que pôde tratar o evento. Por isso esse retorno.&lt;br /&gt;   return true;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static function notify(Exception $e) {&lt;br /&gt;   // cria a mensagem...manda email e registra um log.&lt;br /&gt;   self::log($mensagem);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static function log($message) {&lt;br /&gt;   $exceptionLogLocation = sfConfig::get('sf_log_dir') . '/exceptions.log';&lt;br /&gt;   $fileHandle = fopen($exceptionLogLocation, "a+");&lt;br /&gt;   fwrite($fileHandle, $message."\n");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static function isAjax(){&lt;br /&gt;   return sfContext::getInstance()-&gt;getRequest()-&gt;isXmlHttpRequest() ||&lt;br /&gt;   sfContext::getInstance()-&gt;getRequest()-&gt;hasParameter('iframeAjaxUpload');&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3&lt;/span&gt; Feito. Fica aqui mais uma dica de algumas coisas que você pode capturar e colocar na mensagem para facilitar a vida depois:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;        $username = sfContext::getInstance()-&gt;getUser()-&gt;getService()-&gt;getUsername();&lt;br /&gt;        $referer = sfContext::getInstance()-&gt;getRequest()-&gt;getReferer();&lt;br /&gt;        $uri = sfContext::getInstance()-&gt;getRequest()-&gt;getUri();&lt;br /&gt;        $ajax = self::isAjax() ? 'Yes' : 'No';&lt;br /&gt;        'time' =&gt; date('Y-m-d H:i:s'),&lt;br /&gt;        'exception_class' =&gt; get_class($e),&lt;br /&gt;        'exception_message' =&gt; $e-&gt;getMessage(),&lt;br /&gt;        'trace' =&gt; $e-&gt;__toString()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-26412580110816171?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/26412580110816171/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/02/tratando-excecoes-no-ambiente-de.html#comment-form' title='5 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/26412580110816171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/26412580110816171'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/02/tratando-excecoes-no-ambiente-de.html' title='Tratando exceções no ambiente de produção.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-2304713194728269425</id><published>2010-01-13T12:39:00.000-08:00</published><updated>2010-01-13T12:42:29.902-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='js html index índice jquery'/><title type='text'>Método para Gerar Índice para HTML com JS</title><content type='html'>&lt;pre&gt;&lt;br /&gt;var htmlMenu = {&lt;br /&gt;    generate : function htmlMenu(id) {&lt;br /&gt;        var headers = {};&lt;br /&gt;        var stack = [];&lt;br /&gt;        var lastLevel = 0;&lt;br /&gt;        var text,level;&lt;br /&gt;        var levels = {'H1':0,'H2':1,'H3':2,'H4':3,'H5':4}&lt;br /&gt;        var menu = '';&lt;br /&gt;        $('#' + id + ' :header').each(function(a){&lt;br /&gt;            actualLevel = levels[this.nodeName];&lt;br /&gt;            if(actualLevel &lt; lastLevel) {&lt;br /&gt;              for(var r = actualLevel+1; r &lt;= lastLevel; r++) {&lt;br /&gt;                  delete stack[r];&lt;br /&gt;              }&lt;br /&gt;            }&lt;br /&gt;            text = $(this).html();&lt;br /&gt;            if(stack[actualLevel] == undefined) {&lt;br /&gt;                stack[actualLevel] = 1;&lt;br /&gt;            }&lt;br /&gt;            else {&lt;br /&gt;                stack[actualLevel]++;&lt;br /&gt;            }&lt;br /&gt;            var index = '';&lt;br /&gt;            var first = true;&lt;br /&gt;            for(i in stack) {&lt;br /&gt;                index += (first ? '' : '.') + stack[i];&lt;br /&gt;                first = false;&lt;br /&gt;            }&lt;br /&gt;            headers[index] = text;&lt;br /&gt;            $(this).append('&amp;lt;a name =" '"&amp;gt;');&lt;br /&gt;            menu += "&amp;lt;a href="'#"&amp;gt;" + index + " - " + text + "&amp;lt;/a&amp;gt;&amp;lt;br/&amp;gt;";&lt;br /&gt;            lastLevel = actualLevel;&lt;br /&gt;        })&lt;br /&gt;        return menu;&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-2304713194728269425?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/2304713194728269425/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2010/01/metodo-para-gerar-indice-para-html-com.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/2304713194728269425'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/2304713194728269425'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2010/01/metodo-para-gerar-indice-para-html-com.html' title='Método para Gerar Índice para HTML com JS'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-8003666865997301408</id><published>2009-12-17T07:55:00.001-08:00</published><updated>2009-12-17T07:57:49.228-08:00</updated><title type='text'>Ver um Json no Browser Firefox de uma maneira decente.</title><content type='html'>Para ver um Json no firefox, use este plugin:&lt;br /&gt;&lt;br /&gt;http://benhollis.net/blog/2009/02/24/jsonview-view-json-documents-in-firefox/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-8003666865997301408?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/8003666865997301408/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/12/vendo-um-json-no-browser-firefox-de-uma.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/8003666865997301408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/8003666865997301408'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/12/vendo-um-json-no-browser-firefox-de-uma.html' title='Ver um Json no Browser Firefox de uma maneira decente.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-6040881753192598802</id><published>2009-10-06T05:16:00.000-07:00</published><updated>2009-10-06T05:25:42.089-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testes unitários'/><category scheme='http://www.blogger.com/atom/ns#' term='código limpo'/><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='refactoring'/><category scheme='http://www.blogger.com/atom/ns#' term='programação'/><title type='text'>Tá difícil escrever testes para o seu código?</title><content type='html'>Claro que sim. Você escreveu o código primeiro. Que tal escrever código para os seus testes. Agora sim. Se precisar de refactoring, os testes garantem. Agora aquele código sem testes vai ter que ter refactoring para poder ser testado? Duas perdas de tempo:&lt;br /&gt;&lt;br /&gt;1 refactoring sem uma malha de testes (tortura é bem parecido)&lt;br /&gt;2 escrever testes tão depois de escrever código. &lt;br /&gt;&lt;br /&gt;Além disso com certeza aí tem código inútil.... não foi escrito para passar nos testes, mas sim por que talvez um dia você possa precisar não é mesmo? O nome desse código para alguns pode ser "visão de futuro", mas para mim chama "teia de aranha". Só serve para se enrolar nele e atrapalhar um caminhar mais direto.&lt;br /&gt;&lt;br /&gt;Digo isso vivi isso no dia a dia agorinha mesmo. Testes primeiro, caminho tranquilo, por a cabeça no travesseiro e dormir...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-6040881753192598802?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/6040881753192598802/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/10/ta-dificil-escrever-testes-para-o-seu.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/6040881753192598802'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/6040881753192598802'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/10/ta-dificil-escrever-testes-para-o-seu.html' title='Tá difícil escrever testes para o seu código?'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-1268447052539434095</id><published>2009-07-09T05:41:00.000-07:00</published><updated>2010-04-19T17:37:21.325-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plugins'/><category scheme='http://www.blogger.com/atom/ns#' term='externals'/><category scheme='http://www.blogger.com/atom/ns#' term='svn:externals'/><category scheme='http://www.blogger.com/atom/ns#' term='modular'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>Gerenciando plugins em um repositório separado com svn:externals.</title><content type='html'>Se temos um código que queremos usar em dois projetos, mas por algum motivo, não queremos manter em um lugar centralizado e usar o "include_path" do php. Podemos criar um repositório só para este código e gerencia-lo como um novo projeto.&lt;br /&gt;&lt;br /&gt;Este recurso é muito útil para plugins que são usados em mais de um projeto. Procurando na net é um tanto fraca a documentação a respeito de como usar o recurso svn:externals. No svn book ele mostra as informações fragmentadas e não o processo todo.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Neste texto eu descrevo o processo de criar um novo repositório, exportar os arquivos para ele e então gerar o link do externals apontando para o novo repositório. &lt;/span&gt; Vou descrever boa parte destas tarefas considerando o uso do tortoise no lado do cliente, mas a parte do externals também vou colocar a linha de comando, visto que a documentação no livro é muito fragmentada.&lt;br /&gt;&lt;br /&gt;1 - Exportar o arquivos para uma nova pasta.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Usando o tortoise, escolhemos a pasta e selecionamos "export". Isso irá criar uma nova pasta não versionada, com tudo o que tinha ali dentro. Não versionada quer dizer sem aquele monte de .svn que ele cria.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;2 - Criar o repositório no servidor.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;svnadmin create jsCallPlugin&lt;br /&gt;&lt;br /&gt;Neste ponto é importante verificar se a pasta tem permissões para ser alterada pelo usuário do svn. Existem várias maneiras de se fazer isso, mas uma que pode ser utilizada é atribuir ao diretório um grupo compartilhado por todos os usuários do svn.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;3 - Fazer um check out do repositório na sua máquina.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;com o tortoise, é só clicar na pasta e então escolher o checkout, colocar o endereço e pronto.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;4 - Mandar os arquivos pro novo repositório.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Lembra aqueles arquivos que fizemos export. Pois é, estes arquivos devem ser copiados para o repositório. Veja que é uma boa prática criar as pastas trunk, branches e tags. Neste caso podemos copiar nossos arquivos para dentro da trunk. Depois é só fazer o "commit" normalmente.&lt;br /&gt;&lt;br /&gt;Este é o método que considero mais simples, mas você também pode, tendo os arquivos no servidor, usar um import para coloca-los no novo repositório.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;5 - Definir o externals.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Finalmente o que interessa.&lt;br /&gt;&lt;br /&gt;O svn:externals é uma propriedade que atribuimos a um diretório na nossa cópia de trabalho. Esta propriedade será enviada para o servidor quando fizermos o commit. Esta propriedade indica que, ao se atualizar uma cópia de trabalho, o svn deverá buscar, para colocar dentro do diretório ao qual atribuimos a propriedade, dados de um outro repositório. Então precisamos entender 3 coisas nessa configuração:&lt;br /&gt;&lt;br /&gt;a) o diretório que tem a propriedade (diretório local)&lt;br /&gt;b) o caminho do repositório&lt;br /&gt;c) o nome do novo diretório que será criado (diretório externo)&lt;br /&gt;&lt;br /&gt;Sabendo destes elementos fica fácil não cair em erros comuns que vemos pelos foruns da net.&lt;br /&gt;&lt;br /&gt;Então, &lt;span style="font-weight: bold;"&gt;para fazer o externals do jeito mais difícil&lt;/span&gt; (no dos) usamos o seguinte comando:&lt;br /&gt;&lt;br /&gt;svn propedit svn:externals . --editor-cmd edit&lt;br /&gt;&lt;br /&gt;O ponto é o endereço do diretório local ao qual atribuimos a prop. (que pode ser trocado, é claro)&lt;br /&gt;&lt;br /&gt;O comando --editor-cmd indica que vamos usar o edit.bat para editar as propriedades.&lt;br /&gt;&lt;br /&gt;bom, dentro deste arquivo precisamos colocar os nomes dos diretórios externos e seus respectivos repositórios:&lt;br /&gt;&lt;br /&gt;dir1 http://repos/plugin1/trunk&lt;br /&gt;dir2 http://repos/plugin2/trunk&lt;br /&gt;&lt;br /&gt;e depois é só salvar o arquivo normalmente. No próximo update, o svn vai buscar destes repositórios e criar as pastas dir1 e dir2, trazendo o conteúdo de plugin1 e plugin2 respectivamente.&lt;br /&gt;&lt;br /&gt;Bom, &lt;span style="font-weight: bold;"&gt;e do jeito mais fácil ?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;No tortoise, basta clicar sobre o diretório local e escolher o comando properties (dentro dos comandos do tortoise, é claro). Com isso vai aparecer uma lista das propriedades e suas configurações. Adicione a propriedade svn:externals com uma linha com "[nomedodiretorio] [urldorepositorio]" para cada externals&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-1268447052539434095?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/1268447052539434095/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/07/gerenciando-plugins-em-um-repositorio.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/1268447052539434095'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/1268447052539434095'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/07/gerenciando-plugins-em-um-repositorio.html' title='Gerenciando plugins em um repositório separado com svn:externals.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-5748434599140886803</id><published>2009-06-12T06:45:00.000-07:00</published><updated>2009-06-12T06:46:50.577-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='js'/><category scheme='http://www.blogger.com/atom/ns#' term='function'/><category scheme='http://www.blogger.com/atom/ns#' term='tipo'/><category scheme='http://www.blogger.com/atom/ns#' term='método'/><category scheme='http://www.blogger.com/atom/ns#' term='string'/><title type='text'>Atribuindo métodos a tipos do js, adicionando um método .repeat() a todas as strings.</title><content type='html'>&lt;code&gt;&lt;br /&gt;String.prototype.repeat = function(numTimes) {&lt;br /&gt;    var ret = '';&lt;br /&gt;    for(var a = 0; a &lt; numTimes;a++) {&lt;br /&gt;        ret += this;&lt;br /&gt;    }&lt;br /&gt;    return ret;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-5748434599140886803?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/5748434599140886803/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/06/atribuindo-metodos-tipos-do-js.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/5748434599140886803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/5748434599140886803'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/06/atribuindo-metodos-tipos-do-js.html' title='Atribuindo métodos a tipos do js, adicionando um método .repeat() a todas as strings.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-1274413384826139526</id><published>2009-05-13T10:25:00.001-07:00</published><updated>2009-05-13T10:28:29.424-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Zend Framework'/><category scheme='http://www.blogger.com/atom/ns#' term='symfony'/><category scheme='http://www.blogger.com/atom/ns#' term='autoload'/><category scheme='http://www.blogger.com/atom/ns#' term='ZF'/><title type='text'>Usando classes do Zend Framework no Symfony</title><content type='html'>1 - incluir as classes em plugins/Zend/lib&lt;br /&gt;&lt;br /&gt;2 - configurar o caminho no settings.yml&lt;br /&gt;&lt;br /&gt;    # Zend Framework    &lt;br /&gt;    zend_autoload:      on&lt;br /&gt;    zend_lib_dir:      "%SF_PLUGINS_DIR%/Zend/lib"&lt;br /&gt;&lt;br /&gt;3 - inicializar o autoload no frontendConfiguration:&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * @package    system&lt;br /&gt; * @subpackage configuration&lt;br /&gt; */&lt;br /&gt;class frontendConfiguration extends sfApplicationConfiguration&lt;br /&gt;{&lt;br /&gt;    public function configure()&lt;br /&gt;    {&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public function initialize()&lt;br /&gt;    {&lt;br /&gt;        parent::initialize();&lt;br /&gt;        /**&lt;br /&gt;         * Zend Framework Autoloading&lt;br /&gt;         */&lt;br /&gt;        if(sfConfig::get('sf_zend_autoload', true) &amp;&amp; ($sf_zend_lib_dir = sfConfig::get('sf_zend_lib_dir'))) {&lt;br /&gt;            set_include_path($sf_zend_lib_dir.PATH_SEPARATOR.get_include_path());&lt;br /&gt;            require_once($sf_zend_lib_dir.DIRECTORY_SEPARATOR.'Zend'.DIRECTORY_SEPARATOR .'Loader.php');&lt;br /&gt;            spl_autoload_register(array('Zend_Loader', 'loadClass'));&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        /**&lt;br /&gt;         * DOMPdf Autoloading&lt;br /&gt;         */&lt;br /&gt;        if(sfConfig::get('sf_dompdf_autoload', true) &amp;&amp; ($sf_dompdf_lib_dir = sfConfig::get('sf_dompdf_lib_dir'))) {&lt;br /&gt;            set_include_path($sf_dompdf_lib_dir.PATH_SEPARATOR.get_include_path());&lt;br /&gt;            //            require_once($sf_dompdf_lib_dir.DIRECTORY_SEPARATOR.'dompdf_config.inc.php');&lt;br /&gt;            //            spl_autoload_register('DOMPDF_autoload');&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-1274413384826139526?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/1274413384826139526/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/05/usando-classes-do-zend-framework-no.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/1274413384826139526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/1274413384826139526'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/05/usando-classes-do-zend-framework-no.html' title='Usando classes do Zend Framework no Symfony'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-18792874678321728</id><published>2009-04-14T12:26:00.001-07:00</published><updated>2009-04-14T12:28:33.349-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='listagem'/><category scheme='http://www.blogger.com/atom/ns#' term='diretorio'/><category scheme='http://www.blogger.com/atom/ns#' term='SPL'/><category scheme='http://www.blogger.com/atom/ns#' term='directory'/><category scheme='http://www.blogger.com/atom/ns#' term='dir'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='RecursiveDirectoryIterator'/><title type='text'>Lendo um diretório com SPL</title><content type='html'>&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;$r = new RecursiveIteratorIterator(&lt;br /&gt;  new RecursiveDirectoryIterator(dirname(__FILE__)),&lt;br /&gt;  RecursiveIteratorIterator::LEAVES_ONLY&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;foreach ($r as $file) {&lt;br /&gt;  echo $file.'&lt;br/&gt;';&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-18792874678321728?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/18792874678321728/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/04/lendo-um-diretorio-com-spl.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/18792874678321728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/18792874678321728'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/04/lendo-um-diretorio-com-spl.html' title='Lendo um diretório com SPL'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-6829873981236578186</id><published>2009-04-07T05:34:00.000-07:00</published><updated>2009-04-07T05:36:35.894-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='my.cnf'/><category scheme='http://www.blogger.com/atom/ns#' term='charset'/><category scheme='http://www.blogger.com/atom/ns#' term='utf8'/><category scheme='http://www.blogger.com/atom/ns#' term='log'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><title type='text'>Pequenos ajustes no mysql.... (bin/my.cnf)</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Para configurar o log das querys:&lt;/span&gt;&lt;br /&gt;log="C:/tmp/mysqllog/mysqld.log"&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Para definir o charset padrão&lt;/span&gt;&lt;br /&gt;character_set_server=UTF8&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-6829873981236578186?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/6829873981236578186/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/04/pequenos-ajustes-no-mysql-binmycnf.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/6829873981236578186'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/6829873981236578186'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/04/pequenos-ajustes-no-mysql-binmycnf.html' title='Pequenos ajustes no mysql.... (bin/my.cnf)'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-3236579882135147035</id><published>2009-03-31T06:49:00.000-07:00</published><updated>2009-03-31T06:51:52.908-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='input'/><category scheme='http://www.blogger.com/atom/ns#' term='enter'/><title type='text'>[snippet] Adicionando inputs ao pressionar enter</title><content type='html'>Inserindo mais inputs no enter:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;adminEssays.form = {};&lt;br /&gt;&lt;br /&gt;adminEssays.form.addInput = function(target) {&lt;br /&gt;        var nextInput;&lt;br /&gt;        inputs = $("#category-subs").children('input');&lt;br /&gt;        if(inputs.length == 0) {&lt;br /&gt;            nextInput = 0;&lt;br /&gt;        }&lt;br /&gt;        else {&lt;br /&gt;          nextInput = 0;&lt;br /&gt;          for (var a = 0; a &lt; inputs.length; a++) {&lt;br /&gt;            id = parseInt(inputs[a].id.substr(21));&lt;br /&gt;            if (id &gt;= nextInput) {&lt;br /&gt;                nextInput = id + 1;&lt;br /&gt;            }&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;        id = "category-subcategory-" + nextInput;&lt;br /&gt;        html = "&lt; input type='text' size='70' id='" + id + "' class='category-subcategory' name='subcategory[]' style='display:block'/&gt;";&lt;br /&gt;        if(nextInput == 0) {&lt;br /&gt;            $('#category-subs').html(html);&lt;br /&gt;        }&lt;br /&gt;        else {&lt;br /&gt;            $(target).after(html);&lt;br /&gt;        }&lt;br /&gt;        $(".category-subcategory").bind("keypress",adminEssays.form.inputEnter);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;adminEssays.form.submitForm = function(id) {&lt;br /&gt;    JsonC.submitForSfAction('adminEssays/insertSubcategories',{},'form-insert-subcategories','form-insert-subcategories-loading');&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;adminEssays.form.inputEnter = function(evt) {&lt;br /&gt;    if(evt.keyCode == 8 || evt.keyCode == 46) {&lt;br /&gt;        var tgt = $(evt.currentTarget);&lt;br /&gt;        if(tgt.val() == '') {&lt;br /&gt;            if( $('#category-subs').children().length &gt; 1 ) {&lt;br /&gt;                var inputToFocus;&lt;br /&gt;                if(tgt.next().length &gt; 0) {&lt;br /&gt;                    inputToFocus = tgt.next()[0];&lt;br /&gt;                }&lt;br /&gt;                else {&lt;br /&gt;                    inputToFocus = tgt.prev()[0];&lt;br /&gt;                }&lt;br /&gt;                tgt.remove();&lt;br /&gt;                inputToFocus.focus();&lt;br /&gt;            }&lt;br /&gt;            return false;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    if(evt.keyCode == 13) {&lt;br /&gt;      prox = $(evt.currentTarget).next().length;&lt;br /&gt;      if(prox &gt; 0) {&lt;br /&gt;          $($(evt.currentTarget).next()[0]).focus();&lt;br /&gt;      }&lt;br /&gt;      else {&lt;br /&gt;          adminEssays.form.addInput(evt.currentTarget);&lt;br /&gt;          $($(evt.currentTarget).next()[0]).focus();&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-3236579882135147035?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/3236579882135147035/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/03/snippet-adicionando-inputs-ao.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/3236579882135147035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/3236579882135147035'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/03/snippet-adicionando-inputs-ao.html' title='[snippet] Adicionando inputs ao pressionar enter'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-2142520972504438713</id><published>2009-03-05T07:49:00.000-08:00</published><updated>2009-03-05T07:54:21.126-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='js'/><category scheme='http://www.blogger.com/atom/ns#' term='Tree'/><category scheme='http://www.blogger.com/atom/ns#' term='dir'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='diretório'/><category scheme='http://www.blogger.com/atom/ns#' term='arquivo'/><title type='text'>Criando uma estrutura de arvore para o jsTree</title><content type='html'>&lt;pre&gt;&lt;br /&gt;public function createTreeStructure($dir) {&lt;br /&gt;    $ar = array(&lt;br /&gt;      'attributes'=&gt;array('id'=&gt;'node-root'),&lt;br /&gt;      'data'=&gt;'TESTS',&lt;br /&gt;      'children'=&gt;$this-&gt;createChildren($dir)&lt;br /&gt;    );&lt;br /&gt;    return $ar;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public function createChildren($dir) {&lt;br /&gt;    static $n=0;&lt;br /&gt;    $it = new RecursiveDirectoryIterator($dir);&lt;br /&gt;    $arAll = array();&lt;br /&gt;    foreach($it as $file) {&lt;br /&gt;      if(strpos($file,'.svn') !== false) continue;&lt;br /&gt;      $ar = array(&lt;br /&gt;        'attributes'=&gt;array('id'=&gt;'node-'.$n),&lt;br /&gt;        'data'=&gt;$file-&gt;getFilename()&lt;br /&gt;      );&lt;br /&gt;      if($file-&gt;isDir()) {&lt;br /&gt;        $ar['children'] = $this-&gt;createChildren($file);&lt;br /&gt;      }&lt;br /&gt;      $arAll[] = $ar;&lt;br /&gt;    }&lt;br /&gt;    return $arAll;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-2142520972504438713?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/2142520972504438713/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/03/criando-uma-estrutura-de-arvore-para-o.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/2142520972504438713'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/2142520972504438713'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/03/criando-uma-estrutura-de-arvore-para-o.html' title='Criando uma estrutura de arvore para o jsTree'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-2055487609264438712</id><published>2009-03-05T04:02:00.000-08:00</published><updated>2009-03-05T07:52:29.757-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gerência'/><category scheme='http://www.blogger.com/atom/ns#' term='citação'/><category scheme='http://www.blogger.com/atom/ns#' term='código'/><category scheme='http://www.blogger.com/atom/ns#' term='gerenciamento'/><category scheme='http://www.blogger.com/atom/ns#' term='qualidade de código'/><title type='text'>O valor de longo prazo de um software é diretamente proporcional a qualidade do código.</title><content type='html'>Já imaginou se todo gerente e toda organização tivessem esta consciência? Seria bem mais fácil de fazer um bom trabalho. Pena que de maneira geral o brasileiro ainda não tem visão de médio e longo prazo.&lt;br /&gt;&lt;br /&gt;O que acontece é que o custo de desenvolvimento de um software, se for bem feito, pode reduzir muito o custo do próximo pelas bibliotecas geradas. E também a manutenção em bom código é muito mais fácil.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-2055487609264438712?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/2055487609264438712/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/03/o-valor-de-longo-prazo-de-um-software-e.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/2055487609264438712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/2055487609264438712'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/03/o-valor-de-longo-prazo-de-um-software-e.html' title='O valor de longo prazo de um software é diretamente proporcional a qualidade do código.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-3598331014297835068</id><published>2009-03-02T03:51:00.000-08:00</published><updated>2009-03-02T04:13:07.133-08:00</updated><title type='text'>Module Pattern</title><content type='html'>Existem diversos bons artigos na net a este respeito. Aqui não vou tentar explorar todas as possibilidades deste padrão. Pretendo apenas dar uma explicação básica do seu funcionamento. &lt;br /&gt;&lt;br /&gt;O "Module Patern" tira proveito dos "closures" do JS. Quando uma função declara variáveis dentro dela e também declara outras funções ou objetos estes objetos e funções têm acesso às variáveis declaradas. Ex:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pessoa = function(nome,telefone) {&lt;br /&gt;  var nome = nome;&lt;br /&gt;  var telefone = telefone;&lt;br /&gt;  this.nome2 = "nome2 - " + nome;&lt;br /&gt;  return {&lt;br /&gt;    mostra: function() {&lt;br /&gt;      document.writeln('Nome:' + nome + '&lt; br / &gt;');&lt;br /&gt;      document.writeln('Telefone:' + telefone + '&lt; br / &gt;');&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A função "mostra" do objeto retornado no código acima, por estar definida dentro da função pessoa, tem acesso às variáveis "nome" e "telefone". &lt;br /&gt;&lt;br /&gt;O interessante é que &lt;b&gt;mesmo depois de a função pessoa ter sido executada&lt;/b&gt;, o objeto continua tendo acesso àquelas variáveis. Isto quer dizer que se chamarmos:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;p = pessoa('joao','3344 5678');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;vai acontecer o seguinte:&lt;br /&gt;&lt;br /&gt;1 - a função pessoa será executada declarando as variáveis "nome" e "telefone"&lt;br /&gt;2 - ela vai retornar um objeto, definido por um código JSON, com um método mostra()&lt;br /&gt;3 - este método mostra vai ter acesso às variáveis "nome" e "telefone" já declaradas.&lt;br /&gt;4 - como a função já foi executada, não temos mais como mexer nas variáveis "nome" e "telefone" a não ser pelo "mostra".&lt;br /&gt;5 - com isso criamos um escopo "privado" para as variaveis definidas.&lt;br /&gt;&lt;br /&gt;O mesmo método pode ser usado para retornar uma função:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mostraLista = function(nome,telefone) {&lt;br /&gt;  var lista = {'segredo1':'noite','segredo2':'dia'};&lt;br /&gt;  return function() {&lt;br /&gt;    for(i in lista) {&lt;br /&gt;   document.writeln(i + ':' + lista[i] + '&lt; br / &gt;');&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Neste caso repare que depois da declaração já executamos a função. Com isso fazemos o seguinte:&lt;br /&gt;&lt;br /&gt;1 - a função é executada declarando o objeto "lista";&lt;br /&gt;2 - ela retorna uma outra função, que fica atribuida a "mostraLista"&lt;br /&gt;3 - esta função retornada fica com acesso "privado" a variável "lista"&lt;br /&gt;&lt;br /&gt;assim podemos chamar&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mostraLista();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;para que este método seja executado com acesso ao objeto. Porém não podemos acessar diretamente por "mostraLista.lista" pois este objeto não existe, o que existe é o acesso dele à variável da função que já foi executada. Caso você queira experimentar, veja que o valor será igual a "undefined".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-3598331014297835068?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/3598331014297835068/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/03/module-pattern.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/3598331014297835068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/3598331014297835068'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/03/module-pattern.html' title='Module Pattern'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-6518110354451607165</id><published>2009-02-17T12:46:00.000-08:00</published><updated>2009-03-05T07:55:04.231-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='js'/><category scheme='http://www.blogger.com/atom/ns#' term='construtor'/><category scheme='http://www.blogger.com/atom/ns#' term='função'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><category scheme='http://www.blogger.com/atom/ns#' term='chamada'/><category scheme='http://www.blogger.com/atom/ns#' term='this'/><title type='text'>Chamada a métodos no Js.</title><content type='html'>Este post é do tipo "um código vale mais do que mil palavras", assim sendo dificilmente você vai aprender alguma coisa apenas vendo o código escrito. Porém se você pegar cada pequeno bloco de código e rodar (O que pode ser feito, acredito, até com o firebug), você vai provavelmente entender os tipos possíveis de chamada em js e o que acontece com a palavra-chave "this" em cada um dos tipos. Podemos entender isso como qual o escopo no qual a função é rodada. &lt;br /&gt;&lt;br /&gt;Vale uma ressalva de que na maioria, o conteúdo que escrevo aqui, aprendi no excelente livro que recomendo: Javascript: The Good Parts - O'REILLY, Douglas Crockford.&lt;br /&gt;&lt;br /&gt;Bão, ao código...&lt;br /&gt;&lt;br /&gt;O Js é uma linguagem atípica, herança por "prototype", sem classes, funções são objetos, vinculação tardia da palavra chave "this", etc... Por conta disso muita gente (eu inclusive) que começa a trabalhar com ele não entende nada. Assim vejo que é importante esclarecer aspectos "simples" da linguagem, como o que acontece quando chamamos uma função.&lt;br /&gt;&lt;br /&gt;Aqui vão exemplos de quatro tipos de chamada:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Chamada á Função&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// quando uma função não pertence a nenhum método...&lt;br /&gt;function ola() {&lt;br /&gt;  document.writeln('ola&lt;br/&gt;');&lt;br /&gt;  // o this neste caso é vinculado ao objeto global.&lt;br /&gt;  document.writeln(this);&lt;br /&gt;}&lt;br /&gt;ola();&lt;br /&gt;&lt;br /&gt;####### com funções internas&lt;br /&gt;/*&lt;br /&gt;já no caso da função estar dentro de uma outra é necessário &lt;br /&gt;uma "solução de contorno" para ter acesso às propriedades da externa. &lt;br /&gt;*/&lt;br /&gt;var ola2 = function() {&lt;br /&gt;  var that = this;&lt;br /&gt;  this.texto = "ola";&lt;br /&gt;  var fala = function() {&lt;br /&gt;    document.writeln(that.texto + "2&lt;br/&gt;");&lt;br /&gt; // segundo o livro, se usasse this direto aqui dentro não &lt;br /&gt; // funcionaria pois ele ficaria vinculado ao objeto global. &lt;br /&gt; // mas no exemplo abaixo, funciona.&lt;br /&gt;    document.writeln(this.texto + "---2&lt;br/&gt;");&lt;br /&gt; this.texto2 = 'aaaaaa';&lt;br /&gt;  }&lt;br /&gt;  fala();&lt;br /&gt;  document.writeln('texto2:' + this.texto2 + "&lt;br/&gt;");&lt;br /&gt;}&lt;br /&gt;//ola2.fala(); -&gt; daria erro pois não foi ligado ao this de ola2, é apenas funçao interna.&lt;br /&gt;ola2();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Chamada ao Construtor&lt;/span&gt;&lt;br /&gt;* esta maneira é indicada como a "pior dos dois mundos", pois imita uma linguagem com classes, mas não tem, e não deixa claro o uso do "prototype".&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var Pessoa = function(nome) {&lt;br /&gt;  this.nome = nome;&lt;br /&gt;}&lt;br /&gt;/*&lt;br /&gt;chamada ao construtor:&lt;br /&gt;um novo objeto é criado com um link escondido para o valor do prototype da função&lt;br /&gt;*/&lt;br /&gt;p = new Pessoa("Fulano");&lt;br /&gt;// veja que o método foi adicionado depois do objeto ser criado&lt;br /&gt;Pessoa.prototype.escreveNome = function() {&lt;br /&gt;  document.writeln('+++++'+this.nome+'+++++&lt;br/&gt;');&lt;br /&gt;}&lt;br /&gt;// procura em p, não tem, busca no Pessoa.prototype...&lt;br /&gt;p.escreveNome();&lt;br /&gt;&lt;br /&gt;p.escreveNome = function() {&lt;br /&gt;  document.writeln('-----'+this.nome+'-----&lt;br/&gt;');&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// procura em p, tem, usa o dele mesmo&lt;br /&gt;p.escreveNome();&lt;br /&gt;&lt;br /&gt;// remove o método do objeto p&lt;br /&gt;delete(p.escreveNome);&lt;br /&gt;&lt;br /&gt;// procura em p novamente, não tem pois foi apagado, usa novamente o do prototype&lt;br /&gt;p.escreveNome();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Chamada ao Método&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var o = {&lt;br /&gt;  linha:"",&lt;br /&gt;  aumentaLinha: function() {&lt;br /&gt;    this.linha += "---";&lt;br /&gt;  },&lt;br /&gt;  mostraLinha: function() {&lt;br /&gt;    document.writeln(this.linha + '0' + this.linha + '&lt;br/&gt;');&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// a vinculação tardia do "this" ao objeto ocorre apenas na chamada ao método.&lt;br /&gt;for(var a = 0; a&lt;30; a++) {&lt;br /&gt;  // duas maneiras de se chamar o mesmo método:&lt;br /&gt;  o.mostraLinha();&lt;br /&gt;  o['aumentaLinha']();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Chamada Com Apply&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// apply é um método presente em todas funções de js.&lt;br /&gt;// lembrando que as funções de js são objetos e podem ter métodos. &lt;br /&gt;mostraNome = function(extra) {&lt;br /&gt;  document.writeln("this.nome: " + this.nome + "&lt;br/&gt;");&lt;br /&gt;  document.writeln("param extra: " + extra + "&lt;br/&gt;");&lt;br /&gt;}&lt;br /&gt;mostraNome();&lt;br /&gt;document.writeln("valor de mostraNome.apply:" + mostraNome.apply + "&lt;br/&gt;");&lt;br /&gt;document.writeln("&lt;hr/&gt;");&lt;br /&gt;/*&lt;br /&gt;apply chamará o método mostraNome, vinculando o this ao objeto passado &lt;br /&gt;no primeiro parâmetro. E e ele vai passar para o mostra nome o array&lt;br /&gt;de parâmetros passado no segundo parâmetro. &lt;br /&gt;*/&lt;br /&gt;mostraNome.apply({nome:"João "},["pereira dos santos"])&lt;br /&gt;//&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-6518110354451607165?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/6518110354451607165/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/02/chamada-metodos-no-js.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/6518110354451607165'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/6518110354451607165'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/02/chamada-metodos-no-js.html' title='Chamada a métodos no Js.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-8936408070122409280</id><published>2009-02-13T02:42:00.000-08:00</published><updated>2009-03-05T07:53:41.626-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='callback'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='arvore'/><category scheme='http://www.blogger.com/atom/ns#' term='recursiva'/><title type='text'>Chamando o callback do callback no jquery.</title><content type='html'>Me deparei com um problema ao precisar chamar o callback de vários nós de uma arvore para abrir um de cada vez no jquery. Ou seja, tem que abrir a raiz e passar o callback para abrir o filho, só que no callback de abrir o filho (callback do callback) chamar o callback de abrir o neto e assim por diante até onde for necessário. &lt;br /&gt;&lt;br /&gt;Depois de fritar os miolos um poucos, resolvi da seguinte maneira:&lt;br /&gt;Na "classe" TaskTreeC coloquei um método que inicializa algumas variáveis no escopo dela mesma e também define um lock:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;  &lt;br /&gt;  this.openParents = function(id,callback) {&lt;br /&gt;    // para não dar conflito sendo chamada antes de terminar uma operação em andamento.&lt;br /&gt;    if(TaskTreeC.locked) return;&lt;br /&gt;    TaskTreeC.locked = true;&lt;br /&gt;    stack = [];&lt;br /&gt;    TaskTreeC.getParentStack(id,stack);&lt;br /&gt;    TaskTreeC.stack = stack;&lt;br /&gt;    TaskTreeC.recursiveIndex = 0;&lt;br /&gt;    TaskTreeC.openStackCallback = function() {&lt;br /&gt;      TaskTreeC.locked = false;&lt;br /&gt;      if(callback) callback(); &lt;br /&gt;    }&lt;br /&gt;    TaskTreeC.openStack();&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Primeiramente a função recebe um id de um item interno na arvore e chama getParentStack para poder pegar um array com os ids de todos os pais daquele item. Então armazena isso, cria um índice e um callback final que será chamado depois de todo o processo. Então ela chama o método openStack que faz o "serviço sujo".&lt;br /&gt;&lt;br /&gt;No meu caso eu não preciso mostrar os elementos percorridos, mas sim abrir os filhos de cada um deles. Assim o método openStack ficou da seguinte maneira:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;this.openStack = function() {&lt;br /&gt;    if(TaskTreeC.recursiveIndex &lt; TaskTreeC.stack.length) {&lt;br /&gt;      id = TaskTreeC.stack[TaskTreeC.recursiveIndex];&lt;br /&gt;      TaskTreeC.recursiveIndex++;&lt;br /&gt;      if(TaskTreeC.getChildren(id).length) {&lt;br /&gt;        // pega o div que tem os filhos...&lt;br /&gt;        node = $('#node-content-'+id);&lt;br /&gt;        if(node.is(':visible')) {&lt;br /&gt;          // se já estiver aberto, segue em frente sem fazer nada. &lt;br /&gt;          TaskTreeC.openStack();&lt;br /&gt;        }&lt;br /&gt;        else {&lt;br /&gt;          // mostra com um tempo para o browser renderizar&lt;br /&gt;          var t=setTimeout(&lt;br /&gt;            "TaskTreeC.removeHiddenCache(id);"+&lt;br /&gt;            "node.show('blind',{},500,TaskTreeC.openStack);"+&lt;br /&gt;            "TaskTreeC.closeChildren(id);", // fecha os "netos"&lt;br /&gt;            200&lt;br /&gt;          );&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;      else {&lt;br /&gt;        // chama o callback final&lt;br /&gt;        if(TaskTreeC.openStackCallback) {&lt;br /&gt;          var t=setTimeout("TaskTreeC.openStackCallback();",300);&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;    else {&lt;br /&gt;      // chama o callback final&lt;br /&gt;      if(TaskTreeC.openStackCallback) {&lt;br /&gt;        var t=setTimeout("TaskTreeC.openStackCallback();",300);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Outros métodos usados:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;  this.getParentStack = function(id,stack) {&lt;br /&gt;    node = $('#node-'+id);&lt;br /&gt;    parentId = UtilC.getAttrUpTheTree( node.parent() , 'taskId' );&lt;br /&gt;    if(parentId) {&lt;br /&gt;      TaskTreeC.getParentStack(parentId,stack);&lt;br /&gt;    }&lt;br /&gt;    stack[stack.length] = id;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;//UtilC&lt;br /&gt;  this.getAttrUpTheTree = function(item,attrName) {&lt;br /&gt;    //attrib = $(item).attr(attrName); &lt;br /&gt;    if(&lt;br /&gt;      $(item).attr(attrName) != undefined &amp;&amp; &lt;br /&gt;      ( typeof $(item).attr(attrName) === 'string' )&lt;br /&gt;    ) {&lt;br /&gt;      return $(item).attr(attrName); &lt;br /&gt;    }&lt;br /&gt;    else {&lt;br /&gt;      parentNode = $(item).parent()[0];&lt;br /&gt;      if(parentNode != undefined) {&lt;br /&gt;        return RouterC.getAttrUpTheTree($(item).parent()[0],attrName);&lt;br /&gt;      }&lt;br /&gt;      else return null;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;OBS: acho que este código pode ser melhorado utilizando um module pattern, dê uma olhada no outro post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-8936408070122409280?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/8936408070122409280/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/02/chamando-o-callback-do-callback-no.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/8936408070122409280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/8936408070122409280'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/02/chamando-o-callback-do-callback-no.html' title='Chamando o callback do callback no jquery.'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-5378849176712839913</id><published>2009-02-11T05:47:00.001-08:00</published><updated>2009-02-14T16:08:50.032-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='desenvolvimento'/><category scheme='http://www.blogger.com/atom/ns#' term='selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='testes'/><category scheme='http://www.blogger.com/atom/ns#' term='phpunit'/><title type='text'>Dicas para escrita e uso de testes</title><content type='html'>&lt;p&gt;&lt;b&gt;Mantenha TODOS os testes passando e atualizados. &lt;/b&gt; &lt;/p&gt;&lt;p&gt;Toda vez antes de dar commit, faça um update e rode toda a suite de testes na sua máquina. Caso algum teste quebre, concerte o que precisa ser concertado antes de mandar para o repositório. Lembre-se que o código pertence a todos. Você pode consultar quem escreveu o código/teste, mas se tem um teste quebrado na sua máquina, é seu dever fazê-lo passar. &lt;/p&gt;&lt;p&gt;Deixar testes desatualizados no sistema pode gerar um efeito de bola de neve e mais na frente atrapalhar muito o desenvolvimento. Além disso manter testes quebrados no sistema certamente vai gerar mais erros e diminuir a confiança da equipe no processo de testes. &lt;/p&gt;&lt;p&gt;&lt;b&gt;Escreva testes atômicos do ponto de vista do sistema.&lt;/b&gt;  &lt;/p&gt;&lt;p&gt;Cada teste deve ser contido em si. Se o teste depende de algum estado do sistema, busque criar o cenário antes de rodar o teste. (ex: o teste do captcha para o login depende de ter a contagem limpa. Assim limpamos a contagem e rodamos o teste.) Também é uma boa prática limpar a casa depois de usar. Assim quando terminarmos de rodar o teste é importante excluir registros e informações que possam interferir em outros testes. &lt;/p&gt;&lt;p&gt;A classe de teste também deve ser o mais auto-suficiente possível. Não crie interdependências entre testes nem use métodos de outra classe te teste. Isso pode dar sérios problemas e dificuldades de manutenção. &lt;/p&gt;&lt;p&gt;&lt;b&gt;Escreva testes atômicos do ponto de vista das funcionalidades.&lt;/b&gt; &lt;/p&gt;&lt;p&gt;A não ser se você estiver fazendo um teste de integração, não escreva testes que testam tudo de uma vez. Teste cada funcionalidade de uma vez. Isso deixa os testes fáceis de manter e os relatórios muito melhores. Lembre que a lista dos testes faz as vezes de uma especificação do negócio em muitos casos. &lt;/p&gt;&lt;p&gt;Por exemplo. Ao invés de escrever: "A listagem deve ser mostrada corretamente." Escreva algo como: "deve mostrar a soma dos valores no campo Valor Total", "deve paginar caso haja mais de 15 registros", etc... &lt;/p&gt;&lt;p&gt;&lt;b&gt;Escreva testes rápidos&lt;/b&gt; &lt;/p&gt;&lt;p&gt;Busque manter os testes o mais rápido que for possível. Ao caminhar com o desenvolvimento teremos cada vez mais teste e não queremos que o build fique muito lento. Por isso precisamos economizar ao máximo em cada um dos testes. &lt;/p&gt;&lt;p&gt;No selenium evite expressões como "waitForText" ou "waitFor..." qualquer coisa que você não tenha certeza que não vai aparecer. Busque esperar por elementos certos na página e então dê um assert para ver se ele existe. Um bom elemento para usarmos em requisições de Ajax é o indicador de progresso. &lt;/p&gt;&lt;p&gt;&lt;b&gt;Escreva testes que independam ao máximo da interface&lt;/b&gt; &lt;/p&gt;&lt;p&gt;Ao escrever um teste, busque testar elementos que você sabe que não vão ser alterados durante o processo de desenvolvimento. Por exemplo, ao invés de testar por uma mensagem, busque testar por elementos da página que provavelmente não vão mudar com o tempo. Ou então vc pode buscar pela constante que indica o valor da mensagem e não pela string.  &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-5378849176712839913?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/5378849176712839913/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/02/dicas-para-escrita-e-uso-de-testes.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/5378849176712839913'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/5378849176712839913'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/02/dicas-para-escrita-e-uso-de-testes.html' title='Dicas para escrita e uso de testes'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-672197780778365674.post-7222670136333371500</id><published>2009-02-11T03:13:00.000-08:00</published><updated>2009-02-13T03:36:13.956-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='symfony'/><category scheme='http://www.blogger.com/atom/ns#' term='phing'/><category scheme='http://www.blogger.com/atom/ns#' term='selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='doctrine'/><category scheme='http://www.blogger.com/atom/ns#' term='phpunit'/><title type='text'>Criando testes no PHPUnit/Selenium que rodem com o symfony/doctrine/phing/phpunit</title><content type='html'>&lt;span style="font-size:130%;"&gt;Ingredientes&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;symfony+doctrine:&lt;/span&gt; framework de desenvolvimento e ORM&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;phpunit:&lt;/span&gt; para testes unitários&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;phing:&lt;/span&gt; para fazer o build, rodar os testes, gerar a documentação, lavar a roupa, etc...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Problemas a se Vencer&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;O phpunit, quando roda pela linha de comando, não carrega as configurações do projeto do symfony. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Alguns testes do selenium precisam de acesso à api do symfony para configurar o ambiente e ter acesso aonde o browser não tem. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Descrição da Solução&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;Para isso precisamos carregar as configurações do symfony/docrine. Claro que é bom fazer isto uma vez só para não pesar nos testes. Porém precisamos fazer isto de uma maneira que não prenda muito os testes a um diretório específico e que não seja repetitiva.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;O phpunit, quando rodado pelo phing, não considera as suites de teste, rodando todos os testes separadamente.&lt;/span&gt; Por isso, o setUp() da suite, que poderia parecer um bom local, na verdade não é pois ele será desconsiderado pelo phing. Escolhemos então o setUp de cada classe teste. Para não repetir o código, criamos uma classe da qual extendemos todos os nossos testes. Assim nossas classes já herdam o setUp. &lt;span style="font-weight: bold;"&gt;Como o autoload do symfony ainda não foi carregado, precisamos informar para o phpunit o caminho da nossa classe&lt;/span&gt; caso ela não esteja no include.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Solução Proposta:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;### classe pai&lt;br /&gt;&lt;br /&gt;class Sf_PHPUnit_Extensions_SeleniumTestCase extends PHPUnit_Extensions_SeleniumTestCase {&lt;br /&gt;  function setUp()&lt;br /&gt;  {&lt;br /&gt;    if ( SF_APP_NAME != '' )&lt;br /&gt;    {&lt;br /&gt;      define( 'SF_APP_NAME', 'frontend' );&lt;br /&gt;      define( 'SF_ENV', 'test' );&lt;br /&gt;      define( 'SF_CONN', 'doctrine' );&lt;br /&gt;      // ajuste o caminho abaixo de acordo com o local onde vc por esta classe&lt;br /&gt;      require_once(dirname(__FILE__).'/../../../../config/ProjectConfiguration.class.php');&lt;br /&gt;      // busca as configurações do projeto&lt;br /&gt;      $configuration = ProjectConfiguration::getApplicationConfiguration( SF_APP_NAME , SF_ENV, true);&lt;br /&gt;      sfContext::createInstance($configuration);&lt;br /&gt;      sfContext::getInstance()-&gt;getUser()-&gt;initSession('phpunit');&lt;br /&gt;      // em alguns contextos as linhas abaixo podem ser úteis, mas não lembro quais agora :)&lt;br /&gt;      //$databaseManager = new sfDatabaseManager($configuration);&lt;br /&gt;      //$databaseManager-&gt;loadConfiguration();&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    $this-&gt;setBrowser("firefox");&lt;br /&gt;    // "solução de contorno" para definir se o teste está no ambiente de desenv ou homolog.&lt;br /&gt;    if(isset($_SERVER[HOMEDRIVE]) &amp;amp;&amp;amp; $_SERVER[HOMEDRIVE] == 'C:') {&lt;br /&gt;      $this-&gt;setBrowserUrl("http://aliaslocal/");  &lt;br /&gt;    }&lt;br /&gt;    else {&lt;br /&gt;      // ip ou alias do servidor...&lt;br /&gt;      $this-&gt;setBrowserUrl("http://10.1.1.234/");&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;### classe filha&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;require_once 'PHPUnit/Extensions/SeleniumTestCase.php';&lt;br /&gt;// lembrar que este require é necessário pois o autoload do symfony ainda não foi carregado neste ponto.&lt;br /&gt;require_once dirname(__FILE__).'/../../../plugins/sfUtil/lib/test/SF_PHPUnit_Extensions_SeleniumTestCase.php';&lt;br /&gt;&lt;br /&gt;class LoginSeleniumTest extends SF_PHPUnit_Extensions_SeleniumTestCase&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;  function setUp() {&lt;br /&gt;    // lembrar de chamar o setUp do pai caso precise extender aqui...&lt;br /&gt;    parent::setUp();&lt;br /&gt;    $this-&gt;doSomeMoreSetup();&lt;br /&gt;  }&lt;br /&gt;  // ...&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Conclusão:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;Claro que devem existir inúmeras soluções para este problema. Caso você tenha algo a acrescentar ou alguma crítica construtiva, certamente isto vai melhorar ainda mais a nossa proposta.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/672197780778365674-7222670136333371500?l=peagapando.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://peagapando.blogspot.com/feeds/7222670136333371500/comments/default' title='Postar comentários'/><link rel='replies' type='text/html' href='http://peagapando.blogspot.com/2009/02/criando-testes-no-phpunitselenium-que.html#comment-form' title='0 Comentários'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/7222670136333371500'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/672197780778365674/posts/default/7222670136333371500'/><link rel='alternate' type='text/html' href='http://peagapando.blogspot.com/2009/02/criando-testes-no-phpunitselenium-que.html' title='Criando testes no PHPUnit/Selenium que rodem com o symfony/doctrine/phing/phpunit'/><author><name>bruno.p.reis</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
