Manipulação de URL usando PHP

Artigo que explica como manipular URLs em PHP, para ajustar suas partes (protocolo, usuário, senha, domínio, porta, query string e fragmento).

Introdução
Quebra cabeça de URL

No desenvolvimento web, é muito comum a utilização de links. Também é comum que os links precisem ser manipulados para incluir ou remover parâmetros. Neste post, veremos como manipular um link e seus parâmetros de forma facilitada e segura.

Quebrando uma URL em partes

Quando um programador não se conhece os recursos do PHP para manipular URLs, é comum que ele se aventure a manipular a string com a URL diretamente, fazendo operações como verificar se existe '?' na URL e obter o conteúdo a esquerda ou a direita, etc.

A principal função para "parsear" uma URL e quebrá-la em partes é a parse_url. Esta função pode ser usada para dois propósitos: para obter todas as partes de uma URL ou para obter uma parte específica de uma URL (por exemplo, o protocolo, o domínio, a query string, etc).

Para obter todas as partes, basta passar a URL como parâmetro e não informar o segundo parâmetro:

$partes = parse_url('http://rubens:senha@teste.com.br:80/a/b/c.php?x=1&y=2#item');

echo $partes['scheme'] . PHP_EOL;     // Obtém o protocolo: "http"
echo $partes['user'] . PHP_EOL;       // Obtém o usuário "rubens"
echo $partes['pass'] . PHP_EOL;       // Obtém a senha "senha"
echo $partes['host'] . PHP_EOL;       // Obtém o domínio "teste.com.br"
echo $partes['port'] . PHP_EOL;       // Obtém a porta "80"
echo $partes['path'] . PHP_EOL;       // Obtém o path "a/b/c.php"
echo $partes['query'] . PHP_EOL;      // Obtém a query string "x=1&y=2"
echo $partes['fragment'] . PHP_EOL;   // Obtém a ancora (ou fragmento) "item"

Para obter uma parte específica, basta passar o segundo parâmetro da função, que especifica qual parte é desejável. Este parâmetro pode receber uma das constantes:

  • PHP_URL_SCHEME
  • PHP_URL_HOST
  • PHP_URL_PORT
  • PHP_URL_USER
  • PHP_URL_PASS
  • PHP_URL_PATH
  • PHP_URL_QUERY
  • PHP_URL_FRAGMENT

Observação: caso você queira o nome do arquivo, basta aplicar a função basename sobre o valor de $partes['path'], conforme o exemplo:

$arquivo = isset($partes['path']) ? basename($partes['path']) : '';

Quebrando a query string em partes

Como você pode observar, a função parse_url devolve a query string da forma como está na URL e não dividida em partes. Para quebrar a query string em partes, basta usar a função parse_str passando a query string como primeiro parâmetro e um array como segundo parâmetro. O array será preenchido com as variáveis presentes na query string, de forma que o índice do array guarda o nome da variável e cada posição aponta para o respectivo valor da variável (o valor é automaticamente decodificado com urldecode). Veja um exemplo:

// Supondo que $partes['query'] guarda o valor "x=1&y=2"
parse_str($partes['query'], $queryArray);

echo $queryArray['x'] . PHP_EOL; // Obtém "1"
echo $queryArray['y'] . PHP_EOL; // Obtém "2"

Modificando a URL ou Query String

Depois de quebrar a URL e, opcionalmente, a query string, basta manipular o array $partes ou $queryArray da forma desejada. Por exemplo, vamos mudar o protocolo de http para https, mudar a porta de 80 para 81 e remover o parâmetro "x" e incluir o parâmetro "z" com valor "a b c":

// Mudando o protocolo para https
$partes['scheme'] = 'https';

// Mudando a porta para 81
$partes['port'] = 81;

// Removendo a variável "a" do query array
unset($queryArray['x']);

// Incluindo a variável "z" no query array
$queryArray['z'] = 'a b c';

Montando a URL com as partes

Depois de manipular a URL e/ou a query string, agora precisamos montar as partes e formar a URL na forma de string. Para isso, basta usar a função http_build_query para montar o query array para transformá-lo em query string. Esta função já faz a codificação usando urlencode, então você não precisa se preocupar com caracteres reservados.

// Monta a query string
$partes['query'] = http_build_query($queryArray, null, '&');

Infelizmente, a função http_build_url só está disponível via PECL. Porém, é uma função muito simples de se implementar. Veja abaixo:

/**
 * Monta a URL pelas suas partes. (como a funcao http_build_url)
 * @param array $partes Partes da URL (como no retorno de parse_url).
 * @return string
 */
function build_url($partes) {
    $url = '';
    $url .= $partes['scheme'] . '://';
    if (isset($partes['user'])) {
        $url .= urlencode($partes['user']);
        if (isset($partes['pass'])) {
            $url .= ':' . urlencode($partes['pass']);
        }
        $url .= '@';
    }
    $url .= $partes['host'];
    if (isset($partes['port'])) {
        $url .= ':' . number_format($partes['port']);
    }
    if (isset($partes['path'])) {
        $url .= $partes['path'];
    }
    if (isset($partes['query'])) {
        $url .= '?' . $partes['query'];
    }
    if (isset($partes['fragment'])) {
        $url .= '#' . urlencode($partes['fragment']);
    }
    return $url;
}

Bom, agora é só usar essa função e correr para o abraço:

$url = build_url($partes);

11 comentários

Everton Henrique disse...

Excelente conteúdo!
Tenho algumas dúvidas:

1º Gostaria de perguntar por que você usa "PHP_EOL" no final de algumas linhas.

2º Seria possível o uso reverso da manipulação de URLs passando junto da URL, parâmetros adicionais com valores para serem distribuídos em um form, por exemplo?
Algo como:
Supondo que eu queira passar para um form de login de um site qualquer, um usuário e uma senha. Seria possível fazer isso passando os dados por uma URL e utilizando na própria URL, o ID dos campos do form?

Não sei se fui bem claro na última pergunta...

rubS (autor do blog) disse...

Olá Everton,

1º O PHP_EOL é uma constante do PHP que guarda a quebra de linha usual do S.O. Eu coloco apenas para não correr o risco de alguém tentar executar o script e ver todo o conteúdo colado. Se ele executar no terminal, verá uma quebra de linha. Se ele executar na web, verá um espaço.

2º Isso depende mais da página que monta o formulário do que o fato de você passar estes dados via URL. De fato, é possível passar parâmetros via URL (respeitando o limite de tamanho de uma requisição GET), mas estes dados só serão usados para preencher o formulário se ele foi programado para isso. Pode ser que o formulário esteja preparado apenas para trabalhar com dados enviados com POST, por exemplo.

Anônimo disse...

Boa tarde! Me chamo Alessandro e preciso de uma ajuda sua. Tentei de várias maneiras, fazem dias inclusive, e não estou conseguindo... Bom preciso que ao clicar em uma imagem, o php busque a URL que estava e abra uma página tirando dessa URL o nome de uma pasta e "mantendo" mesmo nome de arquivo: Por exemplo:

Estou na página www.site.com.br/eng/teste.php ...... Quando clicar na imagem serei redirecionado para URL www.site.com.br/teste.php

Por favor, como faço isso no php?

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Alessandro

Para remover um diretório de uma URL você pode fazer assim:

$url = 'http://www.site.com.br/eng/teste.php';
$partes = parse_url($url);

// Modificando o path
$partes['path'] = '/' . basename($partes['path']);

// Montando a URL
$url = build_url($partes);

(A função build_url é a que eu postei no artigo).

Anônimo disse...

Rubens, agradeço o retorno. Mas fiz isso que me disse, porém o link fica sendo sempre o mesmo, ou seja, www.site.com.br/teste.php... preciso que sempre o nome do arquivo seja alterado conforme a página que usuário esteja.

Se estou na página contato.php (www.site.com.br/eng/contato.php) serei redirecionado para www.site.com.br/contato.php
Se estou na página teste.php (www.site.com.br/eng/teste.php) serei redirecionado para www.site.com.br/teste.php...
etc...

Assim, estou fazendo um site que tem dois idiomas (portugues e ingles), nesse tenho dois site similares (mudando apenas os textos) portanto o ingles está na raiz /eng (www.site.com.br/eng) e o primário (vamos dizer assim) na raiz da hospedagem. Quando o usuario clica na bandeira do idioma é redirecionado para mesma página porém de acordo com idioma escolhido.

Não sei se consegui explicar bem, e desculpe minha falta de conhecimento.

Aguardo, muito obrigado.
Alessandro

Rubens Takiguti Ribeiro (autor do blog) disse...

Oi, Alessandro

O código que passei foi apenas um exemplo com a URL que você passou. Era só para você tomar como base para implementar alguma função que faça o que você precisa exatamente.

O que o código faz é:
1 - quebrar uma URL em partes;
2 - modificar a parte "path" aplicando a função basename para gerar um path apenas com o nome do arquivo;
3 - junta as partes novamente para formar a nova URL.

O que a função basename faz é extrair apenas o nome do arquivo. Então se aplica a função basename sobre "/eng/teste.php", ela vai devolver "teste.php".

Veja um exemplo de função genérica para fazer isso:
http://pastebin.com/AufFeGv7

E este outro exemplo usa expressão regular para extrair apenas o primeiro diretório do path:
http://pastebin.com/KnEG6j1E

Então você precisa passar para a função a URL da página corrente. Um EXEMPLO de como obter a página corrente, é acessando os dados da variável $_SERVER. Porém, elas podem variar de acordo com o seu servidor (Windows ou Linux).

$url_corrente = $_SERVER['REQUEST_SCHEME'] . '://'. $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];

Anônimo disse...

Rubens, fiz assim...

$server = $_SERVER['SERVER_NAME'];
$endereco = $_SERVER ['REQUEST_URI'];

function build_url($partes) {
$url = '';
$url .= $partes['scheme'] . '://';
if (isset($partes['user'])) {
$url .= urlencode($partes['user']);
if (isset($partes['pass'])) {
$url .= ':' . urlencode($partes['pass']);
}
$url .= '@';
}
$url .= $partes['host'];
if (isset($partes['port'])) {
$url .= ':' . number_format($partes['port']);
}
if (isset($partes['path'])) {
$url .= $partes['path'];
}
if (isset($partes['query'])) {
$url .= '?' . $partes['query'];
}
if (isset($partes['fragment'])) {
$url .= '#' . urlencode($partes['fragment']);
}
return $url;
}

$url = $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
$partes = parse_url($url);

// Modificando o path
$partes['path'] = '/' . basename($partes['path']);

// Montando a URL
$url = build_url($partes);

no href coloquei $url

O que faltou aí?

Anônimo disse...

Rubens, consegui fazer.... Ufa! hahaha

Obrigado pela ajuda e parabéns pelo blog.

Abraço!
Alessandro

Anônimo disse...

Olá Rubens tudo bem? Cara estou retornando precisando tirar uma dúvida contigo....

As url no php que havia feito (conforme mencionei nas mensagens acima) funcionaram, porém ao implantar mesmo código em outro server, não funciona os redirecionamentos.... porque isso? Talvez por causa do Sistema Operacional do Servidor? Pode ser diferente do que deu certo?

Obrigadão!

Alessandro

Rubens Takiguti Ribeiro (autor do blog) disse...

Oi, Alessandro

Como eu mencionei anteriormente, poderia precisar de ajustes para montar a URL em que o usuário está, dependendo do Sistema Operacional. Você pode confirmar isso imprimindo o valor da URL para onde o usuário seria redirecionado. Vai notar que alguma parte estará errada. Neste casso, você precisa checar o que há disponível na variável $_SERVER, para usar o valor adequado.

Dê uma olhada nesse link:
http://stackoverflow.com/questions/6768793/get-the-full-url-in-php