Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Bruno Augusto

Renderização de uma View...

Recommended Posts

Pessoal, estou com um probleminha que não faz muit sentido para mim.

 

Na minha View, no método que renderiza o Templte, tenho isso:

 

public function render( $name = NULL ) {

       if( ! $this -> _shouldRender ) {
           return;
       }

       $this -> _isRendered = TRUE;

       ob_start();

       include $this -> findFile( $name );

       $data = ob_get_contents();

       ob_end_clean();

       echo $data;
   }

Simples assim. View::findFile(), como o nome sugere, apenas encontra o caminho completo do Template passado, nada mais. e, por isso, não vem ao caso.

 

Pois bem, para evitar ter que fazer, em cada action, de cada controller:

 

$this -> view -> render()

Isto é, renderizar manualmente, decidi colocar, no destrutor da classe abstrata de controllers isso:

 

    public function __destruct() {

       if( $this -> view instanceof IView && ! $this -> view -> isRendered() ) {

           $this -> view -> render();
       }
   }

Assim, quando o controller fosse destruído, o Template seria renderizado automaticamente.

 

Nota: Tive que colocar o instanceof para ter certeza de que o objeto presente na propriedade $view é de fato uma View válida.

 

Tudo muito lindo e funcionando as mil maravilhas até que implementei as Exceptions. Todas as minhas Exceções extendem de uma única (ver abaixo) que por sua vez extende da Exception nativa.

 

class Exception extends \Exception {

   /**
    * Placeholders Replacements
    * 
    * @var	array	$replacements
    */
   private $replacements = array();

   /**
    * Exception Constructor
    * 
    * @param   string|optional         $message
    * @param   string|integer|optional $code
    * @param	array|optional			$args
    */
   public function __construct() {

       $args = func_get_args();

       // Shifting the Message

       $message = array_shift( $args );

       // Constructing Parent

       parent::__construct( $message, 0 );

       // Setting Up Placeholders Replacements

       $this -> replacements = (array) $args;
   }

   /**
    * Translates Exception's Message Placeholders
    * 
    * @return  string
    */
   public function __toString() {
       return vsprintf( $this -> getMessage(), $this -> replacements );
   }

Eu acreditava estar tudo perfeito. Mas a renderização automática do Template, feita no destrutor dos Controllers, está, quando uma Exceção é disparada, entrando no que parece ser um loop infinito.

 

Revisei todo o código, desde o Bootstrap até a View, passando pelo Roteador, Controller e etc. e está tudo em ordem.

 

Tanto é que se comento o destrutor e volto a renderizar manualmente, o problema pára, isto é, se houver uma exceção a mesma é direcionada para handler customizado (ver abaixo) e a execução é interrompida, caso contrário continua e renderiza (manualmente).

 

A saber, o handler de Exceção customizado é:

 

class ExceptionHandler {

   /**
    * Handles the Exception
    *
    * @param   Exception   $e  Exception thrown
    *
    * @return  void
    */
   public function handle( \Exception $e ) {
       include dirname( __FILE__ ) . '/Exception/Handler/Layout.phtml';
   }
}

Nada mais que isso, apenas para mostrar de forma elegante as exceções não capturadas.

 

Alguém sabe o que pode ser?

Compartilhar este post


Link para o post
Compartilhar em outros sites

acho que o problema é aqui

class Exception extends Exception {

 

classe Exception se estende à ela mesma

 

tente: class Minha_Exception extends Exception {

 

aconteceu comigo quando usei 'class Exception' criada por mim, dizia que não podia redeclarar a classe Exception

Compartilhar este post


Link para o post
Compartilhar em outros sites

acho que o problema é aqui

class Exception extends Exception {

 

classe Exception se estende à ela mesma

 

tente: class Minha_Exception extends Exception {

 

aconteceu comigo quando usei 'class Exception' criada por mim, dizia que não podia redeclarar a classe Exception

Opa, falha minha. Eu deveria ter postado o source inteiro.

 

Aontece o seguinte. Eu uso namespaces e, sendo assim minha classe Exception, na verdade não se chama Exception. Ela tem todo o o nome do namespace antes dela. :thumbsup:

 

E mesmo assim, programando com os alertas de erro no máximo como estou fazendo, ao invés de entrar nesse problema em particular, eu veria um Fatal error

 

Essa estrutura MVC que você usa: onde posso encontrar material sobre ela?

Por enquanto em lugar nenhum. Quem sabe quando estiver totalmente pronto eu não libero...

Compartilhar este post


Link para o post
Compartilhar em outros sites

nao aprendi ainda namespaces :)

acho que nao posso ajudar muito

 

qual classe esta entrando em loop?

chega a ter saida ou fica apenas processando, processando, processando...?

 

 

as vezes caia em loop com uma classe estendida usando o construct

Compartilhar este post


Link para o post
Compartilhar em outros sites

As classes são os Controllers, especificamente uma das actions.

 

Nela tem apenas algumas linhas escritas: Instanciamento da Model, listagem dos dados de um registro com base no ID passado, uma exception (caso a listagem não retorne nada devido ao ID ser inexistente) e a invocação do render().

 

Manualmente funciona. Automaticamente, via destrutor, aparenta estar em loop, porque, como você disse, fica processando, processando, processando até eu me encher, abortar manualmente e ver o Apache travar.

 

Sabe o que é mais curioso? Os únicos 3 loops que o sistema tem só são ativados quando o meu Development Mode está no máximo (nível 2), que é quando as rotas de URL são geradas. E esse modo só é usado quando crio um novo Controller, já que tenho que incluir as novas rotas no sistema.

 

Fora isso, nadica de nada.

 

É muito estranho. Já tentei depurar na expectativa de o __destruct() estar sendo invocado infinitas vezes, mas não. Tá tudo OK.

Compartilhar este post


Link para o post
Compartilhar em outros sites

parece mesmo classe chamando classe

 

 

na pior da hipoteses, crie um log em cada metodo, quando entrar em loop você vai saber qual esta sendo invocado e a classe que fez cair e sequencia

 

desculpe nao ter uma solução mais 'elegante'... estou de saida agora

[]s

Compartilhar este post


Link para o post
Compartilhar em outros sites

tente colocar flush(); após ob_end_clean();

 

ob_end_clean();
flush();

 

 

se não rolar, faça outra coisa,

 

 

comente o print

//echo $data;

 

e mantenha o flush() depois do ob_end_clean()

Compartilhar este post


Link para o post
Compartilhar em outros sites

Hmmmmm.... Aconteceu algo ainda mais bizarro agora.

 

Ainda não experimentei sua possível solução hinom. Como meu PC de trabalho pifou, tive de preparar um rapidinho em outro para testar. Como estou meio sem tempo, peguei o XAMPP mesmo.

 

Agora, descomentei o destrutor para ativar a auto-renderização e comentei a a chamada ao View::render() manual.

 

Rodei sob um id válido e funcionou perfeitamente. Rodei sob um id inválido (disparando a exceção que dava problema antes) e para minha surpresa rodou perfeitamente também.

 

Pensando comigo, a única coisa que existe de diferente entre os servidores, o que uso constantemente e esse improvisado, além da forma da instalação (um é na unha e o outro o XAMPP) são as configurações da XDebug.

 

No momento não tenho como postar as que uso normalmente, já que o PC antigo sequer liga. Mas se não me falha a memória, aquela configuração que determina o quão profundo a depuração vai está acima do normal.

 

Poderia ser isso?

Compartilhar este post


Link para o post
Compartilhar em outros sites

public function render( $name = NULL ) {
   	ob_start();

       include $this -> findFile( $name );

       $data = ob_get_contents();

       ob_end_clean();

       echo $data;
   }

 

tente colocar flush(); após ob_end_clean();

 

Eu não consigo ver nenhum sentido em criar um buffer de saída, pegar o conteúdo do buffer, limpar o buffer e depois dar um mandar o conteúdo que você pegou para a saída sem fazer absolutamente nada com ele.

 

A não ser que haja algum tratamento desse conteúdo, que não tenha sido postado aqui, esse código pode ser resumido em:

 

include $this -> findFile( $name );

 

Afinal, como você não faz nada com o conteúdo do buffer que não seja mandá-lo para a saída, não tem motivo algum para você ter um buffer; A não ser é claro que você queira uma execução mais lenta.

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites
A não ser que haja algum tratamento desse conteúdo...

:grin:

 

Mas há sim. Eu só omiti para simplificar.

 

O tratamento é remover as novas linhas duplicadas do HTML que invariavelmente aparecem devido ao Template ficar "recortado" entre tantas aberturas e fechamentos de comandos PHP ao longo dele.

 

Assim o HTML fica "limpo" tal qual se nenhum PHP fosse passado para ele.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas há sim. Eu só omiti para simplificar.

 

Certo, é que muitas vezes, omissão leva a confusão. :P

 

De qualquer forma, aposto que seu "loop infinito" é, na verdade, uma recursão.

 

Você citou um ExceptionHandler: Como ele manipula as exceções em relação a exibição ?

 

Imagina o seguinte caso:

 

1. Exceção disparada na renderização de uma View.

2. ExceptionHandler assume, trata e renderiza uma View para mostrar uma mensagem amigável.

3. Exceção disparada na renderização da View de mensagem amigável.

4. ExceptionHandler assume, trata e renderiza uma View para mostrar uma mensagem amigável.

5. Exceção disparada na renderização da View de mensagem amigável.

6. ...

 

Acho que deu para entender.

 

;)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pois é, pensando nesse problema é que fiz a renderização do Template amigável não usar a View e sim apenas dar um include num arquivo que eu já pré-determinei.

 

Você acha que eu deveria instanciar um objeto da View só para dar um include básico?

 

E mais, como explicar o fato de, nesse PC com servidor improvisado não estar mais dando problema?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Você acha que eu deveria instanciar um objeto da View só para dar um include básico?

 

Não é possível dizer isso, não sei como funciona seu sistema.

 

E mais, como explicar o fato de, nesse PC com servidor improvisado não estar mais dando problema?

 

Existe uma grande diferença entre "não estar mais dando problema", que indica que o problema foi corrigido, de "não estar mais aparecendo o problema" que indica que ele pode voltar a aparecer a qualquer momento.

 

Você está utilizando as funções set_error_handler() e set_exception_handler() ?

 

Faça a seguinte modificação no seu código:

 

class ExceptionHandler {
/**
 * Handles the Exception
 *
 * @param   Exception   $e  Exception thrown
 *
 * @return  void
 */
public function handle(\Exception $e ) {
	$fileName = dirname( __FILE__ ) . '/Exception/Handler/Layout.phtml';

	if ( is_file( $fileName ) && is_readable( $fileName ) ) {
		include_once $fileName;
	} else {
		echo 'opz, "' . $fileName . '" não foi encontrado.';
	}
}
}

 

Se houver um ErrorHandler, faça o mesmo com ele.

 

Existe alguma declaração de função ou classe dentro desses phtml ou o conteúdo é apenas marcação ?

 

Caso seja apenas marcação, pode manter o include, caso contrário, utilize include_once.

 

Veja também as configurações de error_reporting e display_errors no php.ini do seu amp e, se possível, compare as diretivas do php.ini do amp "improvisado" com as definições do php.ini da sua máquina "oficial".

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu acho que está havendo algum tipo de confusão na comunicação.

 

Vamos lá

 

Sim, de fato estou usando set_exception_handler() para mostrar o Template presente no include da classe. É um Template "padrão" que, se um dia vier a ser distribuído, irá junto com o sistema inteiro.

 

É certeza que ele existe, não preciso verificar, afinal, se ele não existisse ou o path até estivesse errado, uma Exception não capturada não seria mostrada por esse Template e um erro disparado.

 

Ele não é o problema.

 

O código não foi mexido, pelo menos não nessa parte. Apenas foi testado nesse servidor novo, sem nenhuma configuração manual diferente. Tudo "de fábrica" configurado pelo pessoal lá do ApacheFriends, desenvolvedores do XAMPP.

 

Quanto as diretivas de erro, estou trabalhando com elas no máximo, isto é, error_reporting() recebendo E_ALL | E_STRICT.

 

Como eu disse não existem loops e a única coisa que poderia indicar uma recursão seria a configuração exagerada da XDebug, mas não sei como detectar onde ou como a XDebug estaria indo tão profundamente se o fluxo lógico não possui tantos níveis assim.

Compartilhar este post


Link para o post
Compartilhar em outros sites

É certeza que ele existe, não preciso verificar, afinal, se ele não existisse ou o path até estivesse errado, uma Exception não capturada não seria mostrada por esse Template e um erro disparado.

 

Bruno,

 

É justamente isso que estou dizendo.

 

Confiança, em programação, só existe através de interfaces e não verificar a existência é um erro.

 

Veja, você tem um exception handler que vai capturar suas exceções.

 

Se uma exceção for disparada durante o tratamento de uma exceção, você terá uma recursão igual a que eu descrevi no post #12

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mas não há essa possibilidade, João.

 

A única coisa que o handler faz é incluir o Template que exibe informações a partir dos métodos da classe Exception nativa e uma iteração (que havia esquecido de mencionar) sobre o Stack Trace.

 

Em nenhum momento eu ou qualquer coisa que mexe com esse Template dispara uma exceção, já que eu não uso nenhum throw e dos métodos da classe Exception apenas __toString() dispara algo quando uma string não é retornada e talvez __clone, embora esse eu não tenha certeza.

 

E temos certeza que vsprintf retorna uma string, né? ^_^

 

Posso até incluir essa verificação, muito embora seja desnecessária ao meu ponto de vista.

 

A única exceção que não passa pelo exception handler, ou seja, é capturada, é a ControllerException.

 

Concordo com você que a única garantia que temos é através das Interfaces. Desde que aprendi o que elas são, uso-as muito. Mas não vejo o que elas teriam a ver com esse caso.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Em nenhum momento eu ou qualquer coisa que mexe com esse Template dispara uma exceção, já que eu não uso nenhum throw e dos métodos da classe Exception apenas __toString() dispara algo quando uma string não é retornada e talvez __clone, embora esse eu não tenha certeza.

 

Então tenha essa certeza, __toString não pode disparar uma exceção:

 

<?php
class Test {
public function __toString() {
	throw new Exception( 'Isso é um erro grave e não deve ocorrer.' );
}
}

$t = new Test();

echo $t;

 

Saída:

 

PHP Fatal error:  Method Test::__toString() must not throw an exception in /var/www/html/test/Test.php on line 10
PHP Stack trace:
PHP   1. {main}() /var/www/html/test/Test.php:0

Fatal error: Method Test::__toString() must not throw an exception in /var/www/html/test/Test.php on line 10

Call Stack:
0.0002 	636352   1. {main}() /var/www/html/test/Test.php:0

Compartilhar este post


Link para o post
Compartilhar em outros sites

sobre o flush, era para debug e não uma solução...

apenas com finalidade de provocar uma possível mensagem de erro que mostrasse uma pista do suposto problema..

Compartilhar este post


Link para o post
Compartilhar em outros sites

Exato, justamente por que eu não disparo Exceção, vsprintf() não dispara, ningém mais dispara.

 

Você provavelmente sabe de algo que eu não sei, por isso não consigo captar o que está dizendo ou querendo dizer.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.