Keeping your application code portable between environments
Developing an application with as less hard dependencies as possible is something you want to strive for.
One of those hard dependencies are hardcoded paths.
Therefore it's a good practice to keep this in mind while setting up and developing a project for multiple reasons:
- You can re-use (parts of) scripts and code for multiple environments.
- Using relative paths also allows the support team and engineers of the Dropsolid to move an environment of a project to a new location without the need for code changes, which may be necessary during maintenance or scaling tasks.
This article will explain which common places exist where hardcoded paths are most likely used, and how you can convert them to a relative path.
In bash scripts used for builds / updates / crons / ...¶
The snipped below can be used as example to make a script portable and relative to the application. It also doesn't matter where you execute the script (in the folder itself, in the application root, ...), all paths are determined based on the location of the script itself.
The only change that may be needed is to determine where the script directory is positioned relative to the application root.
The snippet below is an example from a script in the bash/updates/ folder. So we need to go 2 levels up to reach the application's main directory (see the explanation below).
SCRIPT_DIR="$(cd -- "$(dirname "$0")"; pwd -P)"
APPLICATION_DIR=${SCRIPT_DIR%/*/*}
DOCROOT="$APPLICATION_DIR/docroot"
cd $DOCROOT
Explanation:
- line 1: determine the full non-symlinked absolute directory of the current script, independent of the directory where the script is executed.
- line 2: determine the path 2 levels up (2 stars), as we know that's the project root.
- line 3: make other helper variables, e.g. the docroot.
Be aware that following snippet in a script will use the current directory as starting point
cd ../../
So the active directory will be changed 2 directories up of the current directory, and not 2 directories up of the directory of the script.
In the application code¶
The following example is based on Drupal, but the same principle can be used in other frameworks, CMS'es, ...
Drupal has, depending on the core version, multiple ways to determine the root path of your application:
Drupal 7:¶
Use the DRUPAL_ROOT
global constant.
It is initialised at the beginning of a request, so it's useable in custom code, settings.php, install logic,...
See the Drupal API documentation.
Drupal 8:¶
Since Drupal 8 the usage of the global constant like DRUPAL_ROOT
is discouraged, and are replaced by container services (or a fallback for non-container aware code).
See the change record in this topic for more info and how to use the container services (or a fallback for non-container aware code).
In settings.php
Use the $app_root
variable.
(there are more variables by default available in the settings.php files, see the source code).
An example is present in the default.settings.php file in Drupal core:
$settings['container_yamls'][] = $app_root . '/' . $site_path . '/services.yml';$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/environments/live.services.yml';
Drupal 9+:¶
Since Drupal 9, the container services for app.root
and site.path
are deprecated, and are replaced by container parameters to be compatible with Symfony 5.
See the change record for more info and how to use the container parameters in service definitions.
The change record doesn't contain another difference with Drupal 8:
$container->get('app.root')
should be changed in $container->getParameter('app.root')
See the same example from the Drupal 8 example, adapted for Drupal 9:
class Example implements ContainerInjectionInterface {
public function __construct($root, EntityManagerInterface $entity_manager) {
$this->root = $root;
$this->entityManager = $entity_manager;
}
public function create(ContainerInterface $container) {
return new static($container->getParameter('app.root'), $container->get('entity.manager'));
}
public function example() {
file_scan_directory($this->root);
}
In settings.php
Use the $app_root
variable.
(there are more variables by default available in the settings.php files, see the source code).
An example is present in the default.settings.php file in Drupal core:
$settings['container_yamls'][] = $app_root . '/' . $site_path . '/services.yml';$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/environments/live.services.yml';