Rewrite the standalone bootstrap protocol. Deprecate civicrm.settings.php.
tldr
Rewrite Bootstrap.php
to support CMS-first -- and make civicrm.settings.php
unnecessary.
Background: Problem
CiviCRM has many entry-points -- specific examples would be bin/cli.php
, extern/open.php
, extern/url.php
, extern/rest.php
, or extern/cxn.php
. More broadly, all test suites and CLI processes use a standalone bootstrap script. (These entry-points are distinct from the front-end entry-points typically associated with the UI.)
There are a number of technical details and happenstance we can go into, but there are two fundamental nubs in writing and executing a standalone script:
- It doesn't know which CMS is active. There's usually some hard-coding to the find the CMS.
- Bootstrapping the CMS adds overhead. Many admins have written-off this consideration as cost of doing business - but for someone, somewhere, at some point it mattered.
civicrm.config.php
protocol
Background: The original protocol for standalone bootstrap originated many years ago (circa 1.x or 2.x?). It's still used in several cases. It's key design elements:
-
civicrm.config.php
is generated at build-time to match the CMS. The tarball/zipball for each CMS includes a different build of this file. It's main purpose is to locatecivicrm.settings.php
. -
civicrm.settings.php
includes adefine
for theCIVICRM_UF
(among many others). -
CRM_Utils_System::*
includes functions likeloadBootStrap()
. -
Original protocol: A standalone script would include
civicrm.config.php
, which would locate thecivicrm.settings.php
using a CMS-specific search. It then boots Civi by loadingcivicrm.settings.php
and callingCRM_Core_Config::singleton()
. Optionally, it also boots the CMS by callingCRM_Utils_System_*::loadBootStrap()
.
The original boot protocol has some major consequences -- for example:
- It requires creating an extra settings file (
civicrm.settings.php
) on every build. - Depending on how the page-load starts, the Civi settings file and CMS settings may load in different orders. It's not valid to infer Civi settings based on CMS settings (and vice-versa).
- You need to figure the absolute or relative path of
civicrm.config.php
. This is possible incivicrm-core
-- but it's basically impossible with any other publishable deliverable. - Reading/understanding the boot protocol requires having multiple builds of Civi (one for each CMS) -- *that's the only way to see each version of
civicrm.config.php
.
cv
bootstrap protocol
Background: A few years ago, I combined the various civicrm.config.php
files with the aim of producing a universal boot script that solved #3
and #4
. This produced https://github.com/civicrm/cv/blob/master/src/Bootstrap.php which eventually became the heart of the cv
command. This endeavor preserved a bunch of assumptions from the original protocol... but it made a key change: dynamically discovering the boot info.
The dynamic discovery works a bit like this:
- Do a file-system search (from PWD) to figure out the CMS.
- Do a file-system search (from CMS dir) to figure out where
civicrm.settings.php
is. - Unless... you really want a faster bootstrap. Then you can set an environment variable with your boot info (and bypass the searches).
IMHO, dynamic CMS discovery (with optional env-var for optimization) is a pretty good way to make the code more robust to deploying in a variety of environments and file-structures.
There's just one problem: the implementation grew out of civicrm.config.php
and kept some of the assumptions, so the implementation still shares issues #1
and #2
.
Proposal: Provide a universal, CMS-first bootstrap script -- and knock over some dominoes
The basic idea is to write a CLI helper which bootstraps the CMS first -- and then bootstraps Civi. The logic is roughly:
function boot($startDir) {
$parentDirectories = array($startDir, parent($startDir), grandparent($startDir), ...);
foreach ($parentDirectories as $dir)
if ($dir has Drupal) { bootDrupal(); bootCivi(); return; }
elseif ($dir has Joomla) { bootJoomla(); bootCivi(); return; }
elseif ($dir has WordPress) { bootWordPress(); bootCivi(); return; }
elseif ($dir has Backdrop) { bootBackdrop(); bootCivi(); return; }
}
}
This helper still provides standalone bootstrap, so we can still have all our standalone scripts/unit-tests/commands/etc. But it makes a fundamental change:
- You can always rely on having the CMS bootstrapped first.
- Therefore, you can detect things like
CIVICRM_BASEURL
using CMS API's -- because they're always available. - Therefore, you don't need
civicrm.settings.php
to store those settings. - Therefore, you don't need
install/index.php
to createcivicrm.settings.php
. - Therefore, you don't need write-access to create
civicrm.settings.php
(in the typical case). - Therefore, you don't need to migrate or preserve
civicrm.settings.php
(in the typical case).
Note: Of course, the entire CiviCRM install-base currently uses civicrm.settings.php
, and some of them use it to accomplish special ends (like custom multidomain/multisite/multitenant arrangements). Therefore, I wouldn't argue for complete removal of the file -- but, instead, it should be an optional/advanced thing.
General Tasks
- Write the new bootstrap script.
- Phase-in the new bootstrap script for use by
cv
andcivicrm-core
. - Update
civicrm-core
to compute values for all those constants (define('CIVICRM_FOO',...)
) at runtime using CMS functions. - Update the installer to omit
civicrm.settings.php
- Update docs to describe the advanced/optional use of
civicrm.settings.php
.