Subversion PHP · 14 min read · Jan 19, 2026

Configurando Um Repositório Modular Subversion Para Websites PHP-Driven

Configurando Um Repositório Modular Subversion Para Websites PHP-Driven

Willem Bogaerts - Kratz Business Solutions

Resumo

Compartilhar código entre projetos ainda não é uma questão trivial com subversion. Especialmente se você está familiarizado com SourceSafe, você descobrirá que o subversion dificulta o compartilhamento de código. O subversion parece ser realmente ótimo em criar uma bagunça de versões e bom em resolver uma, mas a razão pela qual eu preciso de controle de código-fonte é para prevenir tal bagunça. É aqui que o subversion pode ser grandemente melhorado, mas não é impossível. Este howto demonstrará uma configuração de diretório que leva em conta o mecanismo de compartilhamento do subversion, bem como outras questões que os repositórios trazem.

Convenção Neste Howto

Você verá em muitos lugares. Substitua isso pela raiz do seu repositório. A raiz do seu repositório é uma URL que geralmente começa com https://, file:/// ou svn://.
Assume-se que você sabe o que é subversion, que conhece seu uso básico e que você tem ou pode criar um repositório.
Este howto não lhe dá “a melhor” maneira de organizar um repositório, porque isso depende de suas necessidades. Ele visa ajudar mostrando algumas dessas decisões e como elas afetam a estrutura do repositório.

Maneira de Compartilhar Código do Subversion

Diretórios em uma cópia de trabalho podem conter links para outros repositórios definindo uma propriedade svn:externals. Isso fará com que o diretório do repositório vinculado seja incluído em sua cópia de trabalho, mas não fará parte do projeto em si. Isso significa que você verá o diretório com todos os arquivos em sua cópia de trabalho, mas não no repositório central. Você só pode ver a propriedade no repositório central.

Existem algumas desvantagens nessa forma de compartilhamento:

  • Você só pode fornecer URLs absolutas para os links, então migrar um repositório sem problemas é quase impossível. Mesmo mudar protocolos (svn:// para https://, por exemplo) trará cópias de trabalho quebradas e muitos problemas.
  • Você não pode vincular arquivos, apenas diretórios. Isso terá um grande impacto na organização do código.

Questões de Diretório PHP

Existem algumas coisas a considerar ao trabalhar com diretórios em um site, e em particular PHP. Como questão de segurança, não queremos colocar todas as fontes em um diretório que seja acessível a partir de um navegador. Os únicos arquivos que colocaremos lá são arquivos que precisam ser chamados por um navegador. Eu chamo esses arquivos de arquivos “executáveis” em oposição a arquivos “definidores” que contêm apenas definições de classe ou definições de função dentro deles. Misturar código executável e código definidor em um único arquivo geralmente não é uma boa ideia.

Além disso, como questão de segurança, muitos sites têm uma área restrita que contém testes unitários, páginas de log de erros ou até mesmo um site completo de backoffice. Essa área restrita é geralmente protegida por senha pelo servidor web.

Assim, um projeto contém diretórios com arquivos definidores e um diretório raiz web (geralmente www/ ou htdocs/) que contém arquivos executáveis e, opcionalmente, uma área restrita.

PHP E Diretórios Relativos

Infelizmente, o PHP tem uma maneira muito contra-intuitiva de determinar a localização de um arquivo incluído. Os comandos para inclusão de um arquivo funcionam todos em relação ao primeiro arquivo chamado, e não em relação ao arquivo atual. Para piorar as coisas, a localização do arquivo atual é usada quando a localização original não leva a um arquivo existente.

Isso soa complicado, e é, então aqui está um exemplo:

Suponha que você chame uma página “index.php”. Esta página inclui uma página “library/functions.php”. Esta, por sua vez, inclui “settings.php”. Você suspeitaria que “settings.php” é procurado no diretório da biblioteca, mas não é. Ele é procurado no mesmo diretório que “index.php”!

Como dito acima, o PHP continua a procurar no diretório esperado se o arquivo não for encontrado. Então, tudo parece funcionar como você espera, até que você encontre arquivos com o mesmo nome em diretórios diferentes. Você terá uma grande dificuldade para descobrir por que o PHP “de repente” escolhe o arquivo do diretório errado.

Isso significa que você não pode incluir outro arquivo com caminhos relativos de forma segura. Devemos torná-los absolutos com código como:

require_once(dirname(__FILE__) . '/library/functions.php');

Não se esqueça do ‘/‘ no início do caminho relativo, pois a função dirname retorna caminhos sem barras finais.

Organização Do Nosso Repositório

Existem algumas coisas a considerar para o código do projeto. Para desenvolvimento, é conveniente ter o projeto inteiro verificado como um todo. Mas para um servidor ao vivo, isso pode não ser o que você deseja. Você pode querer verificar o código do banco de dados (scripts SQL) em um diretório que está longe dos diretórios web, talvez até mesmo em outro servidor. Pode haver partes do projeto que você não deseja de forma alguma em um servidor, mas são necessárias no desenvolvimento, como arquivos de documentação.

Além disso, queremos um lugar central para armazenar o código compartilhado. Poderíamos teoricamente “pegar emprestado” código de outro projeto, mas seria realmente difícil acompanhar quais projetos dependem de quais outros projetos. Em vez disso, moveremos qualquer código padrão para um local central.

Diretórios Raiz

O local central contendo todo o código padrão será “/standard/“. Este diretório será, claro, subdividido nas bibliotecas padrão. Os projetos estarão na pasta “/projects/“, que pode ser subdividida por cliente e projeto, por exemplo. O código de fora, como bibliotecas baixadas como PHPMailer ou FPDF, será colocado em “/external/“.

Branches E Tags

É uma boa prática em um repositório subversion manter seu código em uma branch chamada “trunk” e criar outras branches no mesmo nível, se necessário. Mesmo que você não queira nenhuma branch ainda, crie um diretório “trunk” diretamente abaixo de um projeto. Trunk é a branch ativa.

Pense sobre isso. Quando vinculamos ao trunk de uma biblioteca padrão, vinculamos à branch ativa. Isso significa que todas as correções de erros na biblioteca vinculada serão atualizadas sempre que atualizarmos uma cópia de trabalho. Mas se introduzirmos um erro, isso também será atualizado em nossas cópias de trabalho. Mesmo na que está em um servidor web ao vivo! Você pode querer vincular a uma branch mais ou menos estável em vez disso, mas corrigir erros exigirá um pouco mais de sobrecarga.

Qualquer link que você escolher, é bom saber que você pode sempre mudar depois.

O Que Há Em Um Componente Ou Projeto

Existem muitas coisas que queremos colocar em um repositório, e não queremos tudo no mesmo lugar em nosso servidor web. Algumas coisas são melhores não serem colocadas em um servidor web ou podem ser verificadas em um servidor diferente, como um servidor de banco de dados.

Observe que a configuração de uma cópia de trabalho em uma máquina de desenvolvimento difere daquela em um servidor. Em uma máquina de desenvolvimento, você provavelmente terá um diretório raiz central para todos os seus projetos. Este diretório é então configurado como acessível via seu servidor web localhost, para que você não precise reconfigurar seu servidor para cada projeto em que está trabalhando. Em um servidor ao vivo, as coisas devem ser configuradas de qualquer maneira e considerações de segurança nos fazem verificar apenas aqueles arquivos que são necessários e nada mais. Para começar, fazemos os seguintes diretórios em cada componente (se necessário):

documentation/Entrada do cliente, esquemas de banco de dados e objetos, etc. Não há necessidade de colocar isso em um servidor web, mas é muito útil para desenvolvedores.sql/Scripts de criação, atualização e conversão de banco de dados. para verificação no servidor de banco de dados.code/O código real da aplicaçãotest/Testes unitários

Esses diretórios aparecem aproximadamente os mesmos nos projetos, com a diferença de que os testes unitários devem ser executáveis e, portanto, são parte da seção de código. Se você não quiser que os testes unitários estejam presentes em um servidor ao vivo, pode mantê-los separados. Para projetos, minha configuração será:

documentation/Como acima. Também contém referências de componentes usados.database/Contém scripts de criação de banco de dados e referências de

sql

diretórios de componentes usados.web/O código da aplicação definidora.web/htdocs/A raiz do site com o código da aplicação em execução real, páginas HTML e outros conteúdos web, como folhas de estilo e imagens.web/htdocs/restricted/Área restrita.web/test/Testes unitários. Contém referências dos testes dos componentes usados.selenium/Testes funcionais (veja

http://selenium.openqa.org/

).

Código Executável E Definidor (Novamente)

Poderíamos colocar código executável e código definidor em diretórios separados, para que pudéssemos verificar o código executável em um diretório separado dentro da raiz web. No entanto, isso significa que o subdiretório contendo esse código se tornaria parte da URL (como www.example.com/restricted/errorhandling/viewerrorlog.php), e isso pode não ser o que você deseja (você pode querer www.example.com/restricted/viewerrorlog.php). Há uma solução para isso. Podemos colocar os arquivos executáveis em um lugar não acessível e colocar uma espécie de proxy em um lugar acessível. Este proxy é um arquivo PHP com nada mais do que uma declaração include ou require que aponta para o arquivo na localização não acessível:

/htdocs/restricted/viewerrorlog.php
// É um proxy que aponta para /errorhandling/viewerrorlog.php, que não pode ser chamado diretamente por um navegador.
// (porque está fora da raiz web)
require(dirname(__FILE__) . '/../../errorhandling/viewerrorlog.php');
?>

O diretório errorhandling é então compartilhado da biblioteca padrão e contém arquivos de classe “definidores” para lidar com erros e um arquivo “executável” para visualizá-los.

Configurações Dependentes da Máquina

Arquivos de configurações são um pouco incomuns em um repositório. Você quer tê-los em um repositório, mas não quer que eles sejam atualizados automaticamente. Eu uso um método intermediário para meus arquivos de configurações: crio um arquivo chamado settings_example.php no diretório de configurações. Este arquivo contém todas as configurações com comentários sobre como configurá-las para diferentes máquinas. Ele também contém um comentário que diz que você deve copiá-lo para um arquivo chamado “settings.php”

Eu só me refiro a settings.php de outros arquivos, e defino uma propriedade svn:ignore com o valor de “settings.php” no diretório de configurações. Isso significa que uma cópia de trabalho não funcionará “pronta para uso”, mas não funcionará de qualquer maneira devido à dependência de configurações dependentes da máquina. A propriedade svn:ignore impede que configurações de diferentes máquinas sobrescrevam as suas em uma atualização.

Colocando Em Prática

Um exemplo. Digamos que temos um projeto que precisa enviar e-mails e criar arquivos PDF. Decidimos que usamos o PHPMailer do PEAR e as bibliotecas FPDF. Para evitar impor quaisquer restrições especiais na configuração do PHP, apenas baixamos o PHPMailer e não usamos o método “pear install” para instalá-lo.

Além disso, este projeto terá tratamento de erros padrão e uma classe de banco de dados padrão. Para simplificar, suponho que você comece com um repositório vazio.

Criando Os Diretórios Necessários

Primeiro, crie os diretórios raiz descritos acima: /external/, /standard/ e /projects/. Eu recomendo usar uma interface gráfica para subversion, como TortoiseSVN ou RapidSVN.

Vamos lidar primeiro com as bibliotecas externas. Baixe e descompacte PHPMailer e FPDF, e importe-os para /external/phpmailer/trunk/ e /external/fpdf/trunk/, respectivamente. Se você quiser, pode criar branches “firstdownload” de ambos.

Em seguida, criamos um projeto para trabalhar. Este projeto é para o cliente “CustomerInc” e o projeto é chamado “SamplePrj”. Tenho certeza de que você pode pensar em nomes melhores para seus projetos. Então, criamos o caminho completo de /projects/CustomerInc/SamplePrj/trunk/. O “trunk” é a branch principal: você pode estar trabalhando em outras branches (de desenvolvimento), mas elas eventualmente são mescladas ao trunk. Se você estiver trabalhando no trunk (e por que não estaria se for um novo projeto), este diretório é o diretório a ser verificado como uma cópia de trabalho. Mas vamos esperar com isso. Sob o trunk, criamos diretórios “propósito” para código web (php, html, etc.), código de banco de dados e documentação.
Sob o diretório web, criei uma raiz web (htdocs) e um diretório para arquivos php genéricos específicos do projeto.

Criando Os Compartilhamentos

Até agora, fiz tudo diretamente no repositório. Mas para compartilhar código, precisamos fazer uma cópia de trabalho primeiro. Então vamos fazer isso.

Como nosso servidor web localhost precisa ser capaz de acessar todo o código do nosso projeto, faço minha cópia de trabalho em uma pasta raiz do meu sistema de arquivos (/projects//) ou disco rígido (C:\projects\\). Caso contrário, o servidor web precisaria ter direitos de acesso no diretório home de todos os usuários que usam subversion. Ao usar uma pasta raiz, você pode garantir que o servidor web possa acessá-los todos, e você pode até separar diretórios para diferentes usuários. Para sistemas baseados em unix, todos os usuários do subversion devem ser membros do grupo do seu servidor web, e o diretório total de projetos deve ser pelo menos legível pelo grupo. Pelo menos as coisas da web, isso é.

Em nossa cópia de trabalho, temos o diretório web, onde o diretório htdocs está localizado e onde queremos os links para FPDF e PHPMailer. Para criar esses links, adicione uma propriedade subversion svn:external ao diretório web com o seguinte valor:

fpdf /external/fpdf/trunk/code
phpmailer /external/phpmailer/trunk/code

(sim, o valor consiste em 2 linhas) e então atualize sua cópia de trabalho. Você deve agora obter dois diretórios extras com sua atualização, como mostrado nesta captura de tela de uma cópia de trabalho do nosso projeto.

Nota: Eu criei os links para o trunk. Você pode vincular a uma branch em vez disso ou vincular a um número de revisão específico. Eu criei apenas referências externas às seções de código. Você provavelmente gostaria de criar referências externas à documentação das bibliotecas externas também.

Movendo Código Útil Para A Biblioteca Padrão

À medida que seus projetos amadurecem, você desenvolve mais e mais código que seria útil em outros projetos. No nosso caso, digamos que desenvolvemos um pacote de tratamento de erros e uma classe de banco de dados. Claro, o primeiro passo é remover quaisquer dependências de projeto desse código. Coloque o código para compartilhar (vamos chamá-lo de pacote) em um diretório separado, pois deve estar em um diretório separado quando for compartilhado de volta da biblioteca padrão. Então, garantimos que o código genérico de tratamento de erros esteja em um diretório errorhandling e a classe de banco de dados esteja em um diretório database, ambos diretamente sob o diretório web.

Você pode usar os comandos svn mkdir e svn move ou qualquer cliente gráfico de subversion para mover o pacote em seu projeto para /standard//trunk/code. Não se esqueça de atualizar sua cópia de trabalho de desenvolvimento após isso (você verá o diretório do pacote desaparecer), então adicione duas linhas à propriedade svn:external (da forma /standard//trunk/code), atualize (para obter o diretório de volta, agora da biblioteca padrão) e confirme a alteração da propriedade. A maioria dos problemas está na atualização de suas cópias de trabalho, especialmente se essa cópia de trabalho for um site ao vivo para o qual você deseja o mínimo de tempo de inatividade possível.
Se você atualizar suas cópias de trabalho após cada alteração no servidor, nada pode dar errado. O problema surge quando você tem uma cópia de trabalho que ainda tem o pacote na localização original do projeto e deseja atualizar para um estado onde esse mesmo diretório agora vem de uma referência svn:externals. Se você tentar isso, receberá um erro dizendo que o diretório de destino para essa referência externa já existe, então a referência externa não pode ser criada. Isso é facilmente resolvido removendo esse diretório manualmente antes de atualizar essa cópia de trabalho.

Mova quaisquer testes unitários, documentação, scripts SQL, etc. de maneira semelhante para a biblioteca padrão.

Atualizando O Servidor Ao Vivo

Quando mais de uma pessoa tem acesso ao servidor web ou de banco de dados, é melhor usar uma conta para todos eles. Isso garante que os diretórios .svn não sofram com configurações de conta conflitantes e direitos de usuário. Além disso, você pode negar a essa conta permissões de gravação no repositório, para que um servidor web comprometido não possa facilmente cometer coisas ruins.

Em um servidor web Linux, os seguintes direitos fazem sentido: leitura e gravação para o usuário que atualiza a cópia de trabalho “ao vivo”, direitos de leitura para o grupo (que é o grupo do servidor web) e nenhum direito para outros. Se você definir o bit SGID nos diretórios, todos os arquivos adicionados também serão acessíveis ao servidor web. Você pode definir o bit SGID com chmod g+s . Considere definir a umask para 0027 para definir as permissões corretas para arquivos adicionados também. Um diretório htdocs recém-adicionado pode então parecer:

drwxr-s--- 9 webdev abyssd 4096 2007-09-08 12:42 restricted/  
-rw-r----- 1 webdev abyssd 441 2007-09-08 12:42 index.php  
-rw-r----- 1 webdev abyssd 2954 2007-09-08 12:42 main.css

Código de Banco de Dados Modular

Já discutimos como as inclusões funcionam no PHP, mas como incluímos arquivos em SQL? Precisamos ser capazes de incluir diferentes arquivos de diferentes diretórios no código SQL, já que organizamos nosso repositório dessa maneira. Se compartilharmos código PHP, devemos compartilhar o código SQL correspondente também. Eu escrevi um pequeno script em PHP e um pouco em python para fazer isso. Você pode encontrá-los em http://www.w-p.dds.nl/sqlincludeparser_php.txt (PHP) e http://www.w-p.dds.nl/sqlincludeparser_py.txt (Python). Esses scripts permitem que você defina declarações de inclusão em um arquivo base da forma:

CREATE DATABASE IF NOT EXISTS someDatabase;
USE someDatabase;
-- @include(errorhandling/errortables.sql) # (re)cria tabelas usadas pelo pacote de tratamento de erros
DROP TABLE IF EXISTS someTable;
CREATE TABLE someTable
      (someID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
...etc.

Se você tornar cada arquivo incluído e o arquivo base repetíveis, poderá usá-lo para chegar a um estado limpo repetidamente, o que pode ser muito útil para testes e desenvolvimento. Para usá-lo, basta canalizar a saída do meu script para o cliente de linha de comando sql:

sqlincludeparser.py  | mysql -u  -p []
Share: X/Twitter LinkedIn

Receba novas postagens na sua caixa de entrada

Sem spam. Cancele a assinatura a qualquer momento.