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á
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á “
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
).
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:
Vamos lidar primeiro com as bibliotecas externas. Baixe e descompacte PHPMailer e FPDF, e importe-os para
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
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/
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
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
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.cssCó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 [] Receba novas postagens na sua caixa de entrada
Sem spam. Cancele a assinatura a qualquer momento.