Don't like ads? PRO users don't see any ads ;-)

PHP: Browser.class v0.7

By: Elieder on Jun 2nd, 2012  |  syntax: PHP  |  size: 28.93 KB  |  hits: 40  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. <?php
  2.  
  3. /**
  4.  * Browser.class Manipula requisições de uma maneira mais simples e fácil.
  5.  * @author Elieder Damasceno Sousa
  6.  * @version 0.7
  7.  * facebook.com/eliedersousa
  8.  * eliederdamasceno@gmail.com
  9.  * @eliedersousa
  10.  * ********************************
  11.  * UPDATES:
  12.         v0.7:
  13.         - Método parsePage(), para parsear formulários e scripts na página.
  14.         - Método preparePostData() reformulado para usar a função http_build_query()
  15.         - Método getProxyList(), que retorna uma lista com proxys
  16.         - Adicionado novos USERAGENTS (Firefox12, Chrome19, IE6/7, TESTE)
  17.         - Adicionado controle de erros em cada método, para impedir o uso incorreto dos mesmos.
  18.         - Default headers do Firefox para serem enviados em cada requisição
  19.         - Adicionado controles if(isset($data[""])) para cada CURLOPT que necessite, afim de impedir que as requisições enviem parâmetros default.
  20.         - Adicionado suporte inicial a proxys na função cURL.
  21.         - Corrigido o bug de redirecionamento, na função parseHeaders.
  22.        
  23.         v0.5:
  24.         - Adicionado os métodos
  25.        
  26.         v0.4:
  27.         - Adicionado o método printAll(), para printar todas as variáveis.
  28.        
  29.         v0.3:
  30.         - Método openMultiplePages() para multiplas requisições; além das constantes de requisição.
  31.  */
  32. class Browser {
  33.    
  34.     /**
  35.      * Guarda a history de páginas requisitadas, com [url, time, post]
  36.      * @var array $history
  37.      */
  38.     public $history;
  39.    
  40.     /**
  41.      * Guarda todos os headers setados pelas requisições. Se $followLocationManual estiver habilitado,
  42.      * os redirecionamentos são tratados em índices separados; caso contrário os headers de cada página redirecionada
  43.      * ficam em um mesmo índice.
  44.      * @var array $headers
  45.      */
  46.     public $headers;
  47.    
  48.     /**
  49.      * Guarda os cookies setados pelas requisições.
  50.      * @var array $cookies
  51.      */
  52.     public $cookies;
  53.    
  54.     // Variável auxiliar, para setar qual foi o último domínio que pegamos um cookie.
  55.     // Isso é extremamente importante para que eu possa passar de maneira inteligente o cookie para
  56.     // O domínio correto; embora esteja errado, pois o domínio tem que ser determinado com base
  57.     // Na URL passada, e não no último cookie capturado (pois eu posso requisitar 2 páginas de domínios
  58.     // Diferentes em sequência
  59.     // TODO: Manipular o domínio com base na URL, e não no cookie.
  60.     /**
  61.      * Guarda o domínio atual que estamos trabalhando (da requisição anterior).
  62.      * @var string $domain
  63.      */
  64.     private $domain = "";
  65.    
  66.     /**
  67.      * O USERAGENT que será usado em todas as requisições.
  68.      * @var string $useragent
  69.      */
  70.     private $useragent = self::USERAGENT_FIREFOX12;
  71.    
  72.     /**
  73.      * Controle de headers, e history
  74.      * @var int $historyCount
  75.      */
  76.     private $historyCount = 0;
  77.    
  78.     /**
  79.      * Manipula a forma como os redirecionamentos são feitos nas requisições. Se a variável estiver como 0, os
  80.      * redirecionamentos serão feitos com CURLOPT_FOLLOWLOCATION; caso esteja com o valor 1, serão feitos manualmente.
  81.      * @var int $followLocationManual
  82.      */
  83.     private $followLocationManual = 0;
  84.    
  85.     private $disableFollow = 0;
  86.    
  87.     /**
  88.      * Valor máximo de segundos que uma requisição pode esperar antes de ser abortada.
  89.      * @var int $curl_timeout
  90.      */
  91.     private $curl_timeout = 80;
  92.    
  93.     /**
  94.      * Valor em microsegundos que o loop de requisições múltiplas deve esperar para verificar se todas acabaram.
  95.      * @var int $curl_multi_timeout
  96.      */
  97.     private $curl_multi_timeout = 100;
  98.    
  99.     /**
  100.      * Quantidade máxima de redirecionamentos automaticos que a classe seguirá.
  101.      * Evita cair em loop caso o redirect venha em uma mensagem codificada por GZIP.
  102.      */
  103.     private $curl_maxredirects = 4;
  104.    
  105.     // Constantes de manipulação de múltiplas requisições
  106.     const CURLMULTI_URLS = 1;
  107.     const CURLMULTI_POST = 2;
  108.     const CURLMULTI_AUTH = 3;
  109.    
  110.     // TODO: Fazer as constantes de USERAGENT!
  111.     // Aqui, todos useragents estão para o Windows.
  112.     const USERAGENT_IE6 = "Mozilla/4.0 (compatible; MSIE 6.1; Windows XP)";
  113.     const USERAGENT_IE7 = "Mozilla/4.0(compatible; MSIE 7.0b; Windows NT 6.0)";
  114.     const USERAGENT_FIREFOX8 = "Mozilla/5.0 (Windows NT 6.1; rv:8.0) Gecko/20100101 Firefox/8.0!";
  115.     const USERAGENT_FIREFOX12 = "Mozilla/5.0 (Windows NT 6.1; rv:12.0) Gecko/20100101 Firefox/12.0";
  116.     const USERAGENT_CHROME190 = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.52 Safari/536.5";
  117.     const USERAGENT_TESTE = "# USERAGENT PARA TESTES DA BROWSER CLASS #";
  118.        
  119.     /**
  120.      * Instancia a classe Browser.
  121.      * @return void
  122.      */
  123.     public function __construct() {
  124.         $this->history = array();
  125.         $this->headers = array();
  126.         $this->cookies = array();
  127.        
  128.         // Se o host não der suporte a FOLLOWLOCATION
  129.         if(ini_get("safe_mode")) {
  130.             $this->followLocationManual = 1;
  131.         } else {
  132.             $this->followLocationManual = 0;
  133.         }
  134.     }
  135.    
  136.     /**
  137.      * Constrói os headers na variável $headers; separando por índices.
  138.      * Útil para manter CURLOPT_HEADER sempre desligado!
  139.      * Retorna o tamanho dos headers em bytes (strlen($headers))
  140.      * @return int
  141.      */
  142.     private function HeaderListener($req, $header) {
  143.         $arrayheader = array();
  144.         preg_match('/.*/', $header, $arrayheader);
  145.         if(strlen(trim($arrayheader[0])) > 0) array_push($this->headers[$this->historyCount], $arrayheader[0]);
  146.         return strlen($header);
  147.     }
  148.    
  149.     /**
  150.      * Abre a página requisitada, guardando os dados na array $history, e separando informações.
  151.      * Lembre-se sempre: $data tem que ser uma array!
  152.      * array("post"=>post....);
  153.      * @param string $url
  154.      * @param array $data
  155.      * @return string | requisição_curl
  156.      */
  157.     public function open($url, $data=null) {
  158.         if(!isset($url) || !is_string($url)) {
  159.             throw new Exception("Browser::open - O primeiro parâmetro requer uma string.");
  160.             die();
  161.         }
  162.         if(isset($data) && !is_array($data)) {
  163.             throw new Exception("Browser::open - O segundo parâmetro, quando passado, requer uma string.");
  164.             die();
  165.         }
  166.         // TODO: filtrar a URL, para que ela não aceite &amp; e etcs...
  167.         // $url = html_entity_decode($url);
  168.        
  169.         if(isset($data["post"]) && is_array($data["post"])) $data["post"] = $this->preparePostData($data["post"]);
  170.         // TODO: open() -> Validação da $url passada.
  171.         return $this->cURL($url, $data);
  172.     }
  173.    
  174.     // TODO: cURL() -> Arrumar a descrição dos parâmetros dessa função; especialmente o parâmetro $data que não pede só string, mas também uma array.
  175.     /**
  176.      * Faz as requisições, grava elas na array history, e inicia a análise dos headers.
  177.      * @param string $url A URL que será requisitada.
  178.      * @param string $data Uma string com dados em formato nome=valor&nome2=valor2&....,
  179.      * para ser passada em requisições POST.
  180.      * @return string | requisição_curl
  181.      */
  182.     private function cURL($url, $data=null, &$multiplerequest=null) {
  183.                 if(!isset($url) || !is_string($url)) {
  184.                         throw new Exception("Browser::cURL - O primeiro parâmetro requer uma string.");
  185.                         die();
  186.                 }
  187.                 if(isset($data) && !is_array($data)) {
  188.                         throw new Exception("Browser::cURL - O segundo parâmetro, quando passado, requer uma array.");
  189.                         die();
  190.                 }
  191.         // Possiveis valores para $data:
  192.         // auth, post, referer
  193.         // post DEVE ser uma string de nome=valor&nome2=
  194.         // auth deve ser user:pass
  195.        
  196.         array_push($this->history, array("url" => $url, "time" => time(), "data" => $data));
  197.         $this->headers[$this->historyCount] = array();
  198.        
  199.         // TODO: cURL() -> Preparar headers aqui.
  200.         // TODO: cURL() -> Preparar referer aqui.
  201.         $rq = @curl_init($url);
  202.        
  203.         // PREPARANDO OS COOKIES
  204.         // TODO: Está errado: o certo seria pegar o domínio com base na url passada
  205.         $domainCookie = isset($this->cookies[$this->domain]) ? implode(";", $this->cookies[$this->domain]) : null;
  206.        
  207.         if(!$this->followLocationManual) {
  208.             try {
  209.                 curl_setopt($rq, CURLOPT_FOLLOWLOCATION, 1);
  210.             } catch(Exception $e) {};
  211.         }
  212.        
  213.         // Referer da página sempre será a página anterior, OU a própria página, se followLocationManual for 1
  214.         // Isso por que alguns logins pedem que o redirecionamento tenha como referer a página anterior.
  215.         // TODO: Criar um parâmetro na função openPage() para que essa função cURL trate o referer da forma que
  216.         // o usuário quiser, por exemplo: $isLoginURL (um parâmetro lá na openPage() que diz se a URL chamada é uma
  217.         // página de login, e caso seja, o referer usado seja da página anterior)...
  218.         if($this->followLocationManual) {
  219.             $referer = isset($this->history[$this->historyCount-1]) ? $this->history[$this->historyCount-1]["url"] : $url;
  220.         } else {
  221.             $referer = $url;
  222.         }
  223.                
  224.                 // Firefox 12 default headers.
  225.                 $default_header = array(
  226.                         "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
  227.                         "Accept-Language: pt-br,pt;q=0.8,en-us;q=0.5,en;q=0.3",
  228.                         "Accept-Encoding: gzip, deflate",
  229.                         "Connection: keep-alive");
  230.    
  231.                 // GENERAL AREA
  232.                 // #CURLOPT_ENCODING com o valor 'NULL' envia "Accept-Encoding: deflate, gzip", o inverso de "gzip, deflate" kkk
  233.                 if(isset($data["auth"])) curl_setopt($rq, CURLOPT_USERPWD, $data["auth"]);
  234.                 if(isset($data["encoder"])) curl_setopt($rq, CURLOPT_ENCODING, $data["encoder"]);
  235.                 if(isset($domainCookie)) curl_setopt($rq, CURLOPT_COOKIE, $domainCookie);
  236.                 if(!isset($data["ignore_referer"])) curl_setopt($rq, CURLOPT_REFERER, $referer);
  237.                
  238.         curl_setopt_array($rq, array(
  239.                         CURLOPT_HTTPHEADER => isset($data["header"]) ? $default_header.$data["header"] : $default_header,
  240.             CURLOPT_HEADER => 0, //(isset($multiplerequest) ? 1 : 0), (com essa linha comentada, multiplas requisições não terão header.
  241.             CURLOPT_TIMEOUT => $this->curl_timeout,
  242.             CURLOPT_MAXREDIRS => (isset($data["encoder"])) ? 1 : $this->curl_maxredirects, // Um valor seguro para impedir cair em loop.
  243.             CURLOPT_USERAGENT => $this->useragent,
  244.             // TODO: cURL() -> CURLOPT_SSL: Future Features.
  245.             CURLOPT_SSL_VERIFYHOST => 0,
  246.             CURLOPT_SSL_VERIFYPEER => 0,
  247.             CURLOPT_RETURNTRANSFER => 1,
  248.             CURLOPT_HEADERFUNCTION => array(&$this, 'HeaderListener'),
  249.         ));
  250.        
  251.                 // USING PROXY?
  252.                 if(isset($data["proxy"])) {
  253.                         // proxyauth = 'user:password';
  254.                         // curl_setopt($rq, CURLOPT_PROXYUSERPWD, $data["proxyauth"]);
  255.                         // curl_setopt($rq, CURLOPT_HTTPPROXYTUNNEL, 1);
  256.                         curl_setopt($rq, CURLOPT_PROXY, $data["proxy"]);
  257.                 }
  258.                
  259.         // POST AREA
  260.         if(isset($data["post"])) {
  261.             curl_setopt($rq, CURLOPT_POST, 1);
  262.             curl_setopt($rq, CURLOPT_POSTFIELDS, $data["post"]);          
  263.         }
  264.        
  265.         // MULTIPLE_REQUESTS E RETURN AREA
  266.         if(isset($multiplerequest)) {
  267.             curl_multi_add_handle($multiplerequest, $rq);
  268.             return $rq;
  269.         } else {
  270.             // RETURN AREA
  271.             // TODO: cURL() -> Fazer um parser para o resultado da requisição, para filtrar tags na página a fim de criar robôs.
  272.             // Por isso que aqui, não faço diretamente: return curl_exec($rq);
  273.             $result = curl_exec($rq);
  274.            
  275.             // Após a requisição vamos extrair todas as informações dos headers
  276.             $followURL = $this->parseHeaders($url);
  277.  
  278.             // Retorna a requisição, ou chama a função recursivamente caso tenha que dar followLocation.
  279.             if($this->followLocationManual) {
  280.                 if($followURL != null) return $this->cURL($followURL);
  281.             }
  282.             return $result;
  283.         }
  284.     }
  285.        
  286.         public function getProxyList() {
  287.                 $list = $this->open("http://www.ip-adress.com/proxy_list/");
  288.                 preg_match_all("/\>(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{2,5})/", $list, $list);
  289.                 return $list[1];
  290.         }
  291.    
  292.     public function openMultipleRequest($data, $tipo=Browser::CURLMULTI_URLS, $url=null) {
  293.         // Cria um handler múltiplo.
  294.         $mh = curl_multi_init();
  295.         $container = array();
  296.         $conteudo  = array();
  297.         $active    = array();
  298.        
  299.         if($tipo == Browser::CURLMULTI_URLS) {
  300.             // Manipula múltiplas requisições de urls diferentes.
  301.             // Isso aqui é só preparação; estamos apenas preparando as requisições (curl_init e curl_setopt's)
  302.             foreach($data["url"] as $url) {
  303.                 // Chama a função que faz a requisição, mandando como parâmetro o manipulador curl_multi.
  304.                 $container[] = $this->cURL($url, null, $mh);
  305.             }
  306.            
  307.             // Aaaand... heeere we gooo!
  308.             do {
  309.                 $n = curl_multi_exec($mh, $active);
  310.                 usleep($this->curl_multi_timeout);                
  311.             } while($active);
  312.            
  313.             foreach ($container as $w) {
  314.                 $conteudo[] = curl_multi_getcontent($w);
  315.                 curl_multi_remove_handle($mh, $w);
  316.                 curl_close($w);
  317.             }
  318.         } else if($tipo == Browser::CURLMULTI_POST) {
  319.             // $data é uma array de: {"auth"=>"autenticação", "post" => "post"}
  320.             // Ou seja, são vários índices de "posts";
  321.            
  322.             // Manipula múltiplas requisições a uma mesma URL, mas com POSTS diferentes
  323.             foreach ($data as $pieceofdata) {
  324.                 if(isset($pieceofdata["post"]) && is_array($pieceofdata["post"])) $pieceofdata["post"] = $this->preparePostData($pieceofdata["post"]);
  325.                
  326.                 // Chama a função que faz a requisição, mandando como parâmetro o manipulador curl_multi.
  327.                 $container[] = $this->cURL($url, $pieceofdata, $mh);
  328.             }
  329.            
  330.             // Aaaand... heeere we gooo! (again)
  331.             do {
  332.                 curl_multi_exec($mh, $active);
  333.                 usleep($this->curl_multi_timeout);
  334.             } while($active > 0);
  335.            
  336.             foreach ($container as $w) {
  337.                 $conteudo[] = curl_multi_getcontent($w);
  338.                 curl_multi_remove_handle($mh, $w);
  339.                 curl_close($w);
  340.             }
  341.         } else {
  342.             throw new Exception("Browser::openMultiplePage() -> something goes wrong!");
  343.             die();
  344.         }
  345.         return $conteudo;
  346.     }
  347.    
  348.     /**
  349.      * Prepara a array passada como parâmetro para ser enviada como uma string
  350.      * @param array $data Array de dados de uma requisição POST em forma de array("nome" => valor)
  351.      * @return string
  352.      */
  353.     private function preparePostData($data) {
  354.         if(!isset($data) || !is_array($data)) {
  355.             throw new Exception("Browser::preparePostData() - O parâmetro requer uma array.");
  356.             die();
  357.         }
  358.         // TODO: deprecated - usar http_build_query(), com aquele parâmetro que impede bug no Windows.
  359.         /*$str = "";
  360.         foreach ($data as $nome => $valor) $str .= "$nome=$valor&";
  361.         $str = substr($str, 0, strlen($str) - 1);
  362.         return $str;*/
  363.                 return http_build_query($data);
  364.     }
  365.    
  366.     // Analisa os headers da página, setando cookies para o domínio, etc.
  367.     // TODO: Esta função está errada. Para que o followLocationManual funcione, eu preciso setar os cookie para um
  368.     // domínio DEPOIS do redirect ter sido feito.
  369.     // OU seja, PRIMEIRO verificar se tem redirect, depois os cookies; e se tiver redirect, fazê-lo, e ai sim, na
  370.     // próxima chamada, pegar os cookies da requisição anterior (que vieram junto com o header do redirect anterior
  371.     // e setar.
  372.     private function parseHeaders($url) {
  373.         // Guarda possíveis cookies deste domínio
  374.         $cookies = array();
  375.         $domain = "";
  376.        
  377.         // Nos headers, cada cookie está em uma linha Set-Cookie: logo precisamos desse loop.
  378.         foreach($this->headers[$this->historyCount] as $header) {
  379.             // Procura por Set-Cookie no header que está analisando, e guarda em $temp
  380.             preg_match("/Set-Cookie: (.+)/", $header, $temp);
  381.            
  382.             // Se algum cookie foi encontrado, é separado
  383.             if(isset($temp[1]) && strlen(trim($temp[1])) > 0) {
  384.                 // Separa o cookie em [nome,valor]
  385.                 // Primeiro, vamos filtrar o cookie até o primeiro ';'
  386.                 preg_match("/([^\;]*)/", $temp[1], $preCookie);
  387.                
  388.                 // Agora sim, separamos em [nome,valor]
  389.                 $cookieReady = $this->splitCookie($preCookie[1]);
  390.        
  391.                 // Seta o cookie
  392.                 // Lembre-se: para enviar um cookie, precisamos do nome=valor!
  393.                 // Por isso, uso  $cookieReady["name"]."=".$cookieReady["value"]
  394.                 // Separei o cookie por [nome,valor] apenas para usar o nome dele como índice na array
  395.                 $cookies[$cookieReady["name"]] = $cookieReady["name"]."=".$cookieReady["value"];
  396.                
  397.                 // Tenta pegar o nome de domínio através do $cookie
  398.                 // Usando $temp[1], pois $splittedCookie[2] só tem o valor, e não as informações de domínio.
  399.                 $this->domain = $this->returnDomainFromCookieHeader($temp[1]);
  400.             }
  401.  
  402.             // Faz um followLocation 'manualmente' (sem usar CURL), caso a flag esteja ligada
  403.             if($this->followLocationManual) {
  404.                 // Procura por Location: no header que está analisando, e guarda em $temp
  405.                 preg_match("/Location: (.+)/", $header, $temp);
  406.                
  407.                 // Se achar uma linha com Location:, segue a url encontrada!
  408.                 if(isset($temp[1])) {
  409.                     $followUrl = $temp[1];
  410.                     // TEMP: Usando essa linha, só fará redirecionamentos para o mesmo domínio.
  411.                     // $followUrl = $url.$followUrl;
  412.                     preg_match("/(^\/.+)/", $temp[1], $samedomain);
  413.                     if(isset($samedomain[1])) {
  414.                         preg_match("/(.+)\//", $url, $findslash);
  415.                         if(isset($findslash[1])) {
  416.                             $url = $findslash[1];
  417.                         }
  418.                         $followUrl = $url.$samedomain[1];
  419.                     }
  420.                 }
  421.             }
  422.         }
  423.  
  424.         // Pega o domínio pela $url caso ele não esteja no cookie.
  425.         if(!$this->domain) $this->domain = $this->returnDomainFromURL($url);
  426.        
  427.         // Se houve cookies, insere para a array $cookies[$domain] os cookies capturados.
  428.         // TODO: Isso está errado; ele sobrescreve todo o índice; e não, ele só pode adicionar cookies, e reenscrever os que já existem; sem deletar os que não serão modificados.
  429.         if(count($cookies) > 0) {
  430.             // Para cada cookie, vamos adicionar os que não existem, e subsituir os que já existem
  431.             foreach($cookies as $cookieName=>$cookieValue) {
  432.                 $this->cookies[$this->domain][$cookieName] = $cookieValue;
  433.             }
  434.         }
  435.        
  436.         // Aqui é um local seguro para incrementarmos o $historyCount.
  437.         $this->historyCount++;
  438.        
  439.         // E depois de analisar todos os headers, faz um followLocation caso $followURL esteja setado.
  440.         if($this->followLocationManual && isset($followUrl)) {
  441.             return $followUrl;
  442.         } else return null;
  443.     }
  444.    
  445.         /* FAIL on Ajax */
  446.         public function parsePage($page) {
  447.                 // Primeiramente, vamos transformar $page em um DOMdocument válido, para usar XPath.
  448.                 $document = new DOMDocument();
  449.                 @$document->loadHTML($page);
  450.                 $xpath = new DOMXPath($document);
  451.                
  452.                 // E vamos pegar todos os forms na página.
  453.                 $domforms = $document->getElementsByTagName("form");
  454.                 $formslength = $domforms->length;
  455.                
  456.                 $container = array();
  457.                
  458.                 // Para cada form encontrado na página
  459.                 for($w=0;$w<$formslength;$w++) {
  460.                         // Vamos pegar os inputs dentro dele.
  461.                         $xpathinputs = $xpath->query(".//input", $domforms->item($w));
  462.                         $inputslength = $xpathinputs->length;
  463.                        
  464.                         // E para cada input encontrado...
  465.                         for($z=0;$z<$inputslength;$z++) {
  466.                                 $inputs[$z] = array();
  467.                                 // Vamos pegar seus atributos, e jogá-los em uma array.
  468.                                 foreach($xpathinputs->item($z)->attributes as $name => $value) {
  469.                                         $inputs[$z][$name] = $value->value;
  470.                                 }
  471.                                 // Por fim, vamos pegar o nodeValue do input (se houver).
  472.                                 // TODO: Verificar se usar trim() aqui é seguro.
  473.                                 $inputs[$z]["nodeValue"] = trim($xpathinputs->item($z)->nodeValue);
  474.                         }
  475.                        
  476.                         // Após parsear os inputs do form, vamos pegar os atributos do form em si, e colocar tudo na array 'container'.
  477.                         $attforms = array();
  478.                         foreach($domforms->item($w)->attributes as $name => $value) {
  479.                                 $attforms[$name] = $value->value;
  480.                         }
  481.                         $attforms["nodeValue"] = trim($domforms->item($w)->nodeValue);
  482.                         $attforms["inputs"] = $inputs;
  483.                        
  484.                         array_push($container, $attforms);
  485.                 }
  486.                
  487.                 // Tags 'script'
  488.                 $domscripts = $document->getElementsByTagName("script");
  489.                 $scriptslength = $domscripts->length;
  490.                
  491.                 $scriptscontainer = array();
  492.                
  493.                 for($t=0;$t<$scriptslength;$t++) {
  494.                         if($domscripts->item($t)->hasAttribute("src")) {
  495.                                 $scriptscontainer["src{$t}"] = $domscripts->item($t)->getAttribute("src");
  496.                         } else {
  497.                                 $scriptscontainer[$t] = $domscripts->item($t)->nodeValue;
  498.                         }
  499.                 }
  500.                
  501.                 array_push($container, $scriptscontainer);
  502.                
  503.                 return $container;
  504.     }
  505.    
  506.         /**
  507.      * Separa um cookie em [nome,valor]
  508.      * @param string
  509.      * @return array
  510.      */
  511.     private function splitCookie($cookie) {
  512.         if(!isset($cookie) || !is_string($cookie)) {
  513.                         throw new Exception("Browser::splitCookie - O parâmetro requer uma string.");
  514.                         die();
  515.                 }
  516.                 // Tira os espacinhos que tem entre os cookies
  517.         $cookie = trim($cookie);
  518.         // Para separar: nome vai até o primeiro ' = '
  519.         // 'valor' vai até o primeiro ' ; ' , o resto do cookie são informações de validade...
  520.         // Essas informações de validade são úteis apenas para por exemplo
  521.         // Pegar o domínio de onde o cookie é válido
  522.         // Pega o primeiro sinal de igual (o que separa nome/valor no cookie)
  523.         $position = stripos($cookie, "=");
  524.         // Separa por [nome,valor]
  525.         $name = substr($cookie, 0, $position);
  526.         $value = substr($cookie, $position+1);
  527.         return array("name" => $name, "value" => $value);
  528.     }
  529.    
  530.     /**
  531.      * Grava o cookie atual (e do domínio atual) em um arquivo.
  532.      * Lembre-se: o cookie gravado será o da última requisição feita.
  533.      * O cookie gravado sempre será gravado em formato .txt
  534.      * @param string $filename O caminho onde o arquivo de texto será criado. Tenha certeza de que o local tem permissões de escrita (chmod 0777)
  535.      * @return int
  536.      */
  537.     public function writeCookieOnFile($filename) {
  538.                 if(!isset($filename) || !is_string($filename)) {
  539.                         throw new Exception("Browser::writeCookieOnFile - O parâmetro requer uma string.");
  540.                         die();
  541.                 }
  542.         $cookie = implode(";", $this->cookies[$this->domain]);
  543.         if(!preg_match("/\.txt/", $filename)) $filename .= ".txt";
  544.         return file_put_contents("{$filename}", $cookie);
  545.     }
  546.    
  547.     /**
  548.      * Retorna o nome de domínio passado a url.
  549.      * @param string $url
  550.      * @return string
  551.      */
  552.     private function returnDomainFromURL($url) {
  553.         preg_match("/^(?:.?.tp.?\:\/\/)?([^\/]+)/", $url, $domain);
  554.         $domain = $domain[1];
  555.         if(isset($domain)) return $domain; else return null;
  556.     }
  557.  
  558.     /**
  559.      * Pega o nome de domínio atráves do header de um Set-Cookie
  560.      * @param string $header
  561.      * @return string
  562.      */
  563.     private function returnDomainFromCookieHeader($header) {
  564.         // Pega o nome do domínio que está no cookie
  565.         // TODO: Revisar esta regexp para pegar o nome do domínio no cookie (talvez mude de domínio para domínio)
  566.         preg_match("/domain=\.?([^;]+)/i", $header, $domain);
  567.         if(isset($domain[1])) {
  568.             $domain = trim($domain[1]);
  569.             return $domain;
  570.         } else {
  571.             return null;
  572.         }
  573.     }
  574.    
  575.     //------------------------------------------------------------------------------------------------
  576.     // GETTERS/SETTERS
  577.     //------------------------------------------------------------------------------------------------
  578.    
  579.     /**
  580.      * Seta um user-agent para nosso navegador.
  581.      * Use Browser::USERAGENT para facilitar.
  582.      * @param string $useragent Use uma das constantes em: Browser::USERAGENT
  583.      * return void
  584.      */
  585.     public function setUserAgent($useragent) {
  586.         if(!isset($useragent) || !is_string($useragent)) {
  587.             throw new Exception("Browser::setUserAgent - O parâmetro requer uma string.");
  588.             die();
  589.         }
  590.         $this->useragent = $useragent;
  591.     }
  592.    
  593.     /**
  594.      * Retorna a string representando o user-agent atual.
  595.      * @return string
  596.      */
  597.     public function getUserAgent() {
  598.         return $this->useragent;
  599.     }
  600.    
  601.     /**
  602.      * Retorna o domínio atual (o domínio da última requisição).
  603.      * @return string
  604.      */
  605.     public function getDomainName() {
  606.         return $this->domain;
  607.     }
  608.    
  609.     /**
  610.      * Retorna um cookie específico para um domínio. Se o cookie não existir, o método retorna null.
  611.      * @param string $cookie
  612.      * @param string $domain
  613.      * @return string
  614.      */
  615.     public function getCookie($cookie, $domain=null) {
  616.                 if(!is_string($cookie)) {
  617.                         throw new Exception("Browser::getCookie - Parâmetro 1 requer uma string.");
  618.                         die();
  619.                 }
  620.         if(!isset($domain)) {
  621.                         $domain = $this->domain;
  622.                 } else if(!is_string($domain)) {
  623.                         throw new Exception("Browser::getCookie - Parâmetro 2 requer uma string, ou valor nulo.");
  624.                         die();
  625.                 }
  626.         if(isset($this->cookies[$domain][$cookie])) {
  627.             return $this->cookies[$domain][$cookie];
  628.         } else {
  629.             return null;
  630.         }
  631.     }
  632.    
  633.     /**
  634.      * Seta cookies para um domínio. O método apaga todos os cookies antigos daquele domínio!
  635.      * Esta função é útil quando se tem os cookies em uma string.
  636.      * Por exemplo, como os que são salvos com a função writeCookieOnFile().
  637.      * @param string $cookie Uma string contendo todos os cookies.
  638.      * @param string $dominio (OPCIONAL) Domínio onde serão usados os cookies.
  639.      */
  640.     public function setCookie($cookie, $domain=null) {
  641.                 if(!is_string($cookie)) {
  642.                         throw new Exception("Browser::setCookie - Parâmetro 1 requer uma string.");
  643.                         die();
  644.                 }
  645.         // Se o domínio não foi passado, vamos usar o atual.
  646.         if(!isset($domain)) {
  647.                         $domain = $this->domain;
  648.                 } else if(!is_string($domain)) {
  649.                         throw new Exception("Browser::setCookie - Parâmetro 2 requer uma string, ou valor nulo.");
  650.                         die();
  651.                 }
  652.                
  653.         // Apagando os cookies antigos
  654.         $this->cookies[$domain] = array();
  655.         // Depois, vamos separar os cookies um por um, por [nome,valor]
  656.         $cookies = explode(";", $cookie);
  657.        
  658.         foreach($cookies as $entirecookie) {
  659.             // Separa o cookie em [nome,valor]
  660.             $cookieready = $this->splitCookie($entirecookie);
  661.             // Seta o cookie
  662.             $this->cookies[$domain][$cookieready["name"]] = $cookieready["name"]."=".$cookieready["value"];
  663.         }        
  664.     }
  665.    
  666.     /**
  667.      * Configura para que as requisições não sejam feitas com CURLOPT_FOLLOWLOCATION, caso o host esteja
  668.      * em safe_mode, ou open_basedir. Use o parâmetro 1 para usar fazer redirecionamentos 'manualmente',
  669.      * 0 para usar CURLOPT_FOLLOWLOCATION. O valor default é 0.
  670.      * OBS: Ao ser instanciada, a classe já identifica se a função está habilitada no host, configurando automaticamente esse método.
  671.      * @param int $isRedirecting FALSE|0 -> Usa CURLOPT_FOLLOWLOCATION, TRUE|1 -> faz a requisição manualmente.
  672.      * @return void
  673.      */
  674.     public function setManualRedirect($isRedirecting) {
  675.         $this->followLocationManual = (bool)$isRedirecting;
  676.     }
  677.    
  678.     //------------------------------------------------------------------------------------------------
  679.     // DEBUG!
  680.     //------------------------------------------------------------------------------------------------
  681.        
  682.     /**
  683.      * Dá um print_r em todas as variáveis importantes de uma só vez.
  684.      * @return void
  685.      */
  686.     public function printAll() {
  687.         print_r(array(
  688.             "domain"            => $this->domain,
  689.             "useragent"    => $this->useragent,
  690.             "historyCount" => $this->historyCount,
  691.             "history"      => $this->history,
  692.             "cookies"      => $this->cookies,
  693.             "headers"      => $this->headers,
  694.         ));
  695.     }
  696. }
  697.  
  698. ?>