Subversion Repository · 14 min read · Jan 19, 2026

Impostare un Repository Subversion Modulare Per Siti Web PHP-Driven

Impostare un Repository Subversion Modulare Per Siti Web PHP-Driven

Willem Bogaerts - Kratz Business Solutions

Abstract

Condividere codice tra progetti non è ancora una questione banale con subversion. Soprattutto se sei familiare con SourceSafe, scoprirai che subversion rende difficile condividere codice. Subversion sembra essere davvero ottimo nel creare un pasticcio di versioni e buono nel risolverne uno, ma il motivo per cui ho bisogno di controllo del codice sorgente è per prevenire tale pasticcio. Qui subversion può essere notevolmente migliorato, ma non è impossibile. Questo howto dimostrerà una configurazione delle directory che tiene conto del meccanismo di condivisione di subversion, così come di altri problemi che i repository portano.

Convenzione In Questo Howto

Vedrai in molti posti. Sostituisci questo con la radice del tuo repository. La radice del tuo repository è un URL che di solito inizia con https://, file:/// o svn://.
Si presume che tu sappia cos’è subversion, che conosci il suo utilizzo di base e che hai o puoi creare un repository.
Questo howto non ti fornisce “il migliore” modo per organizzare un repository, perché dipende dalle tue esigenze. Mira ad aiutarti mostrando alcune di quelle decisioni e come influenzano la struttura del repository.

Modo di Condivisione del Codice di Subversion

Le directory in una copia di lavoro possono contenere collegamenti ad altri repository definendo una proprietà svn:externals. Questo farà sì che la directory del repository collegato venga inclusa nella tua copia di lavoro, ma non diventerà parte del progetto stesso. Ciò significa che vedrai la directory con tutti i file nella tua copia di lavoro, ma non nel repository centrale. Puoi vedere la proprietà solo nel repository centrale.

Ci sono alcuni svantaggi a questo modo di condividere:

  • Puoi fornire solo URL assoluti per i collegamenti, quindi migrare senza problemi un repository è quasi impossibile. Anche cambiare protocollo (da svn:// a https://, per esempio) porterà a copie di lavoro rotte e a molti problemi.
  • Non puoi collegare file, solo directory. Questo avrà un grande impatto sull’organizzazione del codice.

Problemi di Directory PHP

Ci sono alcune cose da considerare quando si lavora con directory su un sito web, e in particolare PHP. Per motivi di sicurezza, non vogliamo mettere tutte le sorgenti in una directory accessibile da un browser. Gli unici file che metteremo lì sono file che devono essere chiamati da un browser. Chiamo questi file “file in esecuzione” rispetto ai “file di definizione” che contengono solo definizioni di classi o definizioni di funzioni. Mescolare codice in esecuzione e codice di definizione in un file non è generalmente una buona idea.

Inoltre, per motivi di sicurezza, molti siti hanno un’area riservata che contiene test unitari, pagine di log degli errori, o anche un intero sito di backoffice. Quest’area riservata è solitamente protetta da password dal server web.

Quindi un progetto contiene directory con file di definizione e una directory radice web (spesso www/ o htdocs/) che contiene file in esecuzione e, facoltativamente, un’area riservata.

PHP E Directory Relative

Purtroppo, PHP ha un modo molto controintuitivo di determinare la posizione di un file incluso. I comandi per l’inclusione di un file funzionano tutti rispetto al primo file chiamato, e non rispetto al file corrente. Per rendere le cose ancora peggiori, la posizione del file corrente viene utilizzata quando la posizione originale non porta a un file esistente.

Questo suona complicato, ed è, quindi ecco un esempio:

Supponiamo che tu chiami una pagina “index.php”. Questa pagina include una pagina “library/functions.php”. Questa a sua volta include “settings.php”. Ti aspetteresti che “settings.php” venga cercato nella directory library, ma non è così. Viene cercato nella stessa directory di “index.php”!

Come detto sopra, PHP continua a cercare nella directory prevista se il file non viene trovato. Quindi tutto sembra funzionare come ti aspetti, fino a quando non incontri file con lo stesso nome in directory diverse. Allora avrai davvero difficoltà a capire perché PHP “improvvisamente” sceglie il file dalla directory sbagliata.

Questo significa che non puoi includere in modo sicuro un altro file con percorsi relativi. Dobbiamo renderli assoluti con codice come:

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

Non dimenticare lo ‘/‘ all’inizio del percorso relativo, poiché la funzione dirname restituisce percorsi senza barre finali.

Organizzazione Del Nostro Repository

Ci sono alcune cose da considerare per il codice del progetto. Per lo sviluppo, è conveniente avere l’intero progetto controllato come un tutto. Ma per un server live, questo potrebbe non essere ciò che desideri. Potresti voler controllare il codice del database (script SQL) in una directory lontana dalle directory web, magari anche su un altro server. Potrebbero esserci parti del progetto che non vuoi affatto su un server, ma sono necessarie nello sviluppo, come i file di documentazione.

Inoltre, vogliamo un luogo centrale in cui memorizzare il codice condiviso. Potremmo teoricamente “prendere in prestito” codice da un altro progetto, ma sarebbe davvero difficile seguire quali progetti dipendono da quali altri progetti. Invece, sposteremo qualsiasi codice standard in una posizione centrale.

Directory Radice

La posizione centrale contenente tutto il codice standard sarà “/standard/“. Questa directory sarà ovviamente suddivisa nelle librerie standard. I progetti saranno nella cartella “/projects/“, che può essere suddivisa per cliente e progetto, ad esempio. Il codice esterno, come librerie scaricate come PHPMailer o FPDF, sarà messo in “/external/“.

Rami E Tag

È buona prassi in un repository subversion mantenere il tuo codice in un ramo chiamato “trunk” e creare altri rami allo stesso livello se necessario. Anche se non vuoi ancora alcun ramo, crea una directory “trunk” direttamente sotto un progetto. Trunk è il ramo attivo.

Pensa a questo. Quando colleghiamo al trunk di una libreria standard, colleghiamo al ramo attivo. Questo significa che tutte le correzioni di errori nella libreria collegata verranno aggiornate ogni volta che aggiorniamo una copia di lavoro. Ma se introduciamo un errore, questo verrà aggiornato anche nelle nostre copie di lavoro. Anche quella su un server web live! Potresti voler collegarti a un ramo più o meno stabile invece, ma correggere errori richiederà un po’ più di overhead.

Qualunque collegamento tu scelga, è bene sapere che puoi sempre cambiare in seguito.

Cosa C’è In Un Componente O Progetto

Ci sono molte cose che vogliamo mettere in un repository, e non vogliamo tutto nello stesso posto sul nostro server web. Alcune cose è meglio non metterle affatto su un server web o potrebbero essere controllate su un server diverso, come un server di database.

Nota che la configurazione di una copia di lavoro su una macchina di sviluppo differisce da quella su un server. Su una macchina di sviluppo, probabilmente avrai una directory radice centrale per tutti i tuoi progetti. Questa directory è quindi configurata come accessibile tramite il tuo server web localhost, così non devi riconfigurare il tuo server per ogni progetto su cui stai lavorando. Su un server live, le cose devono essere comunque configurate e considerazioni di sicurezza ci portano a controllare solo quei file che sono necessari e nient’altro. Per cominciare, creiamo le seguenti directory in ogni componente (se necessario):

documentation/Input del cliente, schemi di database e oggetti, ecc. Non è necessario mettere questo su un server web, ma è molto utile per gli sviluppatori.sql/Script di creazione, aggiornamento e conversione del database. per il checkout sul server di database.code/Il codice dell’applicazione effettivatest/Test unitari

Queste directory appaiono grossomodo le stesse nei progetti, con la differenza che i test unitari dovrebbero essere eseguibili e sono quindi parte della sezione codice. Se non vuoi che i test unitari siano presenti su un server live, puoi tenerli separati. Per i progetti, la mia configurazione sarà:

documentation/Come sopra. Contiene anche riferimenti dai componenti utilizzati.database/Contiene script di creazione del database e riferimenti dalle directory sql dei componenti utilizzati.web/Il codice dell’applicazione definente.web/htdocs/La radice del sito con il codice dell’applicazione in esecuzione effettivo, pagine HTML e altri contenuti web, come fogli di stile e immagini.web/htdocs/restricted/Area riservata.web/test/Test unitari. Contiene riferimenti dai test dei componenti utilizzati.selenium/Test funzionali (vedi

http://selenium.openqa.org/).

Codice In Esecuzione E Codice Di Definizione (Ancora)

Potremmo mettere codice in esecuzione e codice di definizione in directory separate, in modo da poter controllare il codice in esecuzione in una directory separata all’interno della radice web. Tuttavia, questo significa che la sottodirectory contenente quel codice diventerebbe parte dell’URL (come www.example.com/restricted/errorhandling/viewerrorlog.php), e questo potrebbe non essere ciò che desideri (potresti voler www.example.com/restricted/viewerrorlog.php). C’è una soluzione a questo. Possiamo mettere i file in esecuzione in un luogo non accessibile e mettere una sorta di proxy in un luogo accessibile. Questo proxy è un file PHP con nient’altro che un’istruzione include o require che punta al file nella posizione non accessibile:

/htdocs/restricted/viewerrorlog.php
// È un proxy che punta a /errorhandling/viewerrorlog.php, che non può essere chiamato direttamente da un browser.
// (perché è al di fuori della radice web)
require(dirname(__FILE__) . '/../../errorhandling/viewerrorlog.php');
?>

La directory errorhandling è quindi condivisa dalla libreria standard e contiene file di classe “definenti” per gestire gli errori e un file “in esecuzione” per visualizzarli.

Impostazioni Dipendenti Dalla Macchina

I file di impostazione sono un po’ insoliti in un repository. Vuoi averli in un repository, ma non vuoi che vengano aggiornati automaticamente. Uso un metodo intermedio per i miei file di impostazione: creo un file chiamato settings_example.php nella directory delle impostazioni. Questo file contiene tutte le impostazioni con commenti su come impostarle per diverse macchine. Contiene anche un commento che dice che devi copiarlo in un file chiamato “settings.php”

Faccio solo riferimento a settings.php da altri file, e imposto una proprietà svn:ignore con il valore di “settings.php” sulla directory delle impostazioni. Questo significa che una copia di lavoro non funzionerà “out of the box”, ma non lo farà comunque a causa della dipendenza dalle impostazioni dipendenti dalla macchina. La proprietà svn:ignore impedisce che le impostazioni di macchine diverse sovrascrivano le tue durante un aggiornamento.

Metterlo In Pratica

Un esempio. Supponiamo di avere un progetto che deve inviare email e creare file PDF. Abbiamo deciso di utilizzare PHPMailer di PEAR e le librerie FPDF. Per evitare di imporre restrizioni speciali nella configurazione di PHP, scarichiamo semplicemente PHPMailer e non utilizziamo il metodo “pear install” per installarlo.

Inoltre, questo progetto avrà una gestione degli errori standard e una classe database standard. Per semplicità, suppongo che tu parta da un repository vuoto.

Creare Le Directory Necessarie

Per prima cosa, crea le directory radice descritte sopra: /external/, /standard/ e /projects/. Ti consiglio di utilizzare un’interfaccia grafica per subversion, come TortoiseSVN o RapidSVN.

Ci occuperemo prima delle librerie esterne. Scarica e decomprimi PHPMailer e FPDF, e importali in /external/phpmailer/trunk/ e /external/fpdf/trunk/ rispettivamente. Se vuoi, puoi creare rami “firstdownload” da entrambi.

Successivamente, creiamo un progetto su cui lavorare. Questo progetto è per il cliente “CustomerInc” e il progetto si chiama “SamplePrj”. Sono sicuro che puoi trovare nomi migliori per i tuoi progetti. Quindi creiamo il percorso completo di /projects/CustomerInc/SamplePrj/trunk/. Il “trunk” è il ramo principale: potresti lavorare in altri rami (di sviluppo), ma vengono eventualmente fusi nel trunk. Se stai lavorando nel trunk (e perché non dovresti se è un nuovo progetto), questa directory è la directory da controllare come copia di lavoro. Ma aspettiamo con questo. Sotto il trunk, creiamo directory “di scopo” per il codice web (php, html, ecc.), codice del database e documentazione.
Sotto la directory web, ho creato una radice web (htdocs) e una directory per file php specifici del progetto generico.

Creare Le Condivisioni

Fino ad ora, ho fatto tutto direttamente sul repository. Ma per condividere codice, dobbiamo prima fare una copia di lavoro. Quindi facciamolo.

Poiché il nostro server web localhost deve essere in grado di raggiungere tutto il nostro codice di progetto, faccio la mia copia di lavoro su una cartella radice del mio filesystem (/projects//) o disco rigido (C:\projects\\). Altrimenti, il server web deve avere diritti di accesso nella home directory di tutti gli utenti che utilizzano subversion. Utilizzando una cartella radice, puoi assicurarti che il server web possa accedervi tutti, e puoi persino separare directory per utenti diversi. Per i sistemi basati su unix, tutti gli utenti di subversion dovrebbero essere membri del gruppo del tuo server web, e la directory totale dei progetti dovrebbe essere almeno leggibile dal gruppo. Almeno le cose web, cioè.

Nella nostra copia di lavoro, abbiamo la directory web, dove si trova la directory htdocs e dove vogliamo i collegamenti a FPDF e PHPMailer. Per creare quei collegamenti, aggiungi una proprietà subversion svn:external alla directory web con il seguente valore:

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

(sì, il valore è composto da 2 righe) e poi aggiorna la tua copia di lavoro. Dovresti ora ottenere due directory extra con il tuo aggiornamento, come mostrato in questo screenshot di una copia di lavoro del nostro progetto.

Nota: ho creato i collegamenti al trunk. Potresti collegarti a un ramo invece o collegarti a un numero di revisione specifico. Ho creato solo riferimenti esterni alle sezioni di codice. Probabilmente vorresti creare riferimenti esterni alla documentazione delle librerie esterne anche.

Spostare Codice Utile Nella Libreria Standard

Man mano che i tuoi progetti maturano, sviluppi sempre più codice che sarebbe utile in altri progetti. Nel nostro caso, diciamo che abbiamo sviluppato un pacchetto di gestione degli errori e una classe database. Ovviamente, il primo passo è rimuovere eventuali dipendenze di progetto da questo codice. Metti il codice da condividere (chiamiamolo un pacchetto) in una directory separata, poiché deve essere in una directory separata quando viene condiviso dalla libreria standard. Quindi ci assicuriamo che il codice generico di gestione degli errori sia in una directory errorhandling e la classe database sia in una directory database, entrambe direttamente sotto la directory web.

Puoi usare i comandi svn mkdir e svn move o qualsiasi client grafico di subversion per spostare il pacchetto nel tuo progetto in /standard//trunk/code. Non dimenticare di aggiornare la tua copia di lavoro di sviluppo dopo questo (vedrai scomparire la directory del pacchetto), quindi aggiungi due righe alla proprietà svn:external (nella forma /standard//trunk/code), aggiorna (per ottenere di nuovo la directory, ora dalla libreria standard) e commetti la modifica della proprietà. La maggior parte dei problemi si trova nell’aggiornare le tue copie di lavoro, specialmente se quella copia di lavoro è un sito web live per il quale desideri il minor tempo di inattività possibile.
Se aggiorni le tue copie di lavoro dopo ogni modifica sul server, nulla può andare storto. Il problema sorge quando hai una copia di lavoro che ha ancora il pacchetto nella posizione originale del progetto e vuoi aggiornare a uno stato in cui quella stessa directory ora proviene da un riferimento svn:externals. Se provi a farlo, riceverai un errore che dice che la directory di destinazione per quel riferimento esterno esiste già, quindi il riferimento esterno non può essere creato. Questo è facilmente risolvibile rimuovendo manualmente quella directory prima di aggiornare quella copia di lavoro.

Sposta eventuali test unitari, documentazione, script SQL, ecc. in modo simile nella libreria standard.

Aggiornare Il Server Live

Quando più di una persona ha accesso al server web o al server di database, è meglio utilizzare un account per tutti loro. Questo assicura che le directory .svn non soffrano di impostazioni di account e diritti utente in conflitto. Inoltre, puoi negare a quell’account i permessi di scrittura sul repository, in modo che un server web compromesso non possa facilmente commettere cose brutte.

Su un server web Linux, i seguenti diritti hanno senso: lettura e scrittura per l’utente che aggiorna la copia di lavoro “live”, diritti di lettura per il gruppo (che è il gruppo del server web) e nessun diritto per gli altri. Se imposti il bit SGID sulle directory, tutti i file aggiunti saranno anche accessibili al server web. Puoi impostare il bit SGID con chmod g+s . Considera di impostare il umask a 0027 per impostare i permessi giusti per i file aggiunti. Una directory htdocs appena aggiunta potrebbe quindi apparire come:

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

Codice Database Modulare

Abbiamo già discusso di come funzionano gli include in PHP, ma come includiamo file in SQL? Dobbiamo essere in grado di includere file diversi da directory diverse nel codice SQL, poiché abbiamo organizzato il nostro repository in questo modo. Se condividiamo codice PHP, dovremmo condividere anche il corrispondente codice SQL. Ho scritto un piccolo script PHP e un piccolo script python per farlo. Puoi trovarli su http://www.w-p.dds.nl/sqlincludeparser_php.txt (PHP) e http://www.w-p.dds.nl/sqlincludeparser_py.txt (Python). Questi script ti consentono di definire istruzioni include in un file base della forma:

CREATE DATABASE IF NOT EXISTS someDatabase;
USE someDatabase;
-- @include(errorhandling/errortables.sql) # (re)crea tabelle utilizzate dal pacchetto di gestione degli errori
DROP TABLE IF EXISTS someTable;
CREATE TABLE someTable
      (someID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
...ecc.

Se rendi ogni file incluso e il file base ripetibile, puoi usarlo per tornare a uno stato pulito più e più volte, il che può essere molto utile per testare e sviluppare. Per usarlo, basta canalizzare l’output del mio script al client della riga di comando sql:

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

Ricevi i nuovi post nella tua casella di posta.

Nessuno spam. Disiscriviti in qualsiasi momento.