package_manager.api.php
Documentation related to Package Manager.
File
-
core/
modules/ package_manager/ package_manager.api.php
View source
<?php
/**
* @file
* Documentation related to Package Manager.
*/
/**
* @defgroup package_manager_architecture Package Manager architecture
* @{
*
* @section sec_overview Overview
* Package Manager is an API-only module which provides the scaffolding and
* functionality needed for Drupal to make changes to its own running code base
* via Composer. It doesn't have a user interface.
*
* @see https://getcomposer.org/
* @see https://github.com/php-tuf/composer-stager
*
* @section sec_concepts Concepts
* At the center of Package Manager is the concept of a stage directory. A
* stage directory is a complete copy of the active Drupal code base, created
* in a temporary directory that isn't accessible over the web. The stage
* directory doesn't have any site-specific assets like settings.php, uploaded
* files, or SQLite databases.
*
* Only one stage directory can exist at any given time, and it is "owned" by
* the user or session that originally created it. Only the owner can perform
* operations on the stage directory, and only using the same class (i.e.,
* \Drupal\package_manager\StageBase or a subclass) they used to create it.
*
* Package Manager can run Composer commands in the stage directory to require
* or update packages in it, and then copy those changes back into the live,
* running code base (which is referred to as the "active directory"). The
* stage directory can then be safely deleted. Four distinct operations:
* create, require, apply, and destroy. They comprise the "stage life cycle."
*
* Package Manager's \Drupal\package_manager\StageBase controls the stage life
* cycle and is an abstract class that must be subclassed. Most of the time,
* there should be little need to heavily customize a StageBase subclass;
* custom code should generally use the event system to interact with the stage.
*
* @see sec_stage_events Stage API: Events
* Events are dispatched before and after each operation in the stage life
* cycle. There are two types of events: pre-operation and post-operation.
* Pre-operation event subscribers can analyze the state of the stage directory,
* or the system at large, and flag errors if they detect any problems. If
* errors are flagged, the operation is prevented. Therefore, pre-operation
* events are helpful to ensure that the stage directory is in a valid state.
* Post-operation events are simple triggers allowing custom code to react when
* an operation is successfully completed. They cannot flag errors to block
* stage operations (although they can use the core messenger and logging
* systems as needed).
*
* All stage events extend \Drupal\package_manager\Event\StageEvent, and all
* pre-operation events extend
* \Drupal\package_manager\Event\PreOperationStageEvent. All events have a
* $stage property which allows access to the stage object itself.
*
* The stage dispatches the following events during its life cycle:
*
* - \Drupal\package_manager\Event\PreCreateEvent
* Dispatched before the stage directory is created. At this point, the
* stage will have recorded which user or session owns it, so another stage
* directory cannot be created until the current one is destroyed. If
* subscribers flag errors during this event, the stage will release its
* ownership. This is the earliest possible time to detect problems that might
* prevent the stage from completing its life cycle successfully. This event
* is dispatched only once during the stage life cycle.
* @see sec_stage_exceptions
*
* - \Drupal\package_manager\Event\PostCreateEvent
* Dispatched after the stage directory has been created, which means that the
* running Drupal code base has been copied into a separate, temporary
* location. This event is dispatched only once during the stage life cycle.
*
* - \Drupal\package_manager\Event\PreRequireEvent
* Dispatched before one or more Composer packages are required into the
* stage directory. This event may be dispatched multiple times during the
* stage life cycle, and receives a list of the packages which are about to
* be required into the stage directory. The list of packages CANNOT be
* altered by subscribers.
*
* - \Drupal\package_manager\Event\PostRequireEvent
* Dispatched after one or more Composer packages have been added to the
* stage directory. This event may be dispatched multiple times during the
* stage life cycle, and receives a list of the packages which were required
* into the stage directory. (Note that this is a list of packages which
* were specifically *asked for*, not the full list of packages and
* dependencies that was actually installed.)
*
* - \Drupal\package_manager\Event\PreApplyEvent
* Dispatched before changes in the stage directory (i.e., new and/or updated
* packages) are copied to the active directory. This is the final opportunity
* for event subscribers to flag errors before the active directory is
* modified, because once that has happened, the changes cannot be undone.
* This event may be dispatched multiple times during the stage life cycle.
*
* - \Drupal\package_manager\Event\PostApplyEvent
* Dispatched after changes in the stage directory have been copied to the
* active directory. It should only be used for cleaning up after other
* operations that happened during the stage life cycle. For example, a
* PostCreateEvent subscriber might have set a state value which is no longer
* needed once the stage has been applied to the active directory -- in such a
* case, a PostApplyEvent subscriber should delete that value.
* `drupal_flush_all_caches()` is called just before this event is dispatched,
* so subscribers shouldn't need to flush any caches or rebuild the service
* container. This event may be dispatched multiple times during the stage
* life cycle, and should *never* be used for schema changes (i.e., operations
* that should happen in `hook_update_N()` or a post-update function).
*
* @section sec_stage_api Stage API: Public methods
* The public API of any stage consists of the following methods:
*
* - \Drupal\package_manager\StageBase::create()
* Creates the stage directory, records ownership, and dispatches pre- and
* post-create events. Returns a unique token which calling code must use to
* verify stage ownership before performing operations on the stage
* directory in subsequent requests (when the stage directory is created,
* its ownership is automatically verified for the duration of the current
* request). See \Drupal\package_manager\StageBase::claim() for more
* information.
*
* - \Drupal\package_manager\StageBase::require()
* Adds and/or updates packages in the stage directory and dispatches pre-
* and post-require events. The stage must be claimed by its owner to call
* this method.
*
* - \Drupal\package_manager\StageBase::apply()
* Copies changes from the stage directory into the active directory, and
* dispatches the pre-apply event. The stage must be claimed by its owner to
* call this method.
*
* - \Drupal\package_manager\StageBase::postApply()
* Performs post-apply tasks after changes have been copied from the stage
* directory. This method should be called as soon as possible in a new
* request because the code on disk may no longer match what has been loaded
* into PHP's runtime memory. This method clears all Drupal caches, rebuilds
* the service container, and dispatches the post-apply event. The stage must
* be claimed by its owner to call this method.
*
* - \Drupal\package_manager\StageBase::destroy()
* Destroys the stage directory, and releases ownership. It is possible to
* destroy the stage without having claimed it first, but this shouldn't be
* done unless absolutely necessary.
*
* - \Drupal\package_manager\StageBase::stageDirectoryExists()
* Determines if the stage directory exists and returns a boolean accordingly.
* This allows validators to directly know if the stage directory exists
* without using \Drupal\package_manager\StageBase::getStageDirectory(), which
* throws an exception if the stage directory does not exist.
*
* - \Drupal\package_manager\StageBase::getStageDirectory()
* Returns the absolute path of the directory where changes should be staged.
* It throws an exception if the stage hasn't been created or claimed yet.
*
* - \Drupal\package_manager\StageBase::isApplying()
* Determines if the staged changes are being applied to the active directory.
* It will return FALSE if more than an hour has passed since the apply
* operation began.
*
* - \Drupal\package_manager\StageBase::isAvailable()
* Determines if a stage directory can be created.
*
* @section sec_stage_exceptions Stage life cycle exceptions
* If problems occur during any point of the stage life cycle, a
* \Drupal\package_manager\Exception\StageException is thrown. If problems are
* detected during one of the "pre" operations, a subclass of that is thrown:
* \Drupal\package_manager\Exception\StageEventException. This will contain
* \Drupal\package_manager\ValidationResult objects.
*
* Package Manager does not catch or handle these exceptions: they provide a
* framework for other modules to build user experiences for installing,
* updating, and removing packages.
*
* @section sec_validators_status_checks API: Validators and status checks
* Package Manager requires certain conditions in order to function properly.
* Event subscribers which check such conditions should ensure that they run
* before \Drupal\package_manager\Validator\BaseRequirementsFulfilledValidator,
* by using a priority higher than BaseRequirementsFulfilledValidator::PRIORITY.
* BaseRequirementsFulfilledValidator will stop event propagation if any errors
* have been flagged by the subscribers that ran before it.
*
* The following base requirements are checked by Package Manager:
*
* - Package Manager has not been explicitly disabled in the current
* environment.
* - The Composer executable is available.
* - The detected version of Composer is supported.
* - composer.json and composer.lock exist in the project root, and are valid
* according to the @code composer validate @endcode command.
* - The stage directory is not a subdirectory of the active directory.
* - There is enough free disk space to do stage operations.
* - The Drupal site root and vendor directory are writable.
* - The current site is not part of a multisite.
* - The project root and stage directory don't contain any unsupported links.
* See https://github.com/php-tuf/composer-stager/tree/develop/src/Domain/Service/Precondition#symlinks
* for information about which types of symlinks are supported.
*
* Apart from base requirements, Package Manager also enforces certain
* constraints at various points of the stage life cycle (typically
* \Drupal\package_manager\Event\PreCreateEvent and/or
* \Drupal\package_manager\Event\PreApplyEvent), to ensure that both the active
* directory and stage directory are kept in a safe, consistent state:
*
* - If the composer.lock file is changed (e.g., by installing or updating a
* package) in the active directory after a stage directory has been created,
* Package Manager will refuse to make any further changes to the stage
* directory or apply the staged changes to the active directory.
* - Composer plugins are able to perform arbitrary file system operations, and
* hence could perform actions that make it impossible for Package Manager to
* guarantee the Drupal site will continue to work correctly. For that reason,
* Package Manager will refuse to make any further changes if untrusted
* Composer plugins are installed or staged. If you know what you are doing,
* it is possible to trust additional Composer plugins by modifying
* package_manager.settings's "additional_trusted_composer_plugins" setting.
* - The Drupal site must not have any pending database updates (i.e.,
* update.php needs to be run).
* - Composer must use HTTPS to download packages and metadata (i.e., Composer's
* secure-http configuration option must be enabled). This is the default
* behavior.
*
* Package Manager also assumes certain things that it does not explicitly
* enforce or check:
*
* - Only Composer operations should be performed on the stage directory. If
* other file operations were performed, any newly created files might not
* be copied back to the active site because of
* \Drupal\package_manager\PathExcluder\UnknownPathExcluder.
*
* Event subscribers which enforce these and other constraints are referred to
* as validators.
*
* \Drupal\package_manager\Event\StatusCheckEvent may be dispatched at any time
* to check the status of the Drupal site and whether Package Manager can
* function properly. Package Manager does NOT dispatch this event on its own
* because it doesn't have a UI; it is meant for modules that build on top of
* Package Manager to ensure they will work correctly before they try to do any
* stage operations, and present errors however they want in their own UIs.
* Status checks can be dispatched irrespective of whether a stage directory has
* actually been created.
*
* In general, validators should always listen to
* \Drupal\package_manager\Event\StatusCheckEvent,
* \Drupal\package_manager\Event\PreCreateEvent, and
* \Drupal\package_manager\Event\PreApplyEvent. If they detect any errors,
* they should call the event's ::addError() method to prevent the stage life
* cycle from proceeding any further. If a validator encounters an exception,
* it can use ::addErrorFromThrowable() instead of ::addError(). During status
* checks, validators can call ::addWarning() for less severe problems --
* warnings will NOT stop the stage life cycle. All three are convenience
* methods for equivalent \Drupal\package_manager\ValidationResult constructors,
* which can then be added to the event using ::addResult().
*
* @see \Drupal\package_manager\ValidationResult
* @see \Drupal\package_manager\Event\PreOperationStageEvent::addError()
* @see \Drupal\package_manager\Event\PreOperationStageEvent::addErrorFromThrowable()
* @see \Drupal\package_manager\Event\StatusCheckEvent::addWarning()
* @see \Drupal\package_manager\Event\PreOperationStageEvent::addResult()
*
* @section sec_excluded_paths Excluding files from stage operations
* Certain files are never copied into the stage directory because they are
* irrelevant to Composer or Package Manager. Examples include settings.php
* and related files, public and private files, SQLite databases, and git
* repositories. Custom code can subscribe to
* Drupal\package_manager\Event\CollectPathsToExcludeEvent to flag paths which
* should never be copied into the stage directory from the active directory or
* vice versa.
*
* @see \Drupal\package_manager\Event\CollectPathsToExcludeEvent
*
* @section sec_services Useful services
* The following services are especially useful to validators:
* - \Drupal\package_manager\PathLocator looks up certain important paths in the
* active directory, such as the vendor directory, the project root and the
* web root.
* - \Drupal\package_manager\ComposerInspector is a wrapper to interact with
* Composer at the command line and get information from it about the
* project's `composer.json`, which packages are installed, etc.
*
* @section sec_package_manager_failure_marker Package Manager failure marker
* A file PACKAGE_MANAGER_FAILURE.yml is placed in the active directory while
* staged code is copied back into it, and then removed after the copying is
* finished. If this file exists, it means that the staged changes failed to be
* applied to the active directory (for example: a file system error, or the
* copying process was interrupted), and the site is therefore in an
* indeterminate state. The only thing you can do is to restore the code and
* database from a backup.
* @see \Drupal\package_manager\FailureMarker
*
* @}
*/
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.