Ir para conteúdo

POWERED BY:

Arquivado

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

Victor Ferreira

Copiar array de objetos

Recommended Posts

Bom, amigos, pra quem conhece a teoria dos grafos ficará mais fácil entender a modelagem do problema.

 

Tenho uma classe chamada Grafo. Outra classe chamada Vértice. Uma outra classe chama-se CaixeiroViajante e uma outra ainda chama-se VizinhoMaisProximo. Estou estudando heurísticas do CaixeiroViajante.

 

Bom, eu leio um arquivo texto e crio para cada linha neste arquivo um objeto vértice Vértice com nome (chave associativa para a posição do vértice 1, 2, 3...), coordenadas X e Y, e um Booleano dizendo se este vértice foi visitado ou não. uma e armazeno cada um dos vértices numa estrutura de dados chamado ArrayVertices que fica na classe Grafo.

 

Então o Grafo possui as referências para estes objetos vértices num array. Ai eu passo o objeto Grafo pra classe CaixeiroViajante que vai realizando as operações. O problema é que, se eu tentar fazer mais de uma operação seguida (exemplo, se eu quiser calcular o caminho pelo Vizinho Mais Proximo mais de uma vez ou N vezes dentro de um LOOP), eu tenho um problema com o array de Vértices (alias, com os objetos dentro dele). O problema é que eu só faço os cálculos se os vértices não tiverem sido visitados. Porém, quando eu faço a primeira operação sobre os vértices, eu mudo a variável booleana de false pra true. Portanto, a partir da 2º vez ele acha que tudo já foi visitado e pára de calcular.

 

Para resolver isso, eu poderia criar cópias deste array de vértices. Mas se eu clonar o objeto Grafo, não dá certo pois o array de vértices dentro dele é um array de referencias, portanto ele mantém os ponteiros para aqueles objetos Vértices (que estarão todos visitados depois da primeira passagem por eles).

 

Algum de vocês sabe como resolver este problema? Conseguir copiar pelo menos uma vez esse Array de objetos? Pois se eu fizer isto, eu consigo guardar tudo numa variável aleatória e ficar sobrescrevendo sempre que eu tiver que calcular novamente.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Em PHP, objetos são passador SEMPRE por referência.

Para evitar isso, use a palavra chave clone, que cria uma cópia de um dado objeto.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Victor Ferreira,

 

A solução para o seu problema chama-se Memento, mas poste o código seu código todo aqui, é sempre divertido brincar com Grafo.

 

:D

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá, amigos, desculpem a demora para responder

 

Bom, o código é um pouco grande, mas está assim:

 

index.php, o que eu acesso quando quero calcular as heurísticas

<?php
require_once 'Vertice.class';
require_once 'Grafo.class';

$grafo = new Grafo();

$arquivo = fopen('grafo.txt' ,'r');

while(($linha = fgets($arquivo)) !== false){
   $partesVertice = explode(' ', $linha);
   $vertice = new Vertice($partesVertice[0], $partesVertice[1], $partesVertice[2]);
   $grafo->adicionarVertice(clone $vertice);
}

fclose($arquivo);
error_reporting(E_ALL);
ini_set('display_errors','On');
require_once 'CaixeiroViajante.class';
$caixeiroViajante = new CaixeiroViajante($grafo);

//$caixeiroViajante->calcularVizinhoMaisProximo();
$caixeiroViajante->TodosVizinhoMaisProximo();

?>

 

Vertice.class, aqui está o 'problema'.

<?php 
class Vertice{
   public $x;
   public $y;
   public $nome;
   public $visitado;

   public function __construct($nome, $x, $y) {
       $this->nome = $nome;
       $this->x = $x;
       $this->y = $y;
       $this->visitado = false;
   }
}
?>

 

Grafo.class, que armazena o arrayVertices e tem uns métodos de debugg

<?php
require_once 'Vertice.class';

class Grafo {
   public $arrayVertices;
   public $arrayVerticesCopia;
   public $arrayVerticesNaoVisitados;

   public function __construct() {
       $this->arrayVertices = array();
       $this->tabelaAdjacencias = array();
       $this->caminho = array();
   }

   public function adicionarVertice($vertice){
       $this->arrayVerticesNaoVisitados[$vertice->nome] = true;
       $this->arrayVertices[$vertice->nome] = $vertice;
   }

   public function printarNomeVertices(){
       foreach($this->arrayVertices as $chave => $valor) echo $chave.'<BR/>';
   }

   public function retornarQuantidadeVertices(){
       return count($this->arrayVertices);
   }

   public function printarPosXY($pos){
       echo $this->arrayVertices[$pos]->x.'<BR/>';
       echo $this->arrayVertices[$pos]->y.'<BR/>';
   }

   public function calcularDistancia($vertice1, $vertice2){
       return sqrt( pow($vertice2->x - $vertice1->x, 2) + pow($vertice2->y - $vertice1->y, 2) );
   }
}
?>

 

CaixeiroViajante.class que é a classe que chama as heuristicas, calcula tempo, printa na tela o resultado.

<?php
require_once 'Grafo.class';

class CaixeiroViajante {

   public $grafo;
   public $caminho;
   public $distanciaCaminho;
   public $verticeOrigem;
   public $tempo;

   public function __construct($grafo) {
       $this->grafo = $grafo;
       $this->caminho = array();
       $this->distanciaCaminho = 0;
   }


   public function TodosVizinhoMaisProximo(){
       $ultimoqtdVertices = $this->grafo->retornarQuantidadeVertices() +1;

       $menorTodasDistancias = INF;
       $menorTodosCaminhos = array();
       $grafo = $this->grafo;

       $tempoInicial = $this->retornarTempoSegundos();
       require_once 'VizinhoMaisProximo.class';
       for($i = 1; $i<$ultimoqtdVertices; $i++){
           $caminho = array();

           $vizinhoMaisProximo = new VizinhoMaisProximo(clone $this->grafo, $i);
           $vizinhoMaisProximo->calcular();

           if($vizinhoMaisProximo->distanciaCaminho < $menorTodasDistancias){
               $menorTodasDistancias = $vizinhoMaisProximo->distanciaCaminho;// armazena sempre a menor distancia já calculada
               $menorTodosCaminhos = $vizinhoMaisProximo->caminho;
           }
       }

       $tempoFinal = $this->retornarTempoSegundos();
       $this->tempo = $tempoFinal - $tempoInicial;

       $objeto = new stdClass();
       $objeto->caminho = $menorTodosCaminhos;
       $objeto->distanciaCaminho = $menorTodasDistancias;
       $this->imprimirResultado($objeto);
   }

   private function retornarTempoSegundos(){
       list($usec, $sec) = explode(" ", microtime());
       return ((float)$usec + (float)$sec);
   }


   public function calcularVizinhoMaisProximo(){
       require_once 'VizinhoMaisProximo.class';

       $grafo = $this->grafo;
       $tempoInicial = $this->retornarTempoSegundos();

       $vizinhoMaisProximo = new VizinhoMaisProximo($grafo, rand(1, $this->grafo->retornarQuantidadeVertices()));
       $vizinhoMaisProximo->calcular();
       $tempoFinal = $this->retornarTempoSegundos();
       $this->tempo = $tempoFinal - $tempoInicial;
       $this->imprimirResultado($vizinhoMaisProximo);
   }

   public function imprimirResultado($objeto){
       echo 'Começando pelo vértice '.$this->verticeOrigem.'..\n\n';
       echo 'O caminho é: '.implode('-',$objeto->caminho).'\n\n';
       echo 'E a distância percorrida é de: '.$objeto->distanciaCaminho.'\n\n';
       echo 'O tempo foi: '.$this->tempo.' segundos\n\n\n';
   }
}
?>

 

VizinhoMaisProximo.class, que é a classe da Heurística do Vizinho Mais Próximo. É ela quem calcula os caminhos. Funciona bem se eu chamá-la uma vez só. Mas se eu chamar num loop sem destruir os outros objetos, tudo terá sido visitado a partir da segunda vez.

<?php
class VizinhoMaisProximo {
   public $grafo;
   public $caminho;
   public $distanciaCaminho;
   public $verticeAtual;
   public $verticeOrigem;

   public function __construct($grafo, $verticeOrigem) {
       $this->grafo = $grafo;
       $this->caminho = array();
       $this->distanciaCaminho = 0;

       $this->verticeOrigem = $verticeOrigem;
   }

   public function calcular(){
       $qtdVerticesAux = $this->grafo->retornarQuantidadeVertices();
       $qtdVertices = $qtdVerticesAux + 1;
       $grafo = $this->grafo;
       $caminho = array();
       $verticeAtual = $this->verticeOrigem;
       $distanciaCaminho = 0;

       while($qtdVerticesAux != 0){

           $menorDistancia = INF;

           $grafo->arrayVertices[$verticeAtual]->visitado = true; //visitou
           $caminho[] = $verticeAtual;//adiciona ao caminho
           $verticeRetido = $verticeAtual;

           for($posVizinho = 1; $posVizinho < $qtdVertices; $posVizinho++){
               if($grafo->arrayVertices[$posVizinho]->visitado == false){//só se não tiver sido visitado
                   $distancia = $grafo->calcularDistancia($grafo->arrayVertices[$verticeAtual], $grafo->arrayVertices[$posVizinho]);

                   if($distancia < $menorDistancia){
                       $menorDistancia = $distancia;
                       $verticeRetido = $posVizinho;
                   }
               }

           }
           $verticeAtual = $verticeRetido;
           if(!is_infinite($menorDistancia)) $distanciaCaminho += $menorDistancia;
           $qtdVerticesAux--;
       }
       //volta à origem
       $caminho[] = $this->verticeOrigem;
       $distanciaCaminho += $grafo->calcularDistancia($grafo->arrayVertices[$verticeAtual], $grafo->arrayVertices[$this->verticeOrigem]);

       //torna global
       $this->caminho = $caminho;
       $this->distanciaCaminho = $distanciaCaminho;
   }
}
?>

 

Como eu poderia usar Memento para solucionar o meu problema?

 

Abraços!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Victor,

 

Analisando seu código, vi que não é um caso de uso de Memento. Na verdade, existe um erro de implementação que causou o problema.

 

Vou ilustrar Memento aqui apenas para não ficar a citação no vazio, mas em seguida vou postar o algorítimo do vizinho mais próximo sem a necessidade de Memento.

 

Memento

 

O design pattern Memento permite, sem violar o encapsulamento, manter o estado interno de um objeto e restaurá-lo posteriormente.

 

Algumas vezes é necessário armazenar o estado de um objeto para restaurá-lo depois, por isso achei que seria adequado ao seu problema, pois você poderia ter um ponto de verificação (estado original não visitado), visitá-lo e, quando for necessário, voltar ao estado original (não visitado).

 

gallery_94216_34_5445.png

 

Os participantes do Memento são:

 

Memento:

Armazena o estado interno do Originator. O Memento pode armazenar muito, pouco ou o quanto for necessário do estado interno do Originator.

 

Originator:

O Originator cria um Memento contendo um ponto de verificação de seu estado interno e usa esse Memento para restaurar seu estado posteriormente.

 

Caretaker:

É o responsável por cuidar com segurança do Memento, ele apenas cuida, jamais faz qualquer operação ou analisa o conteúdo do Memento.

As consequências de uso do Memento são óbvias, como o Memento preserva o encapsulamento, informações que apenas o Originator deve conhecer não são expostas o que, consequentemente, não dá detalhes sobre a implementação. Porém, se o Originator possuir um volume de dados muito grande, a implementação pode causar problemas.

 

A implementação, no caso do PHP, é um tanto problemática. O ideal seria se PHP oferecesse artifícios como as classes amigas do C++, dessa forma a interface do Memento seria acessível apenas para o Originator.

 

<?php
class State {
}

class Memento {
private $state;

public function setState( State $state ) {
	$this->state = $state;
}

public function getState() {
	return $this->state;
}
}

class Originator {
private $visited;

public function createMemento() {
	$state = new State();
	$state->visited = $this->visited;

	$memento = new Memento();
	$memento->setState( $state );

	return $memento;
}

public function isVisited() {
	return !!$this->visited;
}

public function makeVisited( $visited = true ) {
	$this->visited = !!$visited;
}

public function setMemento( Memento $memento ) {
	$state = $memento->getState();

	$this->visited = $state->visited;
}
}

 

Claro que essa é uma implementação muito simplista. O uso seria:

 

<?php
$o = new Originator();
$memento = $o->createMemento(); //cria o ponto de verificação

var_dump( $o->isVisited() ); //bool(false)

$o->makeVisited(); //Muda o estado do objeto

var_dump( $o->isVisited() ); //bool(true)

$o->setMemento( $memento ); //restaura o ponto de verificação

var_dump( $o->isVisited() ); //bool(false)

 

Você encontra uma outra implementação de Memento aqui :seta: http://forum.imasters.com.br/topic/426661-log-em-phpmysql/page__gopid__1686430#entry1686430

 

Travelling Salesman Problem

 

O problema do caixeiro viajante é bem interessante, principalmente quando aplicado à logística ou genética. Já o algorítimo nearest neighbour algorithm, dependendo da situação, pode não ser a melhor escolha. Ele consegue gerar um caminho curto rapidamente, mas não necessariamente o caminho ótimo.

 

Claro que TSP é um problema NP-completo com complexidade exponencial, mas o algorítimo do vizinho mais próximo pode não ser eficiente se o comprimento dos últimos passos forem muito maiores que dos primeiros passos.

 

De qualquer forma, você definitivamente não precisa de um participante VizinhoMaisProximo como você tem na sua implementação, principalmente pelo fato do VizinhoMaisProximo ser, por definição, um Vertice e seu participante VizinhoMaisProximo sequer é derivado de Vertice.

 

Outro detalhe da sua implementação é que um Grafo é composto por um conjunto V, não vazio, de vértices e um conjunto de arestas (pares de V). Sua implementação de grafo não leva em consideração as ligações, apenas os pontos, por isso não é, matematicamente, um Grafo.

 

A definição de Grafo seria: Graph<Point,Link<Point,Point>>

 

Sua implementação também dá um excesso de responsabilidade ao participante Grafo: Além de cuidar dos pontos, ele também é responsável pela exibição e isso viola o princípio da responsabilidade única. Escrevi um post sobre isso, veja para compreender o que quero dizer :seta: http://thegodclass.tumblr.com/post/9749358418/refatoracao-s-r-p-contactsdisplay

 

Como estamos falando de caixeiro viajante, vou fazer a implementação do grafo usando os aspectos geográficos, ou seja, não vou abstrair a definição dos pontos nem dos links.

 

com/imasters/tsp/GeoGraph.php

 

 

<?php
/**
* Exemplo de implementação do problema do caixeiro viajante
* @package	com.imasters.tsp
*/

/**
* Definição de um conjunto de pontos ligados por retas.
* @author	João Batista Neto <neto.joaobatista@imasters.com.br>
*/
class GeoGraph implements Countable, IteratorAggregate {
/**
 * @var	array
 */
private $points = array();

/**
 * @var	array
 */
private $links = array();

/**
 * Adiciona uma ligação entre dois pontos ao grafo.
 * @param	GeographicLink $link
 * @throws	InvalidArgumentException Se os dois pontos não estiverem
 * 			contidos no grafo.
 */
public function addLink( GeographicLink $link ) {
	$a = $link->getPointA();
	$b = $link->getPointB();

	if ( $this->contains( $a ) && $this->contains( $b ) ) {
		$this->links[] = $link;
	} else {
		$msg = 'Os dois pontos devem estar contidos no grafo';

		throw new InvalidArgumentException( $msg );
	}
}

/**
 * Adiciona um ponto ao grafo se ele já não estiver contido no grafo.
 * @param	GeographicPoint $point
 */
public function addPoint( GeographicPoint $point ) {
	if ( !$this->contains( $point ) ) {
		$this->points[] = $point;
	}
}

/**
 * Verifica se um ponto está contido no grafo.
 * @param	GeographicPoint $point
 * @return	boolean TRUE se o ponto estiver contido no grafo.
 */
public function contains( GeographicPoint $point ) {
	foreach ( $this->points as $p ) {
		if ( $p === $point ) {
			return true;
		}
	}

	return false;
}

/**
 * Recupera o total de ligações existentes no grafo.
 * @return	integer
 * @see		Countable::count()
 */
public function count() {
	return count( $this->links );
}

/**
 * Recupera o total de pontos existentes no grafo.
 * @return	integer
 */
public function countPoints() {
	return count( $this->points );
}

/**
 * Recupera um Iterator com as ligações existentes no grafo.
 * @return	Iterator
 * @see		IteratorAggregate::getIterator()
 */
public function getIterator() {
	return new ArrayIterator( $this->links );
}

/**
 * Recupera um Iterator com os pontos existentes no grafo.
 * @return	Iterator
 */
public function getPointsIterator() {
	return new ArrayIterator( $this->points );
}
}

 

 

 

O GeoGraph, como já foi dito antes, possui um conjunto de pontos e um conjunto de links (pares de pontos):

 

com/imasters/tsp/GeographicPoint.php

 

 

<?php
/**
* Exemplo de implementação do problema do caixeiro viajante
* @package	com.imasters.tsp
*/

/**
* Requerido para o cálculo da distância entre dois pontos
*/
require_once 'com/imasters/tsp/GeographicLink.php';

/**
* Definição de um ponto geográfico
* @author	João Batista Neto <neto.joaobatista@imasters.com.br>
*/
class GeographicPoint {
/**
 * @var	float
 */
private $latitude;

/**
 * @var	float
 */
private $longitude;

/**
 * Constroi o ponto indicando a latitude e longitude.
 * @param	float $latitude
 * @param	float $longitude
 */
public function __construct( $latitude , $longitude ) {
	$this->latitude = $latitude;
	$this->longitude = $longitude;
}

/**
 * Cria uma ligação entre dois pontos.
 * @param	GeographicPoint $point O ponto de destino
 * @return	GeographicLink A ligação entre os dois pontos
 */
public function createLinkTo( GeographicPoint $point ) {
	$link = new GeographicLink( $this , $point );

	return $link;
}

/**
 * Calcula a distância entre dois pontos.
 * @param	GeographicPoint $point O ponto de destino.
 * @return	float A distância em kilômetros.
 */
public function distanceTo( GeographicPoint $point ) {
	return $this->createLinkTo( $point )->distance();
}

/**
 * Recupera a latitude.
 * @return	float
 */
public function getLatitude() {
	return $this->latitude;
}

/**
 * Recupera a longitude.
 * @return	float
 */
public function getLongitude() {
	return $this->longitude;
}
}

 

 

 

com/imasters/tsp/GeographicLink.php

 

 

<?php
/**
* Exemplo de implementação do problema do caixeiro viajante
* @package	com.imasters.tsp
*/

/**
* Definição de uma ligação entre dois pontos geográficos
* @author	João Batista Neto <neto.joaobatista@imasters.com.br>
*/
class GeographicLink {
/**
 * @var	GeographicPoint
 */
private $a;

/**
 * @var	GeographicPoint
 */
private $b;

/**
 * Constroi um objeto de ligação entre dois pontos.
 * @param	GeographicPoint $a
 * @param	GeographicPoint $b
 */
public function __construct( GeographicPoint $a , GeographicPoint $b ) {
	$this->a = $a;
	$this->b = $b;
}

/**
 * Calcula a distância entre os dois pontos.
 * @return	float
 */
public function distance() {
	$aLa = $this->a->getLatitude() * M_PI / 180;
	$aLo = $this->a->getLongitude() * M_PI / 180;
	$bLa = $this->b->getLatitude() * M_PI / 180;
	$bLo = $this->b->getLongitude() * M_PI / 180;
	$dLa = $bLa - $aLa;
	$dLo = $bLo - $aLo;

	$a = pow( sin( $dLa / 2 ) , 2 ) + cos( $aLa )
	   * pow( sin( $dLo / 2 ) , 2 ) * cos( $bLa );

	return 12742 * atan2( sqrt( $a ) , sqrt( 1 - $a ) );
}

/**
 * Recupera o ponto A.
 * @return	GeographicPoint
 */
public function getPointA() {
	return $this->a;
}

/**
 * Recupera o ponto B.
 * @return	GeographicPoint
 */
public function getPointB() {
	return $this->b;
}
}

 

 

 

Como uma cidade é um ponto no mapa que possui um nome, vamos derivar o GeographicPoint para que ele possua um nome:

 

com/imasters/tsp/City.php

 

 

<?php
/**
* Exemplo de implementação do problema do caixeiro viajante
* @package	com.imasters.tsp
*/

/**
* A cidade é um ponto geográfico que possui um nome
*/
require_once 'com/imasters/tsp/GeographicPoint.php';

/**
* Definição de uma cidade por onde o caixeiro deverá passar.
* @author	João Batista Neto <neto.joaobatista@imasters.com.br>
*/
class City extends GeographicPoint {
/**
 * @var	string
 */
private $name;

/**
 * Cria o objeto que representa uma cidade por onde o caixeiro passará.
 * @param	string $name Nome da cidade.
 * @param	float $latitude Latitude da cidade.
 * @param	float $longitude Longitude da cidade.
 */
public function __construct( $name , $latitude , $longitude ) {
	parent::__construct( $latitude , $longitude );

	$this->name = $name;
}

/**
 * Recupera o nome da cidade.
 * @return	string
 */
public function getName() {
	return $this->name;
}
}

 

 

 

E, por fim, o caixeiro viajante, que usará o algorítimo do vizinho mais próximo para percorrer uma lista de cidades:

 

com/imasters/tsp/TravellingSalesmanProblem.php

 

 

<?php
/**
* Exemplo de implementação do problema do caixeiro viajante
* @package	com.imasters.tsp
*/

/**
* Requerido para representação da solução para o problema do caixeiro viajante.
*/
require_once 'com/imasters/tsp/GeoGraph.php';

/**
* Problema do caixeiro viajante.
* @author	João Batista Neto <neto.joaobatista@imasters.com.br>
*/
class TravellingSalesmanProblem {
/**
 * @var	array
 */
private $cities = array();

/**
 * Adiciona uma cidade à lista de cidades por onde o caixeiro deverá
 * passar.
 * @param	City $city
 */
public function addCity( City $city ) {
	$this->cities[] = $city;
}

/**
 * Determina a solução para o problema do caixeiro viajante utilizando
 * o algorítimo do vizinho mais próximo.
 * @return	Graph
 */
public function nearestNeighbourAlgorithm() {
	$visited = array();

	$g = new GeoGraph();
	$c = array_shift( $this->cities );
	$t = count( $this->cities );
	$d = INF;
	$v = null;

	$visited[] = $c;
	$g->addPoint( $c );

	while ( $t > 0 ) {
		foreach ( $this->cities as $i => $city ) {
			if ( ( $cd = $c->distanceTo( $city ) ) < $d ) {
				$d = $cd;
				$v = $i;
			}
		}

		$g->addPoint( $this->cities[ $v ] );
		$g->addLink( $c->createLinkTo( $this->cities[ $v ] ) );
		$c = $this->cities[ $v ];
		$d = INF;

		$visited[] = $c;
		unset( $this->cities[ $v ] );
		--$t;
	}

	$this->cities = $visited; //reset

	return $g;
}
}

 

 

 

A implementação é bem simples, o caixeiro viajante recebe uma lista de cidades por onde ele deverá passar. Com essa lista, pegamos um ponto qualquer e definimos como ponto de partida e, então, localizamos a cidade mais próxima de onde estamos.

 

Esse processo de estar em uma cidade e localizar a cidade mais próxima é feito até que não haja mais cidade para ser visitada.

 

Para deixar a implementação mais divertida, vamos utilizar as localizações reais (latitude e longitude) de algumas cidades e, para isso, vamos fazer a consulta no GoogleMaps pelo nome de cada cidade.

 

A implementação do GoogleMaps ficou bastante simplista por ser meramente ilustrativa:

 

com/imasters/google/maps/GoogleMaps.php

 

 

<?php
/**
* Implementação besta de consulta ao Google Maps
* @package	com.imasters.google.maps
*/

/**
* Conexão HTTP requerida para consulta ao Google Maps
*/
require_once 'com/imasters/http/HTTPConnection.php';

/**
* Implementação simples de busca ao Google Maps
* @author	João Batista Neto <neto.joaobatista@imasters.com.br>
*/
class GoogleMaps {
/**
 * @var	HTTPConnection
 */
private static $httpConnection;

/**
 * Constroi o objeto responsável pelas buscas ao Google Maps
 * @param	string $language
 * @param	string $region
 */
public function __construct( $language = 'pt_BR' , $region = 'br' ) {
	if ( self::$httpConnection == null ) {
		self::$httpConnection = new HTTPConnection();
		self::$httpConnection->initialize( 'maps.googleapis.com' );
		self::$httpConnection->setParam( 'sensor' , 'false' );
		self::$httpConnection->setParam( 'language' , $language );
		self::$httpConnection->setParam( 'region' , $region );
	}
}

/**
 * Efetua uma busca ao Google Maps.
 * @param	string $address Endereço que será buscado.
 * @return	stdClass Objeto JSON convertido em stdClass com o resultado
 * 			da busca.
 * @throws	RuntimeException Caso não seja possível fazer a consulta
 */
public function search( $address ) {
	self::$httpConnection->setParam( 'address' , $address );

	$r = self::$httpConnection->execute( '/maps/api/geocode/json' );

	if ( $r->getStatusCode() == 200 ) {
		return json_decode( $r->getContent() );
	} else {
		throw new RuntimeException( 'Falha ao consultar Google Maps' );
	}
}
}

 

 

 

Essa implementação do GoogleMaps utiliza o pacote HTTP que segue abaixo:

 

com/imasters/http/*

 

 

com/imasters/http/Cookie.php

<?php
/**
* @brief	Protocolo HTTP
* @details	Classes e interfaces relacionadas com o protocolo HTTP
* @package com.imasters.http
*/

/**
* @brief	Cookie HTTP
* @details	Implementação de um cookie HTTP segundo a especificação
* RFC 2109.
*/
class Cookie {
/**
 * @brief	Comentário opcional do cookie
 * @var		string
 */
protected $comment;

/**
 * @brief	Domínio do cookie
 * @var		string
 */
protected $domain;

/**
 * @brief	Expiração do cookie (unix timestamp)
 * @var		integer
 */
protected $expires;

/**
 * @brief	Nome do cookie
 * @var		string
 */
protected $name;

/**
 * @brief	Caminho do cookie
 * @var		string
 */
protected $path;

/**
 * @brief	Ambiente seguro (HTTPS)
 * @details	Indica se o User-Agent deve utilizar o cookie apenas em ambiente
 * 			seguro (HTTPS)
 * @var		boolean
 */
protected $secure;

/**
 * @brief	Valor do cookie
 * @var		string
 */
protected $value;

/**
 * @brief	Constroi um cookie
 * @param	string $name Nome do cookie
 * @param	string $value Valor do cookie
 * @param	string $domain Domínio do cookie
 * @param	integer $expires Timestamp da expiração do cookie
 * @param	string $path Caminho do cookie
 * @param	boolean $secure Se o cookie é usado apenas em ambiente seguro.
 * @param	string $comment Comentário do cookie
 * @throws	InvalidArgumentException Se $expires não for um número
 */
public function __construct( $name,
							$value,
							$domain,
							$expires,
							$path = '/',
							$secure = false,
							$comment = null ) {

	$this->name = (string) $name;
	$this->value = (string) $value;
	$this->domain = (string) $domain;

	if ( is_numeric( $expires ) ) {
		$this->expires = (int) $expires;
	} else {
		$msg = '$expires deve ser um número representando o timestamp da ';
		$msg .= 'expiração do cookie, "' . $expires . '" foi dado.';

		throw new InvalidArgumentException( $msg );
	}

	$this->path = (string) $path;
	$this->secure = $secure === true;
	$this->comment = $comment;
}

/**
 * @brief	Retorna a representação do Cookie como uma string
 * @return	string
 */
public function __toString() {
	return sprintf( '%s=%s' , $this->name , $this->value );
}

/**
 * @brief	Recupera o comentário do cookie
 * @return	string
 */
public function getComment() {
	return $this->comment;
}

/**
 * @brief	Recupera o domínio do cookie
 * @return	string
 */
public function getDomain() {
	return $this->domain;
}

/**
 * @brief	Recupera o timestamp da expiração do cookie
 * @return	integer
 */
public function getExpires() {
	return $this->expires;
}

/**
 * @brief	Recupera o nome do cookie
 * @return	string
 */
public function getName() {
	return $this->name;
}

/**
 * @brief	Recupera o caminho do cookie
 * @return	string
 */
public function getPath() {
	return $this->path;
}

/**
 * @brief	Recupera o valor do cookie
 * @return	string
 */
public function getValue() {
	return $this->value;
}

/**
 * @brief	Verifica ambiente seguro.
 * @details	Verifica se o User-Agent deve utilizar o cookie apenas em
 * 			ambiente seguro.
 * @return	boolean
 */
public function isSecure() {
	return $this->secure;
}
}

 

com/imasters/http/CookieManager.php

<?php
/**
* @brief	Protocolo HTTP
* @details	Classes e interfaces relacionadas com o protocolo HTTP
* @package com.imasters.http
*/

require_once 'com/imasters/http/Cookie.php';

/**
*@brief	Interface para definição de um gerenciador de cookies.
*/
interface CookieManager extends Serializable {
/**
 * @brief	Adiciona um cookie para ser armazenado pelo gerenciador.
 * @param	Cookie $cookie
 */
public function addCookie( Cookie $cookie );

/**
 * @brief	Recupera os cookies armazenados para um determinado domínio.
 * @param	string $domain Domínio dos cookies.
 * @param	boolean $secure Indica ambiente seguro (https).
 * @param	string $path Caminho dos cookies.
 * @return	string O valor retornado segue o padrão especificado pela
 * 			RFC 2965 para ser utilizado diretamente no campo de cabeçalho
 * 			Cookie.
 */
public function getCookie( $domain , $secure , $path );

/**
 * @brief	Recupera uma lista com os cookies gerenciados.
 * @param	string $domain Domínio dos cookies.
 * @param	boolean $secure Indica ambiente seguro.
 * @param	string $path Caminho dos cookies.
 * @return	Iterator
 */
public function getCookieIterator( $domain , $secure , $path );

/**
 * @brief	Define o conteúdo do campo de cabeçalho Set-Cookie
 * retornado pelo servidor.
 * @param	string $setCookie
 * @param	string $domain
 */
public function setCookie( $setCookie , $domain = null );
}

 

com/imasters/http/CURL.php

<?php
/**
* @brief	Protocolo HTTP
* @details	Classes e interfaces relacionadas com o protocolo HTTP
* @package com.imasters.http
*/

require_once 'com/imasters/http/HTTPRequest.php';
require_once 'com/imasters/http/HTTPResponse.php';

/**
* @brief	Requisição HTTP cURL
* @details	Implementação da interface HTTPRequest para uma requisição HTTP que
* 			utiliza cURL.
*/
class CURL implements HTTPRequest {
/**
 * @var	resource
 */
private $curlResource;

/**
 * @var	HTTPConnection
 */
private $httpConnection;

/**
 * @var	HTTPResponse
 */
private $httpResponse;

/**
 * @var	boolean
 */
private $openned = false;

/**
 * @var	string
 */
private $requestBody;

/**
 * @var	array
 */
private $requestHeader = array();

/**
 * @var	array
 */
private $requestParameter = array();

/**
 * @brief	Destroi o objeto
 * @details	Destroi o objeto e fecha a requisição se estiver aberta.
 */
public function __destruct() {
	$this->close();
}

/**
 * @see HTTPRequest::addRequestHeader()
 */
public function addRequestHeader( $name , $value , $override = true ) {
	if ( is_scalar( $name ) && is_scalar( $value ) ) {
		$key = strtolower( $name );

		if ( $override === true || !isset( $this->requestHeader[ $key ] ) ) {
			$this->requestHeader[ $key ] = array(
				'name' => $name , 'value' => $value
			);

			return true;
		}

		return false;
	} else {
		throw new InvalidArgumentException(
			'$name e $value precisam ser strings.'
		);
	}
}

/**
 * @brief	Autentica uma requisição HTTP.
 * @param	HTTPAuthenticator $authenticator
 * @see		HTTPRequest::authenticate()
 */
public function authenticate( HTTPAuthenticator $authenticator ) {
	$authenticator->authenticate( $this );
}

/**
 * @see HTTPRequest::close()
 */
public function close() {
	if ( $this->openned ) {
		curl_close( $this->curlResource );
		$this->openned = false;
	}
}

/**
 * @see HTTPRequest::execute()
 */
public function execute( $path = '/' , $method = HTTPRequest::GET ) {
	$targetURL = $this->httpConnection->getURI() . $path;
	$query = null;

	if ( ( $hasParameters = count( $this->requestParameter ) ) > 0 ) {
		$query = http_build_query( $this->requestParameter );
	}

	switch ( $method ) {
		case HTTPRequest::PUT :
		case HTTPRequest::POST :
			if ( $method != HTTPRequest::POST ) {
				curl_setopt(
					$this->curlResource,
					CURLOPT_CUSTOMREQUEST,
					$method
				);
			} else {
				curl_setopt( $this->curlResource , CURLOPT_POST , 1 );
			}

			if ( empty( $this->requestBody ) ) {
				curl_setopt(
					$this->curlResource,
					CURLOPT_POSTFIELDS,
					$query
				);
			} else {
				if ( $hasParameters ) {
					$targetURL .= '?' . $query;
				}

				curl_setopt(
					$this->curlResource,
					CURLOPT_POSTFIELDS,
					$this->requestBody
				);
			}

			curl_setopt( $this->curlResource , CURLOPT_URL , $targetURL );

			break;
		case HTTPRequest::DELETE :
		case HTTPRequest::HEAD :
		case HTTPRequest::OPTIONS:
		case HTTPRequest::TRACE:
			curl_setopt(
				$this->curlResource,
				CURLOPT_CUSTOMREQUEST,
				$method
			);
		case HTTPRequest::GET:
			if ( $hasParameters ) {
				$targetURL .= '?' . $query;
			}

			curl_setopt( $this->curlResource , CURLOPT_URL , $targetURL );

			break;
		default :
			throw new UnexpectedValueException( 'Método desconhecido' );
	}

	$resp = curl_exec( $this->curlResource );
	$errno = curl_errno( $this->curlResource );
	$error = curl_error( $this->curlResource );

	if ( $errno != 0 ) {
		throw new RuntimeException( $error , $errno );
	}

	$this->httpResponse = new HTTPResponse();
	$this->httpResponse->setRawResponse( $resp );

	if ( $this->httpResponse->hasResponseHeader( 'Set-Cookie' ) ) {
		$cookieManager = $this->httpConnection->getCookieManager();

		if ( $cookieManager != null ) {
			$cookieManager->setCookie(
				$this->httpResponse->getHeader( 'Set-Cookie' ),
				$this->httpConnection->getHostName()
			);
		}
	}

	$statusCode = $this->httpResponse->getStatusCode();

	return $statusCode < 400;
}

/**
 * @see HTTPRequest::getResponse()
 */
public function getResponse() {
	return $this->httpResponse;
}

/**
 * @see HTTPRequest::open()
 */
public function open( HTTPConnection $httpConnection ) {
	if ( function_exists( 'curl_init' ) ) {
		/**
		 * Fechamos uma conexão existente antes de abrir uma nova
		 */
		$this->close();

		$curl = curl_init();

		/**
		 * Verificamos se o recurso CURL foi criado com êxito
		 */
		if ( is_resource( $curl ) ) {
			curl_setopt( $curl , CURLOPT_SSL_VERIFYPEER , 0 );
			curl_setopt( $curl , CURLOPT_HEADER , 1 );
			curl_setopt( $curl , CURLOPT_RETURNTRANSFER , 1 );
			curl_setopt( $curl , CURLINFO_HEADER_OUT , 1 );

			if ( ( $timeout = $httpConnection->getTimeout() ) != null ) {
				curl_setopt( $curl , CURLOPT_TIMEOUT , $timeout );
			}

			$ct = $httpConnection->getConnectionTimeout();

			if ( $ct != null ) {
				curl_setopt( $curl , CURLOPT_CONNECTTIMEOUT , $ct );
			}

			$headers = array();

			foreach ( $this->requestHeader as $header ) {
				$headers[] = sprintf(
					'%s: %s' , $header[ 'name' ] , $header[ 'value' ]
				);
			}

			curl_setopt( $curl , CURLOPT_HTTPHEADER , $headers );

			$this->curlResource = $curl;
			$this->httpConnection = $httpConnection;
			$this->openned = true;
		} else {
			throw new RuntimeException( 'Não foi possível iniciar cURL' );
		}
	} else {
		throw new RuntimeException( 'Extensão cURL não está instalada.' );
	}
}

/**
 * @brief	Define um parâmetro
 * @details	Define um parâmetro que será enviado com a requisição, um
 * 			parâmetro é um par nome-valor que será enviado como uma query
 * 			string (<b>ex:</b> <i>?name=value</i>).
 * @param	string $name Nome do parâmetro.
 * @param	string $value Valor do parâmetro.
 * @throws	InvalidArgumentException Se o nome ou o valor do campo não forem
 * 			valores scalar.
 * @see		HTTPRequest::setParameter()
 */
public function setParameter( $name , $value ) {
	$this->requestParameter[ $name ] = $value;
}

/**
 * @see HTTPRequest::setRequestBody()
 */
public function setRequestBody( $requestBody ) {
	$this->requestBody = $requestBody;
}
}

 

com/imasters/http/HTTPAuthenticator.php

<?php
/**
* @brief	Protocolo HTTP
* @details	Classes e interfaces relacionadas com o protocolo HTTP
* @package com.imasters.http
*/

require_once 'com/imasters/http/HTTPRequest.php';

/**
* @brief	Interface para definição de um autenticador HTTP.
*/
interface HTTPAuthenticator {
/**
 * @brief	Autentica uma requisição HTTP.
 * @param	HTTPRequest $httpRequest
 */
public function authenticate( HTTPRequest $httpRequest );
}

 

com/imasters/http/HTTPConnection.php

<?php
/**
* @brief	Protocolo HTTP
* @details	Classes e interfaces relacionadas com o protocolo HTTP
* @package com.imasters.http
*/

require_once 'com/imasters/http/HTTPAuthenticator.php';
require_once 'com/imasters/http/CURL.php';

/**
* @brief	Implementação de um conector HTTP.
*/
class HTTPConnection {
/**
 * @brief	Porta padrão de uma conexão HTTP não segura.
 */
const HTTP_PORT = 80;

/**
 * @brief	Porta padrão de uma conexão HTTP segura.
 */
const HTTPS_PORT = 443;

/**
 * @var	HTTPAuthenticator
 */
protected $httpAuthenticator;

/**
 * @var	CookieManager
 */
protected $cookieManager;

/**
 * @var	integer
 */
protected $connectionTimeout;

/**
 * @var	string
 */
protected $hostname;

/**
 * @var	boolean
 */
protected $initialized = false;

/**
 * @var	integer
 */
protected $port;

/**
 * @var	string
 */
protected $requestBody;

/**
 * @var	array
 */
protected $requestHeader;

/**
 * @var	array
 */
protected $requestParameter;

/**
 * @var	boolean
 */
protected $secure;

/**
 * @var	integer
 */
protected $timeout;

/**
 * @var	string
 */
protected static $userAgent;

/**
 * @brief	Constroi o objeto de conexão HTTP.
 */
public function __construct() {
	if ( self::$userAgent == null ) {
		$locale = setlocale( LC_ALL , null );

		if ( function_exists( 'posix_uname' ) ) {
			$uname = posix_uname();

			self::$userAgent = sprintf(
				'Mozilla/4.0 (compatible; %s; PHP/%s; %s %s; %s)',
				PHP_SAPI , PHP_VERSION , $uname[ 'sysname' ],
				$uname[ 'machine' ] , $locale
			);
		} else {
			self::$userAgent = sprintf(
				'Mozilla/4.0 (compatible; %s; PHP/%s; %s; %s)',
				PHP_SAPI , PHP_VERSION , PHP_OS , $locale
			);
		}
	}

	$this->requestHeader = array();
	$this->requestParameter = array();
}

/**
 * @brief	Adiciona um campo de cabeçalho para ser enviado com a
 * requisição.
 * @param	string $name Nome do campo de cabeçalho.
 * @param	string $value Valor do campo de cabeçalho.
 * @param	boolean $override Indica se o campo deverá ser sobrescrito caso
 * 			já tenha sido definido.
 * @throws	InvalidArgumentException Se o nome ou o valor do campo não forem
 * 			valores scalar.
 */
public function addHeader( $name , $value , $override = true ) {
	if ( is_scalar( $name ) && is_scalar( $value ) ) {
		$key = strtolower( $name );

		if ( $override === true || !isset( $this->requestHeader[ $key ] ) ) {
			$this->requestHeader[ $key ] = array(
				'name' => $name , 'value' => $value
			);

			return true;
		}

		return false;
	} else {
		throw new InvalidArgumentException(
			'$name e $value precisam ser strings.'
		);
	}
}

/**
 * @brief	Fecha a conexão.
 * @throws	BadMethodCallException Se não houver uma conexão inicializada.
 */
public function close() {
	$this->initialized = false;
}

/**
 * @brief	Executa a requisição
 * @details	Executa a requisição HTTP em um caminho utilizando um método
 * 			específico.
 * @param	string $path Caminho da requisição.
 * @param	string $method Método da requisição.
 * @return	HTTPResponse Resposta HTTP.
 * @throws	BadMethodCallException Se não houver uma conexão inicializada ou
 * 			se o objeto de requisição não for válido.
 */
public function execute( $path = '/' , $method = HTTPRequest::GET ) {
	$request = $this->newRequest();

	if ( $request instanceof HTTPRequest ) {
		$host = $this->getHost();
		$accept = '*/*';
		$userAgent = self::$userAgent;

		if ( isset( $this->requestHeader[ 'Host' ] ) ) {
			$host = $this->requestHeader[ 'host' ][ 'value' ];

			unset( $this->requestHeader[ 'host' ] );
		}

		if ( isset( $this->requestHeader[ 'accept' ] ) ) {
			$accept = $this->requestHeader[ 'accept' ][ 'value' ];

			unset( $this->requestHeader[ 'accept' ] );
		}

		if ( isset( $this->requestHeader[ 'user-agent' ] ) ) {
			$userAgent = $this->requestHeader[ 'user-agent' ][ 'value' ];

			unset( $this->requestHeader[ 'user-agent' ] );
		}

		$request->addRequestHeader( 'Host' , $host );
		$request->addRequestHeader( 'Accept' , $accept );
		$request->addRequestHeader( 'User-Agent' , $userAgent );

		if ( $this->httpAuthenticator != null ) {
			$request->authenticate( $this->httpAuthenticator );
		}

		foreach ( $this->requestHeader as $header ) {
			$request->addRequestHeader(
				$header[ 'name' ] , $header[ 'value' ]
			);
		}

		$cookieManager = $this->getCookieManager();

		if ( $cookieManager != null ) {
			$cookies = $cookieManager->getCookie(
				$this->getHostName() , $this->isSecure() , $path
			);

			if ( isset( $this->requestHeader[ 'cookie' ] ) ) {
				$buffer  = $this->requestHeader[ 'cookie' ][ 'value' ];
				$buffer .= '; ' . $cookies;
			} else {
				$buffer = $cookies;
			}

			$request->addRequestHeader( 'Cookie' , $buffer );
		}

		foreach ( $this->requestParameter as $name => $value ) {
			$request->setParameter( $name , $value );
		}

		$request->setRequestBody( $this->requestBody );

		if ( $path == null || !is_string( $path ) || empty( $path ) ) {
			$path = '/';
		} else if ( substr( $path , 0 , 1 ) != '/' ) {
			$path = '/' . $path;
		}

		if ( $this->timeout != null ) {
			$request->setTimeout( $this->timeout );
		}

		if ( $this->connectionTimeout != null ) {
			$request->setConnectionTimeout( $this->connectionTimeout );
		}

		$request->open( $this );
		$request->execute( $path , $method );

		return $request->getResponse();
	} else {
		throw new BadMethodCallException( 'Objeto de requisição inválido.' );
	}
}

/**
 * @brief	Recupera o timeout de conexão.
 * @return	integer
 */
public function getConnectionTimeout() {
	return $this->connectionTimeout;
}

/**
 * @brief	Recupera o gerenciador de Cookies.
 * @return	CookieManager
 */
public function getCookieManager() {
	return $this->cookieManager;
}

/**
 * @brief	Recupera o host da conexão.
 * @return	string
 * @throws	BadMethodCallException Se a conexão não tiver
 * sido inicializada.
 */
public function getHost() {
	if ( $this->initialized ) {
		$hostname = $this->getHostName();

		if ( ( $this->secure && $this->port != HTTPConnection::HTTPS_PORT )
		|| ( !$this->secure && $this->port != HTTPConnection::HTTP_PORT ) ) {
			return $hostname . ':' . $this->port;
		} else {
			return $hostname;
		}
	} else {
		throw new BadMethodCallException( 'Conexão não inicializada' );
	}
}

/**
 * @brief	Recupera o nome do host.
 * @return	string
 * @throws	BadMethodCallException Se não houver uma conexão
 * inicializada.
 */
public function getHostName() {
	if ( $this->initialized ) {
		return $this->hostname;
	} else {
		throw new BadMethodCallException( 'Conexão não inicializada' );
	}
}

/**
 * @brief	Recupera a porta que será utilizada na conexão.
 * @return	integer
 * @throws	BadMethodCallException Se não houver uma conexão
 * inicializada.
 */
public function getPort() {
	if ( $this->initialized ) {
		return $this->port;
	} else {
		throw new BadMethodCallException( 'Conexão não inicializada' );
	}
}

/**
 * @brief	Recupera o timeout.
 * @return	integer
 */
public function getTimeout() {
	return $this->timeout;
}

/**
 * @brief	Recupera a URI que será utilizada na conexão.
 * @return	string
 * @throws	BadMethodCallException Se não houver uma conexão
 * inicializada.
 */
public function getURI() {
	if ( $this->initialized ) {
		return sprintf(
			'%s://%s' , $this->isSecure() ? 'https' : 'http',
			$this->getHost()
		);
	} else {
		throw new BadMethodCallException( 'Conexão não inicializada' );
	}
}

/**
 * @brief	Inicializa a conexão HTTP.
 * @param	string $hostname Servidor que receberá a requisição.
 * @param	boolean $secure Indica se a conexão será segura (https).
 * @param	integer $port Porta da requisição.
 * @param	integer $connectionTimeout Timeout de conexão em segundos.
 * @param	integer $timeout Timeout de espera em segundos.
 */
public function initialize( $hostname , $secure = false,
							$port = HTTPConnection::HTTP_PORT,
							$connectionTimeout = 0,
							$timeout = 0 ) {

	if ( $this->initialized ) {
		$this->close();
	}

	$this->initialized = true;
	$this->hostname = $hostname;
	$this->secure = $secure === true;

	if ( func_num_args() == 2 ) {
		if ( $this->secure ) {
			$this->port = HTTPConnection::HTTPS_PORT;
		} else {
			$this->port = HTTPConnection::HTTP_PORT;
		}
	} else {
		$this->port = (int) $port;
	}

	$this->connectionTimeout = (int) $connectionTimeout;
	$this->timeout = (int) $timeout;
}

/**
 * @brief	Verifica se é uma conexão segura.
 * @return	boolean
 */
public function isSecure() {
	return $this->secure === true;
}

/**
 * @brief	Cria uma instância de um objeto de requisição HTTP.
 * @return	HTTPRequest
 */
public function newRequest() {
	return new CURL();
}

/**
 * @brief	Define um autenticador HTTP.
 * @param	HTTPAuthenticator $httpAuthenticator
 */
public function setAuthenticator( HTTPAuthenticator $httpAuthenticator ) {
	$this->httpAuthenticator = $httpAuthenticator;
}

/**
 * @brief	Define o timeout de conexão.
 * @param	integer $connectionTimeout
 * @throws	InvalidArgumentException Se $connectionTimeout não for um inteiro.
 */
public function setConnectionTimeout( $connectionTimeout ) {
	if ( is_integer( $connectionTimeout ) ) {
		$this->connectionTimeout = $connectionTimeout;
	} else {
		throw new InvalidArgumentException(
			'$connectionTimeout precisa ser o tempo em segundos.'
		);
	}
}

/**
 * @brief	Define um gerenciador de cookies para essa conexão.
 * @param	CookieManager $cookieManager
 */
public function setCookieManager( CookieManager $cookieManager ) {
	$this->cookieManager = $cookieManager;
}

/**
 * @brief	Define um parâmetro
 * @details	Define um parâmetro que será enviado com a requisição, um
 * 			parâmetro é um par nome-valor que será enviado como uma query
 * 			string (<b>ex:</b> <i>?name=value</i>).
 * @param	string $name Nome do parâmetro.
 * @param	string $value Valor do parâmetro.
 * @throws	InvalidArgumentException Se o nome ou o valor
 * 			do campo não forem valores scalar.
 */
public function setParam( $name , $value = null ) {
	if ( is_scalar( $name ) && ( is_scalar( $value ) || is_null( $value ) ) ) {
		$this->requestParameter[ $name ] = $value;
	} else {
		throw new InvalidArgumentException(
			'$name e $value precisam ser strings.'
		);
	}
}

/**
 * @brief	Define o corpo da requisição.
 * @param	string $requestBody
 */
public function setRequestBody( $requestBody ) {
	$this->requestBody = $requestBody;
}

/**
 * @brief	Define o timeout.
 * @param	integer $timeout
 * @throws	InvalidArgumentException Se $timeout não for um inteiro.
 */
public function setTimeout( $timeout ) {
	if ( is_integer( $timeout ) ) {
		$this->timeout = $timeout;
	} else {
		throw new InvalidArgumentException(
			'$timeout precisa ser o tempo em segundos.'
		);
	}
}
}

 

com/imasters/http/HTTPCookieManager.php

<?php
/**
* @brief	Protocolo HTTP
* @details	Classes e interfaces relacionadas com o protocolo HTTP
* @package com.imasters.http
*/

require_once 'com/imasters/http/CookieManager.php';

/**
* @brief	Gerenciador de Cookies HTTP
* @details	Implementação da interface CookieManager para criação de um
* 			gerenciador de cookies que armazena os cookies em um arquivo em
* 			disco.
*/
class HTTPCookieManager implements CookieManager {
/**
 * @var	string
 */
private $cookieFile;

/**
 * @var	array
 */
private $cookies = array();

/**
 * @brief	Constroi o gerenciador de cookies que grava as informações em um
 * 			arquivo.
 * @param	string $dirname Diretório onde os cookies serão gravados, caso
 * 			não informado o diretório temporário do sistema será utilizado.
 */
public function __construct( $dirname = null ) {
	if ( $dirname == null ) {
		$dirname = sys_get_temp_dir();
	}

	if ( is_readable( $dirname ) && is_writable( $dirname ) ) {
		$cookieFile = realpath( $dirname ) . '/cookie.jar';

		if ( !is_file( $cookieFile ) ) {
			touch( $cookieFile );
		} else {
			$cookieManager = unserialize( file_get_contents( $cookieFile ) );

			if ( $cookieManager instanceof HTTPCookieManager ) {
				$this->cookies = $cookieManager->cookies;
			}
		}

		$this->cookieFile = $cookieFile;
	} else {
		$msg  = 'O diretório ' . $dirname;
		$msg .= ' precisa ter permissões de leitura e gravação.';

		throw new RuntimeException( $msg );
	}
}

/**
 * @brief	Destroi o objeto e salva os cookies armazenados
 */
public function __destruct() {
	if ( $this->cookieFile != null ) {
		file_put_contents( $this->cookieFile , serialize( $this ) );
	}
}

/**
 * @see CookieManager::addCookie()
 */
public function addCookie( Cookie $cookie ) {
	$cookieDomain = $cookie->getDomain();

	if ( !isset( $this->cookies[ $cookieDomain ] ) ) {
		$this->cookies[ $cookieDomain ] = array();
	}

	$this->cookies[ $cookieDomain ][] = $cookie;
}

/**
 * @see CookieManager::getCookie()
 */
public function getCookie( $domain , $secure , $path ) {
	return implode(
		'; ' , $this->getCookieArray( $domain , $secure , $path )
	);
}

private function getCookieArray( $domain , $secure , $path ) {
	$cookies = array();
	$secure = $secure === true;

	if ( isset( $this->cookies[ $domain ] ) ) {
		foreach ( $this->cookies[ $domain ] as $cookie ) {
			if (
				$cookie->isSecure() == $secure &&
				$cookie->getPath() == $path ) {

				$cookies[] = $cookie;
			}
		}
	}

	return $cookies;
}

/**
 * @see CookieManager::getCookieIterator()
 */
public function getCookieIterator( $domain , $secure , $path ) {
	return new ArrayIterator(
		$this->getCookieArray( $domain , $secure , $path )
	);
}

/**
 * @see CookieManager::setCookie()
 */
public function setCookie( $setCookie , $domain = null ) {
	if ( is_array( $setCookie ) ) {
		foreach ( $setCookie as $setCookieItem ) {
			$this->setCookie( $setCookieItem );
		}
	} else {
		$matches = array();

		if ( preg_match(
			'/(?<name>[^\=]+)\=(?<value>[^;]+)'.
			'(; expires=(?<expires>[^;]+))?'.
			'(; path=(?<path>[^;]+))?'.
			'(; domain=(?<domain>[^;]+))?'.
			'(; (?<secure>secure))?'.
			'(; (?<httponly>httponly))?/' , $setCookie , $matches ) ){

			$cookieName = null;
			$cookieValue = null;
			$cookieExpires = INF;
			$cookiePath = '/';
			$cookieDomain = $domain;
			$cookieSecure = false;

			foreach ( $matches as $key => $value ) {
				if ( !empty( $value ) ) {
					switch ( $key ) {
						case 'name' :
							$cookieName = $value;
							break;
						case 'value' :
							$cookieValue = $value;
							break;
						case 'expires' :
							$cookieExpires = strtotime( $value );
							break;
						case 'path' :
							$cookiePath = $value;
							break;
						case 'domain' :
							$cookieDomain = $value;
							break;
						case 'secure' :
							$cookieSecure = true;
							break;
					}
				}
			}

			if ( !isset( $this->cookies[ $cookieDomain ] ) ) {
				$this->cookies[ $cookieDomain ] = array();
			}

			$this->cookies[ $cookieDomain ][] = new Cookie(
				$cookieName,
				$cookieValue,
				$cookieDomain,
				$cookieExpires,
				$cookiePath,
				$cookieSecure
			);
		}
	}
}

/**
 * @see Serializable::serialize()
 */
public function serialize() {
	return serialize( $this->cookies );
}

/**
 * @see Serializable::unserialize()
 */
public function unserialize( $serialized ) {
	$cookies = unserialize( $serialized );

	if ( is_array( $cookies ) ) {
		$now = time();

		foreach ( $cookies as $domain => $domainCookies ) {
			foreach ( $domainCookies as $cookie ) {
				if ( $cookie instanceof Cookie ) {
					if ( $cookie->getExpires() > $now ) {
						if ( !isset( $this->cookies[ $domain ] ) ) {
							$this->cookies[ $domain ] = array();
						}

						$this->cookies[ $domain ][] = $cookie;
					}
				}
			}
		}
	}
}
}

 

com/imasters/http/HTTPRequest.php

<?php
/**
* @brief	Protocolo HTTP
* @details	Classes e interfaces relacionadas com o protocolo HTTP
* @package com.imasters.http
*/

require_once 'com/imasters/http/HTTPConnection.php';
require_once 'com/imasters/http/HTTPAuthenticator.php';

/**
* @brief	Requisição HTTP
* @details	Interface para definição de um objeto que fará uma requisição HTTP.
*/
interface HTTPRequest {
/**
 * Método DELETE
 */
const DELETE = 'DELETE';

/**
 * Método GET
 */
const GET = 'GET';

/**
 * Método HEAD
 */
const HEAD = 'HEAD';

/**
 * Método OPTIONS
 */
const OPTIONS = 'OPTIONS';

/**
 * Método POST
 */
const POST = 'POST';

/**
 * Método PUT
 */
const PUT = 'PUT';

/**
 * Método TRACE
 */
const TRACE = 'TRACE';

/**
 * @brief	Adiciona um campo de cabeçalho para ser enviado com a requisição.
 * @param	string $name Nome do campo de cabeçalho.
 * @param	string $value Valor do campo de cabeçalho.
 * @param	boolean $override Indica se o campo deverá ser sobrescrito caso
 * 			já tenha sido definido.
 * @throws	InvalidArgumentException Se o nome ou o valor do campo não forem
 * 			valores scalar.
 */
public function addRequestHeader( $name , $value , $override = true );

/**
 * @brief	Autentica uma requisição HTTP.
 * @param	HTTPAuthenticator $authenticator
 */
public function authenticate( HTTPAuthenticator $authenticator );

/**
 * @brief	Fecha a requisição.
 */
public function close();

/**
 * @brief	Executa a requisição HTTP
 * @details	Executa a requisição HTTP em um caminho utilizando um método
 * 			específico.
 * @param	string $method Método da requisição.
 * @param	string $path Alvo da requisição.
 * @return	string Resposta HTTP.
 * @throws	BadMethodCallException Se não houver uma conexão inicializada.
 */
public function execute( $path = '/' , $method = HTTPRequest::GET );

/**
 * @brief	Recupera a resposta da requisição.
 * @return	HTTPResponse
 */
public function getResponse();

/**
 * @brief	Abre a requisição.
 * @param	HTTPConnection $httpConnection Conexão HTTP relacionada com essa
 * 			requisição
 */
public function open( HTTPConnection $httpConnection );

/**
 * @brief	Define um parâmetro
 * @details	Define um parâmetro que será enviado com a requisição, um
 * 			parâmetro é um par nome-valor que será enviado como uma query
 * 			string (<b>ex:</b> <i>?name=value</i>).
 * @param	string $name Nome do parâmetro.
 * @param	string $value Valor do parâmetro.
 * @throws	InvalidArgumentException Se o nome ou o valor
 * do campo não forem valores scalar.
 */
public function setParameter( $name , $value );

/**
 * @brief	Corpo da requisição HTTP.
 * @param	string $contentBody
 */
public function setRequestBody( $requestBody );
}

 

com/imasters/http/HTTPResponse.php

<?php
/**
* @brief	Protocolo HTTP
* @details	Classes e interfaces relacionadas com o protocolo HTTP
* @package com.imasters.http
*/

require_once 'com/imasters/http/CookieManager.php';

/**
* @brief	Resposta HTTP
* @details	Implementação de um objeto representa uma resposta HTTP.
*/
class HTTPResponse {
/**
 * @var	array
 */
private $responseHeader = array();

/**
 * @var	string
 */
private $responseBody;

/**
 * @var	integer
 */
private $statusCode;

/**
 * @var	string
 */
private $statusMessage;

/**
 * @brief	Recupera o corpo da resposta HTTP.
 * @return	string
 */
public function getContent() {
	return $this->responseBody;
}

/**
 * @brief	Recupera o tamanho do corpo da resposta.
 * @return	integer
 */
public function getContentLength() {
	return $this->getHeaderInt( 'Content-Length' );
}

/**
 * @brief	Recupera o tipo de conteúdo da resposta.
 * @return	string
 */
public function getContentType() {
	return $this->getHeader( 'Content-Type' );
}

/**
 * @brief	Recupera o código de status da resposta do servidor.
 * @return	integer
 */
public function getStatusCode() {
	return $this->statusCode;
}

/**
 * @brief	Recupera a mensagem de status da resposta do servidor.
 * @return	string
 */
public function getStatusMessage() {
	return $this->statusMessage;
}

/**
 * @brief	Verifica se existe um cabeçalho de resposta HTTP.
 * @param	string $name Nome do cabeçalho
 * @return	boolean
 */
public function hasResponseHeader( $name ) {
	return isset( $this->responseHeader[ strtolower( $name ) ] );
}

/**
 * @brief	Recupera o valor um campo de cabeçalho da resposta HTTP.
 * @param	string $name Nome do campo de cabeçalho.
 * @return	string O valor do campo ou NULL se não estiver
 * existir.
 */
public function getHeader( $name ) {
	$key = strtolower( $name );

	if ( isset( $this->responseHeader[ $key ] ) ) {
		if ( !isset( $this->responseHeader[ $key ][ 'name' ] ) &&
			is_array( $this->responseHeader[ $key ] ) ) {

			$values = array();

			foreach ( $this->responseHeader[ $key ] as $header ) {
				$values[] = $header[ 'value' ];
			}

			return $values;
		} else {
			return $this->responseHeader[ $key ][ 'value' ];
		}
	}

	return null;
}

/**
 * @brief	Recupera um valor como inteiro de um campo de cabeçalho da
 * 			resposta HTTP.
 * @param	string $name Nome do campo de cabeçalho.
 * @return	integer
 */
public function getHeaderInt( $name ) {
	return (int) $this->getHeader( $name );
}

/**
 * @brief	Recupera um valor como unix timestamp de um campo de cabeçalho
 *			da resposta HTTP.
 * @param	string $name Nome do campo de cabeçalho.
 * @return	integer UNIX Timestamp ou NULL se não estiver definido.
 */
public function getHeaderDate( $name ) {
	$date = $this->getHeader( $name );

	if ( !is_null( $date ) && !empty( $date ) ) {
		return strtotime( $date );
	}
}

/**
 * @brief	Define a resposta da requisição HTTP.
 * @param	string $response Toda a resposta da requisição
 */
public function setRawResponse( $response,
								CookieManager $cookieManager = null ) {

	$parts = explode( "\r\n\r\n" , $response );

	if ( count( $parts ) == 2 ) {
		$matches = array();
		$this->responseBody = $parts[ 1 ];

		if ( preg_match_all(
			'/(HTTP\/[1-9]\.[0-9]\s+'.
			'(?<statusCode>\d+)\s+(?<statusMessage>.*)|'.
			"(?<headerName>[^:]+)\\s*:\\s*(?<headerValue>.*))\r\n/m",
			$parts[ 0 ] , $matches ) ) {

			foreach ( $matches[ 'statusCode' ] as $o => $match ) {
				if ( !empty( $match ) ) {
					$this->statusCode = (int) $match;
					$this->statusMessage = $matches[ 'statusMessage' ][ $o ];
					break;
				}
			}

			foreach ( $matches[ 'headerName' ] as $o => $name ) {
				if ( !empty( $name ) ) {
					$k = strtolower( $name );
					$header = array(
						'name'	=> $name,
						'value'	=> $matches[ 'headerValue' ][ $o ]
					);

					if ( isset( $this->responseHeader[ $k ] ) ) {
						if ( isset( $this->responseHeader[ $k ][ 'name' ] ) ) {
							$this->responseHeader[ $k ] = array(
								$this->responseHeader[ $k ]
							);
						}

						$this->responseHeader[ $k ][] = $header;

					} else {
						$this->responseHeader[ $k ] = $header;
					}
				}
			}
		}
	} else {
		$this->responseBody = $response;
	}
}
}

 

 

 

Agora misturamos tudo isso ai e preparamos um exemplo de uso:

 

<?php
require_once 'com/imasters/google/maps/GoogleMaps.php';
require_once 'com/imasters/tsp/City.php';
require_once 'com/imasters/tsp/TravellingSalesmanProblem.php';

/**
* Objeto que representa o problema do caixeiro viajante
* @var	TravellingSalesmanProblem
*/
$tsp = new TravellingSalesmanProblem();

/**
* Cidades por onde o caixeiro viajante deverá passar
* @var	array
*/
$cities = array(
'Franca - SP, Brasil',
'São Paulo - SP, Brasil',
'Vitória - ES, Brasil',
'Ribeirão Preto - SP, Brasil',
'Guararema - SP, Brasil',
'Batatais - SP, Brasil'
);

/**
* Vamos utilizar o Google Maps para recuperar as coordenadas geográficas
* (latitude e longitude) de cada uma das cidades.
* @var	GoogleMaps
*/
$maps = new GoogleMaps();

/**
* Percorremos a matriz de cidades e buscamos no Google Maps a latitude e
* longitude para criar os objetos City.
*/
foreach ( $cities as $city ) {
/**
 * Faz a busca pelo nome da cidade
 * @var	stdClass
 */
$json = $maps->search( $city );

/**
 * Se houver resultados, adicionamos a cidade na lista de cidades que o
 * caixeiro deverá visitar.
 */
if ( count( $json->results ) == 1 ) {
	$result = array_shift( $json->results );

	$tsp->addCity( new City(
		$result->formatted_address,
		$result->geometry->location->lat,
		$result->geometry->location->lng
	) );
} else {
	throw new RuntimeException(
		'Não foi possível localizar cidade ' . $city
	);
}
}

/**
* Nesse instante já fizemos a busca pela localização geográfica de cada uma
* das cidades e vamos marcar o tempo que o algorítimo levará para resolver
* o problema.
* @var	float
*/
$start = microtime( true );

/**
* Utilizamos o algorítimo do vizinho mais próximo para identificar a rota que
* o caixeiro deverá percorrer.
*/
foreach ( $g = $tsp->nearestNeighbourAlgorithm() as $l ) {
$a = $l->getPointA();
$b = $l->getPointB();
$d = $l->distance();

printf( "%.02fKm de '%s' até '%s'\n" , $d , $a->getName() , $b->getName() );
}

printf(
"\nExistem %d pontos e %d arestas no grafo\n",
$g->countPoints() , $g->count()
);

printf( "Levamos %fs para resolver o problema.\n",
microtime( true ) - $start
);

 

A saída deverá ser alguma coisa parecida com:

 

42,56Km de 'Franca - São Paulo, Brasil' até 'Batatais - São Paulo, Brasil'

40,00Km de 'Batatais - São Paulo, Brasil' até 'Ribeirão Preto - São Paulo, Brasil'

290,43Km de 'Ribeirão Preto - São Paulo, Brasil' até 'São Paulo, Brasil'

62,84Km de 'São Paulo, Brasil' até 'Guararema - São Paulo, Brasil'

684,98Km de 'Guararema - São Paulo, Brasil' até 'Vitória - ES, Brasil'

 

Existem 6 pontos e 5 arestas no grafo

Levamos 0,007021ms para resolver o problema.

 

;)

 

PS: Se quiser continuar brincando disso, podemos implementar outros algorítimos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

E mais uma vez o João dá uma aula ajudando a galera do fórum! Valeu João! Parabéns cara! #orgulho

Compartilhar este post


Link para o post
Compartilhar em outros sites

E mais uma vez o João dá uma aula ajudando a galera do fórum! Valeu João! Parabéns cara! #orgulho

 

Concordo em tudo

 

#orgulho³

Compartilhar este post


Link para o post
Compartilhar em outros sites

E mais uma vez o João dá uma aula ajudando a galera do fórum! Valeu João! Parabéns cara! #orgulho

 

 

Concordo em tudo

 

#orgulho³

 

kkkkkkkkk

 

Valeu pessoal.

 

:lol:

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.