Subversion PHP · 15 min read · Jan 19, 2026
Configuración de un Repositorio Modular de Subversión para Sitios Web Impulsados por PHP
Configuración de un Repositorio Modular de Subversión para Sitios Web Impulsados por PHP
Willem Bogaerts - Kratz Business Solutions
Resumen
Compartir código entre proyectos sigue siendo un asunto no trivial con subversión. Especialmente si estás familiarizado con SourceSafe, encontrarás que subversión dificulta compartir código. Subversión parece ser realmente excelente en crear un desorden de versiones y bueno en resolver uno, pero la razón por la que necesito control de código fuente es para prevenir tal desorden. Aquí es donde subversión puede mejorar mucho, pero no es imposible. Este cómo hacerlo demostrará una configuración de directorios que toma en cuenta el mecanismo de compartición de subversión, así como otros problemas que traen los repositorios.
Convención en Este Cómo Hacer
Verás
Se asume que sabes qué es subversión, que conoces su uso básico y que tienes o puedes crear un repositorio.
Este cómo hacerlo no te da “la mejor” manera de organizar un repositorio, porque depende de tus necesidades. Su objetivo es ayudar mostrando algunas de esas decisiones y cómo afectan la estructura del repositorio.
La Manera de Subversión de Compartir Código
Los directorios en una copia de trabajo pueden contener enlaces a otros repositorios definiendo una propiedad svn:externals. Esto hará que el directorio del repositorio enlazado se incluya en tu copia de trabajo, pero no lo hará parte del proyecto en sí. Esto significa que verás el directorio con todos los archivos en tu copia de trabajo, pero no en el repositorio central. Solo puedes ver la propiedad en el repositorio central.
Hay algunas desventajas en esta forma de compartir:
- Solo puedes dar URLs absolutas para los enlaces, por lo que migrar un repositorio sin problemas es casi imposible. Incluso cambiar protocolos (de svn:// a https://, por ejemplo) traerá copias de trabajo rotas y muchos problemas.
- No puedes enlazar archivos, solo directorios. Esto tendrá un gran impacto en la organización del código.
Problemas de Directorios PHP
Hay algunas cosas en las que pensar al trabajar con directorios en un sitio web, y en particular PHP. Como cuestión de seguridad, no queremos poner todas las fuentes en un directorio que sea accesible desde un navegador. Los únicos archivos que pondremos allí son archivos que necesitan ser llamados por un navegador. Yo llamo a estos archivos “archivos en ejecución” en contraposición a “archivos de definición” que solo tienen definiciones de clases o definiciones de funciones dentro de ellos. Mezclar código en ejecución y código de definición en un solo archivo generalmente no es una buena idea.
También como cuestión de seguridad, muchos sitios tienen un área restringida que contiene pruebas unitarias, páginas de registro de errores, o incluso un sitio de backoffice completo. Esta área restringida generalmente está protegida por contraseña por el servidor web.
Así que un proyecto contiene directorios con archivos de definición y un directorio raíz web (a menudo www/ o htdocs/) que contiene archivos en ejecución y opcionalmente un área restringida.
PHP y Directorios Relativos
Lamentablemente, PHP tiene una manera muy contraintuitiva de determinar la ubicación de un archivo incluido. Los comandos para la inclusión de un archivo funcionan todos con respecto al primer archivo llamado, y no con respecto al archivo actual. Para empeorar las cosas, la ubicación del archivo actual se utiliza cuando la ubicación original no conduce a un archivo existente.
Esto suena complicado, y lo es, así que aquí hay un ejemplo:
Supón que llamas a una página “index.php”. Esta página incluye una página “library/functions.php”. Esta a su vez incluye “settings.php”. Podrías sospechar que “settings.php” se busca en el directorio de la biblioteca, ¡pero no es así! Se busca en el mismo directorio que “index.php”.
Como se dijo anteriormente, PHP continúa buscando en el directorio esperado si el archivo no se encuentra. Así que todo parece funcionar como esperas, hasta que te encuentras con archivos con el mismo nombre en diferentes directorios. Entonces tendrás un gran problema para averiguar por qué PHP “de repente” elige el archivo del directorio incorrecto.
Esto significa que no puedes incluir de manera segura otro archivo con rutas relativas. Debemos hacerlas absolutas con código como:
require_once(dirname(__FILE__) . '/library/functions.php');No olvides la ‘/‘ al inicio de la ruta relativa, ya que la función dirname devuelve rutas sin barras finales.
Organización de Nuestro Repositorio
Hay algunas cosas a considerar para el código del proyecto. Para el desarrollo, es conveniente tener todo el proyecto revisado como un todo. Pero para un servidor en vivo, esto puede no ser lo que deseas. Puede que desees revisar el código de la base de datos (scripts SQL) en un directorio que esté lejos de los directorios web, tal vez incluso en otro servidor. Puede haber partes del proyecto que no quieras en absoluto en un servidor, pero que son necesarias en desarrollo, como archivos de documentación.
Además, queremos un lugar central para almacenar el código compartido. Teóricamente podríamos “tomar prestado” código de otro proyecto, pero sería realmente difícil seguir qué proyectos dependen de cuáles otros proyectos. En su lugar, moveremos cualquier código estándar a una ubicación central.
Directorios Raíz
La ubicación central que contiene todo el código estándar será “
Ramas y Etiquetas
Es una buena práctica en un repositorio de subversión mantener tu código en una rama llamada “trunk” y crear otras ramas al mismo nivel si es necesario. Incluso si no deseas ninguna rama aún, crea un directorio “trunk” directamente debajo de un proyecto. Trunk es la rama activa.
Piensa en esto. Cuando enlazamos a trunk de una biblioteca estándar, enlazamos a la rama activa. Esto significa que todas las correcciones de errores en la biblioteca enlazada se actualizarán cada vez que actualicemos una copia de trabajo. Pero si introducimos un error, esto también se actualizará en nuestras copias de trabajo. ¡Incluso en la que está en un servidor web en vivo! Puede que desees enlazar a una rama más o menos estable en su lugar, pero corregir errores requerirá un poco más de sobrecarga.
Cualquiera que sea el enlace que elijas, es bueno saber que siempre puedes cambiar más tarde.
Qué Hay en un Componente o Proyecto
Hay muchas cosas que queremos poner en un repositorio, y no queremos que todo esté en el mismo lugar en nuestro servidor web. Algunas cosas es mejor no ponerlas en un servidor web en absoluto o pueden ser revisadas en un servidor diferente, como un servidor de base de datos.
Ten en cuenta que la configuración de una copia de trabajo en una máquina de desarrollo difiere de la de un servidor. En una máquina de desarrollo, probablemente tendrás un directorio raíz central para todos tus proyectos. Este directorio se configura como accesible a través de tu servidor web localhost, por lo que no tienes que reconfigurar tu servidor para cada proyecto en el que estás trabajando. En un servidor en vivo, las cosas deben configurarse de todos modos y las consideraciones de seguridad nos llevan a revisar solo aquellos archivos que son necesarios y nada más. Para empezar, hacemos los siguientes directorios en cada componente (si es necesario):
documentation/Entrada del cliente, esquemas de base de datos y objetos, etc. No es necesario poner esto en un servidor web, pero es muy útil para los desarrolladores.sql/Creación de base de datos, scripts de actualización y conversión. para revisión en el servidor de base de datos.code/El código de la aplicación realtest/Pruebas unitarias
Estos directorios aparecen aproximadamente igual en los proyectos, con la diferencia de que las pruebas unitarias deben ser ejecutables y, por lo tanto, son parte de la sección de código. Si no deseas que las pruebas unitarias estén presentes en un servidor en vivo, puedes mantenerlas separadas. Para proyectos, mi configuración será:
documentation/Como arriba. También contiene referencias de componentes utilizados.database/Contiene scripts de creación de base de datos y referencias de
sql directorios de componentes utilizados.web/El código de aplicación definitorio.web/htdocs/La raíz del sitio con el código de aplicación en ejecución real, páginas HTML y otro contenido web, como hojas de estilo e imágenes.web/htdocs/restricted/Área restringida.web/test/Pruebas unitarias. Contiene referencias de las pruebas de los componentes utilizados.selenium/Pruebas funcionales (ver
http://selenium.openqa.org/ ).
Código en Ejecución y Código de Definición (Otra Vez)
Podríamos poner el código en ejecución y el código de definición en directorios separados, de modo que pudiéramos revisar el código en ejecución en un directorio separado dentro de la raíz web. Sin embargo, esto significa que el subdirectorio que contiene ese código se convertiría en parte de la URL (como www.example.com/restricted/errorhandling/viewerrorlog.php), y eso puede no ser lo que deseas (puedes querer www.example.com/restricted/viewerrorlog.php). Hay una solución para esto. Podemos poner los archivos en ejecución en un lugar no accesible y poner una especie de proxy en un lugar accesible. Este proxy es un archivo PHP con nada más que una declaración include o require que apunta al archivo en la ubicación no accesible:
/htdocs/restricted/viewerrorlog.php
// Es un proxy que apunta a /errorhandling/viewerrorlog.php, que no puede ser llamado directamente por un navegador.
// (porque está fuera de la raíz web)
require(dirname(__FILE__) . '/../../errorhandling/viewerrorlog.php');
?> El directorio errorhandling se comparte desde la biblioteca estándar y contiene archivos de clase de “definición” para manejar errores y un archivo “en ejecución” para verlos.
Configuraciones Dependientes de la Máquina
Los archivos de configuración son un poco inusuales en un repositorio. Quieres tenerlos en un repositorio, pero no quieres que se actualicen automáticamente. Uso un método intermedio para mis archivos de configuración: creo un archivo llamado settings_example.php en el directorio de configuración. Este archivo contiene todas las configuraciones con comentarios sobre cómo configurarlas para diferentes máquinas. También contiene un comentario que dice que debes copiarlo a un archivo llamado “settings.php”.
Solo me refiero a settings.php desde otros archivos, y establezco una propiedad svn:ignore con el valor de “settings.php” en el directorio de configuración. Esto significa que una copia de trabajo no funcionará “fuera de la caja”, pero de todos modos no lo hará debido a la dependencia de configuraciones dependientes de la máquina. La propiedad svn:ignore evita que configuraciones de diferentes máquinas sobrescriban las tuyas en una actualización.
Poniéndolo en Práctica
Un ejemplo. Supongamos que tenemos un proyecto que necesita enviar correos y crear archivos PDF. Hemos decidido que usamos PHPMailer de PEAR y las bibliotecas FPDF. Para evitar imponer restricciones especiales en la configuración de PHP, simplemente descargamos PHPMailer y no usamos el método “pear install” para instalarlo.
Además, este proyecto tendrá manejo de errores estándar y una clase de base de datos estándar. Para simplificar, supongo que comienzas con un repositorio vacío.
Creando los Directorios Necesarios
Primero, crea los directorios raíz descritos anteriormente:
Primero trataremos con las bibliotecas externas. Descarga y descomprime PHPMailer y FPDF, e impórtalos en
A continuación, creamos un proyecto en el que trabajar. Este proyecto es para el cliente “CustomerInc” y el proyecto se llama “SamplePrj”. Estoy seguro de que puedes encontrar mejores nombres para tus proyectos. Así que creamos la ruta completa de
Bajo el directorio web, creé una raíz web (htdocs) y un directorio para archivos php específicos del proyecto genérico.
Creando los Compartidos
Hasta ahora, hice todo directamente en el repositorio. Pero para compartir código, primero tenemos que hacer una copia de trabajo. Así que hagámoslo.
Porque nuestro servidor web localhost tiene que poder alcanzar todo nuestro código de proyecto, hago mi copia de trabajo en una carpeta raíz de mi sistema de archivos (/projects/
En nuestra copia de trabajo, tenemos el directorio web, donde se encuentra el directorio htdocs y donde queremos los enlaces a FPDF y PHPMailer. Para crear esos enlaces, agrega una propiedad de subversión svn:external al directorio web con el siguiente valor:
fpdf /external/fpdf/trunk/code
phpmailer /external/phpmailer/trunk/code ( sí, el valor consiste en 2 líneas) y luego actualiza tu copia de trabajo. Ahora deberías obtener dos directorios adicionales con tu actualización, como se muestra en esta captura de pantalla de una copia de trabajo de nuestro proyecto.
Nota: Creé los enlaces a trunk. Puedes enlazar a una rama en su lugar o enlazar a un número de revisión específico. Solo creé referencias externas a las secciones de código. Probablemente querrías crear referencias externas a la documentación de las bibliotecas externas también.
Moviendo Código Útil a la Biblioteca Estándar
A medida que tus proyectos maduran, desarrollas más y más código que sería útil en otros proyectos. En nuestro caso, digamos que hemos desarrollado un paquete de manejo de errores y una clase de base de datos. Por supuesto, el primer paso es eliminar cualquier dependencia del proyecto de este código. Coloca el código para compartir (llamémoslo un paquete) en un directorio separado, ya que debe estar en un directorio separado cuando se comparta de nuevo desde la biblioteca estándar. Así que nos aseguramos de que el código genérico de manejo de errores esté en un directorio errorhandling y la clase de base de datos esté en un directorio database, ambos directamente bajo el directorio web.
Puedes usar los comandos svn mkdir y svn move o cualquier cliente gráfico de subversión para mover el paquete en tu proyecto a
Si actualizas tus copias de trabajo después de cada cambio en el servidor, nada puede salir mal. El problema surge cuando tienes una copia de trabajo que aún tiene el paquete en la ubicación original del proyecto y deseas actualizar a un estado donde ese mismo directorio ahora proviene de una referencia svn:externals. Si intentas eso, obtendrás un error que dice que el directorio de destino para esa referencia externa ya existe, por lo que no se puede crear la referencia externa. Esto se resuelve fácilmente eliminando ese directorio manualmente antes de actualizar esa copia de trabajo.
Mueve cualquier prueba unitaria, documentación, scripts SQL, etc. de manera similar a la biblioteca estándar.
Actualizando el Servidor en Vivo
Cuando más de una persona tiene acceso al servidor web o de base de datos, es mejor usar una cuenta para todos ellos. Esto asegura que los directorios .svn no sufran de configuraciones de cuenta y derechos de usuario en conflicto. Además, puedes negar a esa cuenta permisos de escritura en el repositorio, por lo que un servidor web comprometido no puede fácilmente cometer cosas desagradables.
En un servidor web Linux, los siguientes derechos tienen sentido: lectura y escritura para el usuario que actualiza la copia de trabajo “en vivo”, derechos de lectura para el grupo (que es el grupo del servidor web) y sin derechos para otros. Si estableces el bit SGID en los directorios, todos los archivos añadidos también serán accesibles para el servidor web. Puedes establecer el bit SGID con 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 Base de Datos Modular
Ya discutimos cómo funcionan las inclusiones en PHP, pero ¿cómo incluimos archivos en SQL? Necesitamos poder incluir diferentes archivos de diferentes directorios en el código SQL, ya que organizamos nuestro repositorio de esta manera. Si compartimos código PHP, también deberíamos compartir el código SQL correspondiente. Escribí un pequeño script en PHP y otro en Python para hacer eso. Puedes encontrarlos en http://www.w-p.dds.nl/sqlincludeparser_php.txt (PHP) y http://www.w-p.dds.nl/sqlincludeparser_py.txt (Python). Estos scripts te permiten definir declaraciones de inclusión en un archivo base de la forma:
CREATE DATABASE IF NOT EXISTS someDatabase;
USE someDatabase;
-- @include(errorhandling/errortables.sql) # (re)crea tablas utilizadas por el paquete de manejo de errores
DROP TABLE IF EXISTS someTable;
CREATE TABLE someTable
(someID INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
...etc.Si haces que cada archivo incluido y el archivo base sean repetibles, puedes usarlo para llegar a un estado limpio una y otra vez, lo cual puede ser muy útil para pruebas y desarrollo. Para usarlo, simplemente canaliza la salida de mi script al cliente de línea de comandos de SQL:
sqlincludeparser.py | mysql -u -p [] Recibe nuevas publicaciones en tu bandeja de entrada.
No spam. Cancela la suscripción en cualquier momento.