Jump to content

Archived

This topic is now archived and is closed to further replies.

Henrique Barcelos

HTTP Response e Request

Recommended Posts

Saudações, galera.

 

Alguém aí tem algum(ns) link(s) sobre HTTP Response e Request?

Estou iniciando o desenvolvimento da camada Controller, dando uma olhada em alguns frameworks e vi que o trabalho do controller se resume basicamente a captar a requisição e retornar a resposta. Eu sei que muito é feito automaticamente pelo navegador, mas como estou pretendendo criar um "framework" HMVC, é necessário criar "requisições" dentro da requisição principal.

 

Eu gostei bastante do funcionamento do Kohana, mas a documentação é fraca e a estrutura é confusa. Essa parte no Zend é imensa e complexa demais para os meus objetivos. Estou querendo desenvolver algo mais simples, que me permita apenas essa simulação de requisições quando necessário.

 

Grato,

Henrique

Share this post


Link to post
Share on other sites

Olha, até programa pirata é mais fácil de encontrar do que informações em quantidade decentes sobre Request e Response.

 

Quando eu comecei a minha, encontrei pouquíssima coisa, o JCMais até me ajudou mas ainda assim a maioria dos links envolvidos vinham ou da Wikipedia em inglês (tem bem mais coisa) ou de uma ou outra das milhares de RFC's existentes.

 

Desenvolvi? Sim. Estou completamente satisfeito? Ainda não.

 

Mas vou te ajudar com o que eu tenho.

 

Apesar do aparente tom afirmativo das frases, encare apenas como movimentação de idéias. São informações esparsas e não fazem tanto sentido num contexto formal, e sim apenas informativo.

 

Tanto as requisições que o seu sistema fizer "automaticamente" como as que você fizer manualmente você centraliza numa única classe, a Request.

 

Além de manipular a requisição, a Request deve trabalhar com os componentes da mesma, como Headers e Cookies. Porém estes são fazem parte da Request.

 

A Request fornece informações sobre a requisição feita (GET, POST, SERVER...) e sobre como ela foi feita( método, user-agent, IP...) e prepara informações para envio, quando externo (POST, por exemplo).

 

Toda requisição, realizada com sucesso ou com erro, deve retornar uma Resposta e aí entra a classe Response

 

Assim como Request, a Response também manipula Headers e Cookies, porém diferentes dos manipulados pela primeira.

 

Apesar de existirem implementações Singleton tanto de Request quanto de Response, elas não deve sê-lo, afinal não existe uma única Requisição ou Resposta.

 

De novo, por enquanto comigo, não existe/deve existir nenhum echo/print fora da classe Response pois é ela quem enviará Headers para o navegador do usuário e centralizando esse tipo de tarefa, torna-se impossível ter aqueles erros infames.

 

Acho que por enquanto é só. Não me lembro de mais nada no momento.

 

Dois tópicos (meus mesmo) com uma boa discussão e que pode te ajudar a ter mais insights:

 

Response (e a View)

 

HTTP (quem é Request e quem é Response)

Share this post


Link to post
Share on other sites

Eu lembro desses tópicos, mas está meio "avançado" pra mim, hehe...

Eu gostaria de entender de que se compõem uma Requisição e uma Resposta.

 

Pelo que eu entendi, ambos são parecidos, possuem headers, cookies, body... Tem algo a mais?

 

Na requisição, os cookies enviados são os já setados no navegador, que eu recupero através de $_COOKIE, na resposta, são cookies para setar, que provém de setcookie. Está correto?

Share this post


Link to post
Share on other sites

Olha, até programa pirata é mais fácil de encontrar do que informações em quantidade decentes sobre Request e Response.

Desculpe pelo flood, mas não consegui segurar .. enfim, "verdade !!!!", muitos dizem que hoje em dia, não se aprende programação quem não quer, na verdade, eu sou até obrigado a concordar em partes, mas conteúdo de qualidade é difícil, e nem sempre estes são perfeitos.

Share this post


Link to post
Share on other sites

Pois é, pessoal... tá osso...

Eu gostei bastante das Requests do Kohana, só que a estrutura dela é meio complexa, tem trocentas requests diferentes e além disso tem um tal Client que eu não entendi muito bem o que é, que também tem uma hierarquia de classes nada pequena.

 

Então decidi me guiar pela Request do CakePHP, que é mais simples, não lida com a criação de novas requisições, lida apenas com os dados da requisição atual, mas ela não faz muito sentido.

 

A do Zend Framework é totalmente diferente também... desse jeito complica =/

Share this post


Link to post
Share on other sites

Pelo que eu entendi, ambos são parecidos, possuem headers, cookies, body... Tem algo a mais?

Talvez pela escassez de informação posso ter feito uma interpretação errônea mas a minha Request não ter body.

 

No meu caso, requisições internas, isto é, análise e roteamento de URL e seleção do Controller adequado, não são explícitamente enviadas.

 

Já as externas são, mas o retorno é um objeto Response pois a requisição foi feita mas o que eu obtive foi uma resposta do servidor alvo.

 

Na requisição, os cookies enviados são os já setados no navegador, que eu recupero através de $_COOKIE, na resposta, são cookies para setar, que provém de setcookie. Está correto?

Com o que eu tenho em mãos, sim :thumbsup:

Share this post


Link to post
Share on other sites

Mas se o objeto Request não tem corpo, você não pode fazer uma requisição POST, por exemplo, nem PUT...

Mas eu acho que vou limitar o meu por enquanto a fazer apenas requisições internas mesmo... :S

Pra completar a boa fase, ainda fiquei sem internet essa semana em casa, não deu pra pesquisar mais nada...

 

Outra dúvida que surgiu foi sobre os dados GET e POST, eles devem ser tratados no objeto Request?

A minha ideia inicial era colocar esses dados somente na Request e dar unset nos índices dos arrays globais para acabar de vez com as variáveis globais, mas não sei se daria muito certo...

 

Por enquanto não tenho nem código para postar, não estou conseguindo captar a ideia dessa coisa toda...

Share this post


Link to post
Share on other sites

Mas se o objeto Request não tem corpo, você não pode fazer uma requisição POST, por exemplo, nem PUT...

Na bem da verdade não faço idéia d que seja uma Requisição PUT. Não sei pra que serve... <_<

 

Mas claro que pode. A Request vai fazer a Requisição e retornar a resposta (Response). Quem decidirá o que fazer com tal resposta é o Controller.

 

Um trechinho do meu, mais especificamente o retorno do método:

 

class Request extends Object {

   public function send() {

       // ...

       $reader = new Reader( $this -> adapter );

       return new Response( $reader -> readAll() );
   }
}

Depois de enviada a requisição, instancio um objeto Reader para ler informações a partir do adaptador passado.

 

Esse método readAll() é aquele velho while combinado com fread().

 

Passo toda essa string como argumento de Response e ela passa a fazer parte do corpo da resposta.

 

Lá no Controller eu faço algo como:

 

$request = new Request( /** ... */ );
print $request -> send() -> getBody();

send() ainda é um método de Request, mas como ele retorna um objeto Response, a cadeia pode continuar com getBody(), que apenas retorna o que eu tenho no corpo da Resposta, que é o que o servidor me devolveu.

 

Mas eu acho que vou limitar o meu por enquanto a fazer apenas requisições internas mesmo... :S

Eu fiquei um bom tempo sem sequer ter uma classe Request. Como era rwquisição interna pura e simples, o roteador dava conta.

 

Daí quando comecei a mexer com os cabeçalhos HTTP "tropecei" na informação de que a View não deveria ecoar o template, que isso era obrigação da chamada Response.

 

E como Response e Request estão intimamente ligadas, uma coisa levou a outra e separei em classes diferentes. Mas ainda assim,só para requisições internas. Requisições externas é algo rlativamente novo nessa classe, coisa de duas semanas (programando pouco).

 

Outra dúvida que surgiu foi sobre os dados GET e POST, eles devem ser tratados no objeto Request?

Eu penso assim: Tratados, não. Manipulados, sim.

 

Veja, o tratamento dos dados GET/POST é um conjunto de diversas filtragens. Não sei você, mas eu criaria (porque ainda não o fiz) um conjunto de classes Filter para filtrar os dados em cadeia.

 

Pelo que andei lendo parece que tem até mais um Padrão de Projeto que vou ter de aprender, mas isso é mais pra frente.

 

No meu caso, eu inicializo minhas propriedades $queryData e $postData, com respectivamente $_GET e $_POST.

 

Meus métodos setQueryData() e setPostData(), então, vão adicionando informações à essas propriedades.

 

Se, por um acaso, venha-se a se esquecer de usar os métodos getQueryData() ou getPostdata() para se acessar os valores, valendo-se dos bons e velhos superglobais, tudo bem, vai funcionar, mas não é bem essa intenção.

A minha ideia inicial era colocar esses dados somente na Request e dar unset nos índices dos arrays globais para acabar de vez com as variáveis globais, mas não sei se daria muito certo...

 

Por enquanto não tenho nem código para postar, não estou conseguindo captar a ideia dessa coisa toda...

Share this post


Link to post
Share on other sites

Sobre o método PUT.

The PUT method requests that the enclosed entity be stored under the supplied Request-URI

 

A aplicação mais conhecida desse método é para upload de arquivos. O envio de arquivos via POST é meio "gambiarra", o mais semântico e mais correto seria PUT.

 

class Request extends Object {

   public function send() {

       // ...

       $reader = new Reader( $this -> adapter );

       return new Response( $reader -> readAll() );
   }
}

 

Agora eu boiei um pouco, o que são esse Reader e esse Adapter?

 

Daí quando comecei a mexer com os cabeçalhos HTTP "tropecei" na informação de que a View não deveria ecoar o template, que isso era obrigação da chamada Response.

 

A minha ideia inicial era utilizar o ouput buffer, usar o ob_get_clean para setar o corpo da resposta. Existe outra maneira?

 

 

Share this post


Link to post
Share on other sites
Sobre o método PUT.
The PUT method requests that the enclosed entity be stored under the supplied Request-URI

 

A aplicação mais conhecida desse método é para upload de arquivos. O envio de arquivos via POST é meio "gambiarra", o mais semântico e mais correto seria PUT.

Hmmm... Anotado. Quem sabe um dia eu tento reinventar a roda e não fazer upload com POST ($_FILES) e faço com esse tipo de requisição.

 

class Request extends Object {

   public function send() {

       // ...

       $reader = new Reader( $this -> adapter );

       return new Response( $reader -> readAll() );
   }
}

 

Agora eu boiei um pouco, o que são esse Reader e esse Adapter?

Tá certo que eu não mencionei o que era o Adapter, mas daria pra pescar a idéia do que é o Reader pelo que eu disse sobre um de seus métodos, readAll().

 

Um resuminho...

 

A classe Reader é uma classe que lê dados a partir de uma Stream aberta. Atualmente tem três formas de leitura: "normal", X bytes de cada vez, leitura de linhas com fgets() e leitura "completa", unindo um laço while() com fread().

 

Ela "nasceu" assim que tive a necessidade de manipular arquivos, internamente. Quando a fiz foi para integrar à um leitor de arquivos MO (Machine Object), de linguagem.

 

Se eu fosse misturar abertura, leitura e escrita de Stream numa única classe, ia ser um caos. Daí separei: Stream (abre, fecha, procura...), Reader (lê) e Writer (escreve).

 

Fim da história :P

 

Já o adaptador, presente na propriedade $adapter, é coisa "nova" na classe. Ele é um adaptador pois posso precisar me conectar à um servidor externo usando Sockets (padrão), mas pode ser que um dia, mesmo com um Stream Context bem definido eu não consiga uma requisição de sucesso.

 

E pela estrutura que eu tenho hoje eu poderia criar um adaptador cURL para isso. :thumbsup:

 

Ainda está meio... "capenga", pois quero terminar a Request (ou pelo menos incrementá-la mais) antes de voltar nesse item, mas a base já existe.

 

Como as requisições internas são feitas mais pelo Roteador e o Front Controller do que pela Request em si, uma requisição não é enviada propriamente dita, isto é, ométodo send() não é chamado.

 

Já uma requisição externa, esta sim é enviada. E por passar o adaptador para a classe Reader eu abro a requisição (invocando o método open() do adaptador) e leio o que o servidor responder, instanciando a Response.

 

Daí quando comecei a mexer com os cabeçalhos HTTP "tropecei" na informação de que a View não deveria ecoar o template, que isso era obrigação da chamada Response.

 

A minha ideia inicial era utilizar o ouput buffer, usar o ob_get_clean para setar o corpo da resposta. Existe outra maneira?

Assim como a Request, minha Response também tem um método send(), o qual envia a Resposta do servidor para o usuário.

 

Aqui é que eu tenho meu echo. No construtor na Response eu inicio o buffer, e no send() envio os headers registrados, ecôo e encerro o buffer.

 

E o que o echo imprime é todo o conteúdo de uma propriedade manipulável através do meu método appendBody(), invocado manual mente ou automaticamente, se a Response receber algo no seu construtor, como o retorno do readAll() feito pela Request.

 

Também é outra classe que carece de atenção pois, por exemplo, ainda não fiz uma verificação de segurançã para que isso funcione:

 

$response = new Response:

$response -> appendBody( 'some content ) -> send() -> appendBody( 'another content' ) -> send();

Isto é, adicionar conteúdo, enviar (o que encerra o buffer), adicionar novas informações e enviar de novo (sem um buffer ativo).

 

Êta tópico bom :lol:

Share this post


Link to post
Share on other sites
A classe Reader é uma classe que lê dados a partir de uma Stream aberta.

Mas é realmente necessário abrir uma stream? E que dados você coloca nela? você utiliza as funções de stream do PHP?

 

Desculpe a insistência, mas eu estou numa fase de capacidade intelectual meio limitada, ehuheuehuhueh... acontece às vezes, passo dias, até semanas sem conseguir aprender NADA.

 

Quando ao adapter, acho que entendi, é a camada responsável por de fato fazer a requisição. No Kohana existe essa estrutura, no Zend também, no Cake a Request é bem mais simples, não consegui localizar algo do tipo.

 

Também é outra classe que carece de atenção pois, por exemplo, ainda não fiz uma verificação de segurançã para que isso funcione:

$response = new Response:
$response -> appendBody( 'some content ) -> send() -> appendBody( 'another content' ) -> send();

Isto é, adicionar conteúdo, enviar (o que encerra o buffer), adicionar novas informações e enviar de novo (sem um buffer ativo).

 

Mas por que enviar o conteúdo e depois enfiar mais coisa do body (pra não dizer "corpo", hehe)?

 

Êta tópico bom laugh.gif

É, espero que me esclareça as ideias...

 

Valeu pela colaboração B)

Share this post


Link to post
Share on other sites

Errrr.... Tem algum outro jeito de abrir um arquivo/URL? Agora fiquei curioso. fopen(), file_get_contents()... Tudo isso não está abrindo uma Data Stream?

 

Quando ao segundo QUOTE, bom, eu sei que não devo fazer isso, tanto porque não pode, quanto porque não faz sentido. Mas, quem sabe um dia talvez, se eu resolver liberr esse sistema, não venha um abençoado e resolve fazer isso.

 

E ainda fica gritando e enchendo que tá tendo erro disso, daquilo... Não tenha tanta paciência assim não.

Share this post


Link to post
Share on other sites

Nessa classe, sim.

 

Mas como ela serve para leitura de Streams, qualquer recurso que rpecisar de seus préstimos, irá usá-la, como foi exemplo citado do leitor de arquivos MO, que nada mais é do que abrir uma STream sobre o arquivo desejado e interpretá-lo (da forma cabeluda que esse negócio é).

Share this post


Link to post
Share on other sites

Uma outra dúvida, o arquivo "php://input" contém exatamente o corpo da requisição? Ou estou imaginando coisas?

 

--------------- Ponto de Mesclagem ---------------

 

O que seriam esses arquivos MO? Dei uma pesquisada rápida e achei algo para C/C++, utilizando o padrão State, não entendi muito bem...

Share this post


Link to post
Share on other sites

O que seriam esses arquivos MO? Dei uma pesquisada rápida e achei algo para C/C++, utilizando o padrão State, não entendi muito bem...

Não sei se são assuntos relacionados, mas a leitura de arquivos MO a que me refiro são daquelesarquivos com sentenças de idiomas, cuja estrutura pode ser vista aqui.

Share this post


Link to post
Share on other sites

Verdade, vamos nos ater ao assunto do tópico =].

 

Bom, depois de muito quebrar a cabeça, decidi me basear no Zend e no Cake. O Cake possui um conceito legal de 'detectors' para a requisição que me permite adicionar métodos de identificação de uma requisição.

 

Posso fazer chamadas como:

$request->is('ajax');
$request->is('external');

 

Se precisar, posso definir meus próprios detectors =].

 

Por enquanto, não vou processar outras requisições 'reais', apenas a principal mesmo.

Requisições internas poderão ser feitas.

 

Segue o código:

 

<?php
/**
* Representa uma requisição.
* @author henrique
*/
class Request {
/**
 * Scheme para http
 */
const SCHEME_HTTP  = 'http';

/**
 * Scheme para https
 */
const SCHEME_HTTPS = 'https';

/**
 * Parâmetros de routing parseados a partir da URL.
 * 
 * @var array
 */
private $_params = array(
	'module' => null,
	'controller' => null,
	'action' => null
);

/**
 * Parâmetros POST da requisição.
 * 
 * @var array
 */
private $_post = array();

/**
 * Parâmetros GET da requisição (via query string).
 * 
 * @var array
 */
private $_get = array();

/**
 * Arquivos enviados junto com a requisição.
 * 
 * @var array
 */
private $_files = array();

/**
 * Os cookies enviados junto com a requisição.
 * 
 * @var array
 */
private $_cookies = array();

/**
 * Se a requisição atual já foi despachada ou não.
 * 
 * @var boolean
 */
private $_dispatched = false;

/**
 * A URI da requisição.
 * 
 * @var string
 */
private $_uri;

/**
 * A URL base da aplicação.
 * 
 * @var string
 */
private $_baseUrl;

/**
 * O caminho base da requisição.
 * 
 * @var string
 */
private $_basePath;

/**
 * O corpo da requisição.
 * 
 * @var string
 */
private $_rawBody;

/**
* Os detectores padrão usados no método is().
* @see Request::addDetector
* @var array
*/
private $_detectors = array(
	'get' => array('env' => 'REQUEST_METHOD', 'value' => 'GET'),
	'post' => array('env' => 'REQUEST_METHOD', 'value' => 'POST'),
	'put' => array('env' => 'REQUEST_METHOD', 'value' => 'PUT'),
	'delete' => array('env' => 'REQUEST_METHOD', 'value' => 'DELETE'),
	'head' => array('env' => 'REQUEST_METHOD', 'value' => 'HEAD'),
	'options' => array('env' => 'REQUEST_METHOD', 'value' => 'OPTIONS'),
	'secure' => array('env' => 'HTTPS', 'value' => 1),
	'ajax' => array('env' => 'HTTP_X_REQUESTED_WITH', 'value' => 'XMLHttpRequest'),
	'flash' => array('env' => 'HTTP_USER_AGENT', 'pattern' => '/^(Shockwave|Adobe) Flash/'),
	'mobile' => array('env' => 'HTTP_USER_AGENT', 'options' => array(
		'Android', 'AvantGo', 'BlackBerry', 'DoCoMo', 'Fennec', 'iPod', 'iPhone', 'iPad',
		'J2ME', 'MIDP', 'NetFront', 'Nokia', 'Opera Mini', 'Opera Mobi', 'PalmOS', 'PalmSource',
		'portalmmm', 'Plucker', 'ReqwirelessWeb', 'SonyEricsson', 'Symbian', 'UP\\.Browser',
		'webOS', 'Windows CE', 'Windows Phone OS', 'Xiino'
	))
);

/**
 * Nome do módulo da requisição.
 * 
 * @var string
 */
private $_module;

/**
 * Nome do controller da requisição.
 * 
 * @var string
 */
private $_controller;

/**
 * Nome da ação da requisição.
 * 
 * @var string
 */
private $_action;

/**
 * Nome do parâmetro que indica o módulo da requisição.
 * 
 * @var string
 */
private $_moduleKey = 'module';

/**
 * Nome do parâmetro que indica o controller da requisição.
 *
 * @var string
 */
private $_controllerKey = 'controller';

/**
 * Nome do parâmetro que indica a ação da requisição.
 *
 * @var string
 */
private $_actionKey = 'action';

/**
 * Construtor.
 * 
 * @param string $uri : URI da requisição
 * @param boolean $parseEnvironment : se os dados dos arrays globais _GET, _POST, _FILES devem ser processados
 */
public function __construct($uri = null, $parseEnvironment = true) {
	$this->setUri($uri);

	if($parseEnvironment) {
		$this->_proccessGet();
		$this->_proccessPost();
		$this->_proccessFiles();
		$this->_proccessCookies();
	}
}

/**
 * Seta o nome do módulo da requisição.
 * 
 * @param string $module
 * @return Request : fluent interface
 */
public function setModule($module) {
	$this->_module = (string) $module;
	return $this;
}

/**
 * Retorna o nome do módulo da requisição.
 * 
 * @return string
 */
public function getModule() {
	return $this->_module;
}

/**
 * Seta o nome do controller da requisição.
 *
 * @param string $controller
 * @return Request : fluent interface
 */
public function setController($controller) {
	$this->_controller = (string) $controller;
	return $this;
}

/**
 * Retorna o nome do controller da requisição.
 *
 * @return string
 */
public function getController() {
	return $this->_controller;
}

/**
 * Seta o nome da ação da requisição.
 *
 * @param string $action
 * @return Request : fluent interface
 */
public function setAction($action) {
	$this->_action = (string) $action;
	return $this;
}

/**
 * Retorna o nome da ação da requisição.
 *
 * @return string
 */
public function getAction() {
	return $this->_action;
}

/**
 * Seta o nome do parâmetro que armazena o módulo da requisição.
 * 
 * @param string $key
 * @return Request : fluent interface
 */
public function setModuleKey($key) {
	$this->_moduleKey = (string) $key;
	return $this;
}

/**
 * Retorna o nome do parâmetro que armazena o módulo da requisição.
 * 
 * @return string
 */
public function getModuleKey() {
	return $this->_moduleKey;
}

/**
 * Seta o nome do parâmetro que armazena o controller da requisição.
 * 
 * @param string $key
 * @return Request : fluent interface
 */
public function setControllerKey($key) {
	$this->_controllerKey = (string) $key;
	return $this;
}

/**
 * Retorna o nome do parâmetro que armazena o controller da requisição.
 * 
 * @return string
 */
public function getControllerKey() {
	return $this->_controllerKey;
}

/**
 * Seta o nome do parâmetro que armazena a ação da requisição.
 * 
 * @param string $key
 * @return Request : fluent interface
 */
public function setActionKey($key) {
	$this->_actionKey = (string) $key;
	return $this;
}

/**
 * Retorna o nome do parâmetro que armazena a ação da requisição.
 * 
 * @return string
 */
public function getActionKey() {
	return $this->_actionKey;
}

/**
 * Processa os dados GET da requisição, colocando-os neste objeto.
 * 
 * @return void
 */
private function _proccessGet() {
	foreach($_GET as $key => $value) {
		if(!empty($value)) {
			$this->_get[$key] = $value;
		}
	}
}

/**
 * Processa os dados POST da requisição, colocando-os neste objeto.
 * 
 * @return void
 */
private function _proccessPost() {
	foreach($_POST as $key => $value) {
		if(!empty($value)) {
			$this->_post[$key] = $value;
		}
	}
}

/**
 * Processa os dados de $_FILES, colocando-os neste objeto.
 * Se tivermos um upload múltiplo, os dados serão transpostos, ficando da forma:
 * array (
 * 		0 => array (
 * 			'name' => 'file.txt'
 * 			'tmp_name' => 'afd2213a121.tmp'
 * 			'mime' => 'text/plain'
 * 			'size' => '1024'
 * 			'error' => 0
 * 		),
 * 		1 => array (
 * 			'name' => 'file.jpg'
 * 			'tmp_name' => 'bfc2419a329.tmp'
 * 			'mime' => 'image/jpeg'
 * 			'size' => '104321'
 * 			'error' => 0
 * 		),
 * 		...
 * )
 * 
 * @return void
 */
private function _proccessFiles() {
	if(isset($_FILES)){
		foreach($_FILES as $key => $value) {
			// Transpõe o array $_FILES em caso de upload múltiplo.
			if(is_array(current($value))) {
				foreach($value as $fileKey => $fileVal) {
					for($i = 0; $i < count($fileVal); $i++) {
						$this->_files[$key][$i][$fileKey] = $fileVal[$i];
					}
				}
			} else {
				$this->_files[$key] = $value;
			}
		}
	}
}

/**
 * Processa dos dados de $_COOKIE, colocando-os dentro do objeto.
 * 
 * @return void
 */
private function _proccessCookies() {
	if(isset($_COOKIE)) {
		foreach($_COOKIE as $key => $value) {
			if(!empty($value)) {
				$this->_cookies[$key] = $value;
			}
		}
	}		
}

/**
 * Seta a URI da requisição. Se nenhum parâmetro for informado, 
 * tenta obter esse valor a partir das variáveis do servidor.
 * 
 * @param string|null $reqUri
 * @return Request : fluent interface
 */
public function setUri($reqUri = null) {
	if($reqUri === null) {
		if($envUri = $this->getServer('HTTP_X_REWRITE_URL')) {
			$reqUri = $envUri;
		} elseif($this->getServer('IIS_WasUrlRewritten') == 1 &&
				 $envUri = $this->getServer('ENCODED_URL')) {
			$reqUri = $envUri;
		} elseif($envUri = $this->getServer('REQUEST_URI')) {
			$reqUri = $envUri;
		} 
	} 

	if(!is_string($reqUri)) {
		trigger_error('Impossível determinar a URI da requisição atual!', E_USER_NOTICE);
		return $this;
	}

	$uri = new Uri($reqUri);
	$this->_get = array_merge($this->_get, $uri->query());
	$this->_uri = $uri->path();

	return $this;
}

/**
 * Retorna a URI da requisição.
 * 
 * @return string
 */
public function getUri() {
	if(empty($this->_uri)) {
		$this->setUri();
	}
	return $this->_uri;
}

/**
 * Seta a URL base da aplicação.
 * 
 * @param string|null $baseUrl
 */
public function setBaseUrl($baseUrl = null) {
	if($baseUrl !== null && !is_string($baseUrl)) {
		return $this;
	}

	if($baseUrl == null) {
		$fileName = basename($this->getServer('SCRIPT_FILENAME'));

		$scriptName = $this->getServer('SCRIPT_NAME');
		$phpSelf = $this->getServer('PHP_SELF');
		$origScriptName = $this->getServer('ORIG_SCRIPT_NAME');

		if($scriptName && basename($scriptName) == $fileName) {
			$baseUrl = $scriptName;
		} elseif($phpSelf && basename($phpSelf) == $filename) {
			$baseUrl = $phpSelf;
		} else if($origScriptName && basename($origScriptName) == $filename) {
			$baseUrl = $origScriptName;
		} else {
			$path = $phpSelf;
			$file = $filename;

			$segs = array_reverse(explode('/', rtrim($file, '/')));
			$index = 0;
			$last = count($segs);
			$baseUrl = '';

			do {
				$seg = $segs[$index];
				$baseUrl .= '/' . $seg . $baseUrl;
				$index++;
			} while($last > $index && strpos($path, $baseUrl) != 0);
		}


		$requestUri = $this->getUri();
		if(strpos($requestUri, $baseUrl) === 0) {
			$this->_baseUrl = $baseUrl;
			return $this;
		}

		if(strpos($requestUri, dirname($baseUrl)) === 0) {
			$this->_baseUrl = rtrim(dirname($baseUrl), '/');
			return $this;
		}

		$truncReqUri = reset(explode('?', $requestUri));

		$baseName = baseName($baseUrl);
		if(empty($basename) || !strpos($truncReqUri, $basename)) {
			$this->_baseUrl = '';
			return $this;
		} 

		if(strlen($requestUri) >= strlen($baseUrl) 
		   && ($pos = strpos($requestUri, $baseUrl)) != 0) {
				$baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl));
		}
	}

	$this->_baseUrl = rtrim($baseUrl, '/');
	return $this;
}

/**
 * Retorna a URL base da requisição.
 * 
 * @param boolean $raw : se FALSE, a url deve ser codificada com urlencode
 * @return string
 */
public function getBaseUrl($raw = true) {
	if(empty($this->_baseUrl)) {
		$this->setBaseUrl();
	}
	return $raw === true ? $this->_baseUrl : urlencode($this->_baseUrl);
}

/**
 * Seta o caminho base da requisição.
 * 
 * @param string|null $path
 * @return Request
 */
public function setBasePath($path = null) {
	if($path === null) {
		$baseUrl = $this->getBaseUrl();
		if(empty($baseUrl)) {
			$this->_basePath = '';
			return $this;
		}

		$fileName = basename($this->getServer('SCRIPT_FILENAME'));
		if(basename($baseUrl) == $fileName) {
			$path = dirname($baseUrl);
		} else {
			$path = $baseUrl;
		}
	}

	// Diretórios no Windows podem ser separados por \
	if(stripos(PHP_OS, 'WIN') === 0) {
		$path = str_replace('\\', '/', $path);
	}

	$this->_basePath = rtrim($path, '/');
	return $this;
}

/**
 * Retorna o caminho base da requisição.
 * 
 * @return string
 */
public function getBasePath() {
	if($this->_basePath === null) {
		$this->setBasePath();
	}

	return $this->_basePath;
}

/**
 * Seta um parâmetro de routing na requisição.
 * 
 * @param string $key
 * @param mixed $value
 * @return Request : fluent interface
 */
public function setParam($key, $value) {
	$this->_params[(string) $key] = $value;
	return $this;
}

/**
 * Retorna um parâmetro de routing da requisição.
 * 
 * @param string $key
 * @param mixed $default : o valor padrão de retorno, caso não exista o parâmetro $key
 */
public function getParam($key, $default = null) {
	$key = (string) $key;
	return $this->hasParam($key) ? $this->_params[$key] : $default;
}

/**
 * Verifica se um dado parâmetro de routing existe.
 * 
 * @param string $key
 * @return boolean
 */
public function hasParam($key) {
	$key = (string) $key;
	return isset($this->_params[$key]);
}

/**
 * Remove um parâmetro de routing.
 * 
 * @param string $key
 * @return boolean
 */
public function unsetParam($key) {
	if($this->hasParam($key)) {
		unset($this->_params[$key]);
		return true;
	}
	return false;
}

/**
 * Seta um parâmetro POST na requisição.
 * 
 * @param string $spec : se é uma string, o parâmetro $value é obrigatório
 * 						 se é um array, deve ser associativo nome => valor
 * @param mixed $value
 * @return Request : fluent interface
 */
public function setPost($spec, $value = null) {
	return $this->_setVar('_post', $spec, $value);
}

/**
 * Retorna um parâmetro POST da requisição ou todos eles, se $key for NULL
 * 
 * @param string|null $key
 * @param mixed $default : o valor padrão de retorno, caso não exista o parâmetro $key
 * @return array|mixed|null
 */
public function getPost($key = null, $default = null) {
	return $this->_getVar('_post', $key, $default);
}

/**
 * Seta um parâmetro GET na requisição.
 * 
 * @param mixed $spec: se é uma string, o parâmetro $value é obrigatório
 * 						se é um array, deve ser associativo nome => valor
 * @param string $value
 * @return Request
 */
public function setQuery($spec, $value = null) {
	return $this->_setVar('_query', $spec, $value);
}

/**
 * Retorna um parâmetro POST na requisição.
 * 
 * @param string|null $key
 * @param mixed $default : o valor padrão de retorno, caso não exista o parâmetro $key
 * @return array|mixed|null
 */
public function getQuery($key = null, $default = null) {
	return $this->_getVar('_get', $key, $default);
}

/**
 * Retorna um cookie da requisição.
 * 
 * @param string|null $key
 * @param mixed $default : o valor padrão de retorno, caso não exista o parâmetro $key
 * @return array|mixed|null
 */
public function getCookie($key = null, $default = null) {
	return $this->_getVar('_cookie', $key, $default);
}

/**
 * Retorna uma variável da requisição (_get, _post, _cookie ou _files).
 * 
 * @param string $varName
 * @param string|null $key
 * @param mixed $default
 * @return array|mixed|null
 */
private function _getVar($varName, $key, $default) {
	if($key === null) {
		return $this->{$varName};
	}
	$key = (string) $key;
	return isset($this->{$varName}[$key]) ? $this->{$varName}[$key] : $default;
}

/**
 * Seta uma variável da requisição (_get ou _post)
 * 
 * @param string $varName
 * @param string|array $spec : se é uma string, o parâmetro $value é obrigatório
 * 							   se é um array, deve ser associativo nome => valor
 * @param mixed $value
 * @throws Request_Exception : se $spec não é um array e $value é NULL
 * @return Request : fluent interface
 */
private function _setVar($varName, $spec, $value) {
	if($value === null){
		if(is_array($spec)) {
			foreach($spec as $key => $value) {
				$this->_setVar($vaName, $key, $value);
			}
			return $this;
		} else {
			throw new Request_Exception(sprintf('Argumentos inválidos para o médodo %s;	deve ser um
														array de valores ou um par chave/valor', __FUNCTION__));
		}
	}

	$this->{$varName}[(string) $spec] = $value;
	return $this;
}

/**
 * Retorna o método da requisição.
 * @return string|null
 */
public function getMethod() {
	return $this->getServer('REQUEST_METHOD');
}

/**
 * Faz uso dos detectores para características da requisição.
 * @param string $type
 */
public function is($type) {
	$type = strtolower($type);
	if(!isset($this->_detectors[$type])) {
		return false;
	}

	$detector = $this->_detectors[$type];
	$envVar = $this->getServer($detector['env']);

	if(isset($detector['env'])) {
		if(isset($detector['value'])) {
			return ($envVar == $detector['value']);
		}

		if(isset($detector['pattern'])) {
			return (bool) preg_match($detector['pattern'], $envVar);
		}

		if(isset($detector['options'])) {
			$pattern = '/' . join('|', $detect['options']) . '/i';
			return (bool) preg_match($pattern, $envVar);
		}
	}

	if(isset($detector['callback']) && is_callable($detector['callback'])) {
		return call_user_func($detect['callback'], $this);
	}

	return false;
}

/**
 * Adiciona um detector na lista de detectores que a requisição pode utilizar.
 *
 * Existem 4 formatos diferentes para a criação de detectores:
 * <ul>
 * 	<li>
 * 		addDetector('post', array('env' => 'REQUEST_METHOD', 'value' => 'POST'))
 * 		Comparação com alguma variável do ambiente.
 * 	</li>
 * 	<li>
 * 		addDetector('iphone', array('env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i'))
 * 		Comparação com alguma variável do ambiente através de uma expressão regular.
 * 	</li>
 * 	<li>
 * 		addDetector('mobile', array('env' => 'HTTP_USER_AGENT', 'options' => array('Fennec', 'Opera Mini'))
 * 		Comparação com uma lista de valores, com a qual é gerada uma expressão regular para comparação.
 * 	</li>
 * <li>
 * 		addDetector('custom', array('env' => 'HTTP_USER_AGENT', 'callback' => 'someFunction')
 * 		Utiliza o callback informado para manipular a checagem. O único argumento passado
 * 		para o callback é o objeto Http_Request. O tipo de retorno deve ser booleano.
 * 	</li>
 * </ul>
 *
 *
 * @param string $name
 * @param array $options
 */
public function addDetector($name, $options) {
	$name = strtolower($name);
	$this->_detectors[$name] = $options;
}

/**
 * Realiza a leitura do conteúdo de 'php://input'. 
 * Útil quando interagimos com requisições JSON ou XML.
 * 
 * Conseguindo um input com uma função de decodificação:
 * $request->input('json_decode');
 * 
 * Utilizando um callback com parâmetros:
 * $request->input('someFunction', $arg1, [[$arg2], $arg3, ...]);
 * 
 * @param string $callback
 * @param mixed $arg1 [OPCIONAL]
 * @param mixed $_ [OPCIONAL]
 * @return string
 */
public function getRawBody($callback) {
	$body = $this->_readInput();
	$argv = func_get_args();
	if(!empty($argv)) {
		$callback = array_shift($argv);
		array_unshift($argv, $body);
		return call_user_func_array($callback, $argv);
	}
	return $body;
}

/**
 * Lê o conteúdo de 'php://input'
 * @return string
 */
private function _readInput() {
	if(empty($this->_rawBody)) {
		try {
			$handler = fopen('php://input', 'r');
			$contents = stream_get_contents($handler);
		} catch(ErrorException $e) {
			$contents = '';
		}
		$this->_rawBody = $contents;
	}
	return $this->_rawBody;
}

/**
 * Retorna um header da requisição.
 * 
 * @param string $header
 * @throws Request_Exception
 * @return string|null
 */
public function getHeader($header) {
	if(empty($header)) {
		throw new Request_Exception('O nome do header HTTP é necessário!');
	}

	$varName = 'HTTP_' . strtoupper(str_replace('-', '_', $header));
	return $this->getServer($varName);
}

/**
 * Retorna o scheme da requisição.
 * 
 * @return string
 */
public function getScheme() {
	return $this->getServer('HTTPS') == 'on' ? self::SCHEME_HTTPS : self::SCHEME_HTTP;
}

/**
 * Retorna uma variável do servidor.
 * 
 * @param string $varName
 * @return string|null
 */
public function getServer($varName) {
	return Environment::getVar($varName);
}

/**
 * Retorna o host da requisição.
 * 
 * @return string
 */
public function getHost() {
	static $host;

	if(empty($host)) {
		$host = $this->getServer('HTTP_HOST');
		if($host !== null) {
			return $host;
		}

		$scheme = $this->getScheme();
		$name   = $this->getServer('SERVER_NAME');
		$port   = $this->getServer('SERVER_PORT');

		if($name === null) {
			$host = '';
		} else if($scheme == self::SCHEME_HTTP && $port = 80 || $scheme == self::SCHEME_HTTPS && $port = 443) {
			$host = $name;
		} else {
			$host = $name . ':' . $port;
		}
	}
	return $host;
}

/**
 * Retorna o IP do cliente.
 * 
 * @param boolean $secure : TRUE se houver suspeita que o cliente pode alterar seu próprio IP
 * @return string|null
 */
public function getClientIp($secure = true) {
	static $clientIp;
	if(empty($clientIp)) {
		$clientIp = $this->getServer('REMOTE_ADDR');
		if($secure){ 
			if(($ip = $this->getServer('HTTP_CLIENT_IP')) !== null) {
				$clientIp = $ip;
			} else if(($ip = $this->getServer('HTTP_X_FORWARDED_FOR')) !== null) {
				$clientIp = $ip;
			}
		} 
	}

	return $clientIp;
}

/**
 * Retorna a URL de referência.
 * 
 * @return string
 */
public function getReferer() {
	static $ref;
	if(empty($ref)) {
		$ref = Environment::getVar('HTTP_REFERER');
		$forwarded = Environment::getVar('HTTP_X_FORWARDED_HOST');
		if($forwarded) {
			$ref = $forwarded;
		}

		if(!$ref) {
			$ref = $this->_baseUrl;
		}
	}

	return $ref;
}

/**
* Retorna informações sobre o user agent da requisição.
* @param string|array|null $value : qual informação retornar. Valores possíveis:
* <ul>
* 	<li>browser</li>
* 	<li>version</li>
* 	<li>plataform</li>
* 	<li>robot</li>
*  	<li>raw</li>
* </ul>
* 
* Se nenhum valor for informado, serão retornados todos os possíveis.
* 
* @return string|array
* @uses Core::getConfigFile
*/
public function getUserAgent($value = null) {
	$userAgent = Environment::getVar('HTTP_USER_AGENT');
	if($value == 'raw') {
		return $userAgent;
	}

	static $data;
	static $info;

	$data = Core::getInstance()->getConfigFile('user_agents');

	if(empty($value)) {
		$value = array_keys($data);
	}

	if(is_array($value)) {
		$ret = array();
		foreach($value as $each) {
			$ret[$each] = $this->getUserAgent($each);
		}
		return $ret;
	}

	if(isset($info[$value])) {
		return $info[$value];
	}

	if($value == 'browser' || $value == 'version') {
		$browsers = $data['browser'];

		foreach($browsers as $search => $name) {
			if(stripos($userAgent, $search) !== false) {
				$info['browser'] = $name;

				if(preg_match('#' . preg_quote($search) . '[^0-9.]*+([0-9.][0-9.a-z]*)#i', $userAgent, $matches)) {
					$info['version'] = $matches[1];
				} else {
					$info['version'] = null;
				}
				return $info[$value];
			}
		}
	} elseif(isset($data[$value])) {
		$group = $data[$value];
		foreach($group as $search => $name) {
			if(stripos($userAgent, $search) !== false) {
				return $info[$value] = $name;
			}
		}
	}

	return $info[$value] = null;
}

/**
 * Verifica se a requisição já foi despachada.
 * 
 * @return boolean
 */
public function isDispatched() {
	return $this->_dispatched;
}

/**
 * Seta a flag indicando se a requisição já foi despachada.
 * 
 * @param boolen $opt
 * @return Request : fluent interface
 */
public function setDispatched($opt) {
	$this->_dispatched = (bool) $opt;
	return $this;
}
}

 

Share this post


Link to post
Share on other sites

Henrique, esse seu método getUserAgent não tem muita responsabilidade não ? se fosse eu, criaria um objeto HttpUserAgent, e nele continha os métodos 'getUserAgent' , 'isMobileDevice' , 'getAgentVersion' , 'getEnvironment' .. coisas do tipo, fica bem mais elegante.

 

Daí você mantém esse método na classe que está, mas vai retornar um objeto responsável pelas informações do User-Agent.

 

Outra coisa, seu método _processFiles .. eu faria assim

 

 

<?php

      $FILES = array ( 
             0 => array ( 
                    'file' => 'xxx.jpg' ,
                    'contentType' => 'image/jpeg'
                    // ...
             ) ,
             1 => array ( 
                    'file' => 'xxx.jpg' ,
                    'contentType' => 'image/jpeg'
             ) ,

             // nunca tentei .. mas 
             3 => array ( 
                    1 => array ( 
                        'file' => 'ccc.png' ,
                        'contentType' => 'image/png'
                    ) ,
                    2 => array ( 
                        'file' => 'yyy.png' ,
                        'contentType' => 'image/png'
                    )
             )
      ) ;

      $flag = RecursiveIteratorIterator::CATCH_GET_CHILD ;
      $arrayIterator = new RecursiveArrayIterator ( $FILES ) ;
      $iterator = new RecursiveIteratorIterator ( $arrayIterator ) ;

      echo '<pre>' ;
      for ( $iterator->rewind ( ) ; $iterator->valid ( ) ; $iterator->next ( ) ) 
             $_files [ $iterator->key ( ) ] [ ] = $iterator->current ( ) ;

      print_r ( $_files ) ;

 

 

 

Na verdade, tudo que possa se duplicar, eu aplicaria um iterator separado, tipo HttpDataIterator .. já que files, posts .. até query string pode haver múltiplos parâmetros, e então esse Iterator já te traria o array de tal forma, que seu objeto possa reconhecer .. SEMPRE no mesmo padrão.

 

Outra coisa .. '://input' , '://output' => InputStreamReader , OutputStreamReader , tanto Reader qnto Writer .. objetos separados ..

 

 

<?php
      class Response {

             public function getInputStream ( ) {
                    return new InputStream ( ) ;
             }

             public function getOutputStream ( ) {
                    return new OutputStream ( ) ;
             }

      }

      class InputStream {

             public function getReader ( ) { /* ... */ }
             public function canRead ( ) { /* ... */ } 

      }

      class OutputStream {

             public function getReader ( ) { /* ... */ }
             public function write ( $content ) { /* ... */ } 
             public function canWrite ( )  { /* ... */ } 
             public function canRead ( ) { /* ... */ } 

      }

      class InputStreamReader { } 
      class OutputStreamReader { } 

      echo '<pre>';
             $fHandler = fopen ( 'php://output' , 'rw' ) ;
             print_r ( stream_get_meta_data ( $fHandler ) ) ;


             $fHandler = fopen ( 'php://input' , 'rw' ) ;
             print_r ( stream_get_meta_data ( $fHandler ) ) ;
      echo '</pre>';

 

Saída:

Array
(
   [wrapper_type] => PHP
   [stream_type] => Output
   [mode] => wb
   [unread_bytes] => 0
   [seekable] => 
   [uri] => php://output
   [timed_out] => 
   [blocked] => 1
   [eof] => 
)
Array
(
   [wrapper_type] => PHP
   [stream_type] => Input
   [mode] => rb
   [unread_bytes] => 0
   [seekable] => 
   [uri] => php://input
   [timed_out] => 
   [blocked] => 1
   [eof] => 
)

 

 

 

Testa o 'mode' do array retornado para saber se pode escrever / ler ..

 

Pergunta:

do {
    $seg = $segs[$index];
    $baseUrl .= '/' . $seg . $baseUrl;
    $index++;
} while($last > $index && strpos($path, $baseUrl) != 0);

 

Qual o motivo do uso do 'do-while' ?

 

EU mudaria uma pá de coisas nesse code aí .. aplicaria um strategy para pegar dados enviados em uma requisição .. isso ainda PELO MENOS evitaria executar um parse para algo que nem temos.

Share this post


Link to post
Share on other sites

×

Important Information

Ao usar o fórum, você concorda com nossos Terms of Use.