Chris@0: get('system.cron_last'); Chris@0: * // Set the cron run time to the current request time. Chris@0: * $state->set('system.cron_last', REQUEST_TIME); Chris@0: * @endcode Chris@0: * Chris@0: * For more on the State API, see https://www.drupal.org/developing/api/8/state Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup config_api Configuration API Chris@0: * @{ Chris@0: * Information about the Configuration API. Chris@0: * Chris@0: * The Configuration API is one of several methods in Drupal for storing Chris@0: * information. See the @link info_types Information types topic @endlink for Chris@0: * an overview of the different types of information. The sections below have Chris@0: * more information about the configuration API; see Chris@0: * https://www.drupal.org/developing/api/8/configuration for more details. Chris@0: * Chris@0: * @section sec_storage Configuration storage Chris@0: * In Drupal, there is a concept of the "active" configuration, which is the Chris@0: * configuration that is currently in use for a site. The storage used for the Chris@0: * active configuration is configurable: it could be in the database, in files Chris@0: * in a particular directory, or in other storage backends; the default storage Chris@0: * is in the database. Module developers must use the configuration API to Chris@0: * access the active configuration, rather than being concerned about the Chris@0: * details of where and how it is stored. Chris@0: * Chris@0: * Configuration is divided into individual objects, each of which has a Chris@0: * unique name or key. Some modules will have only one configuration object, Chris@0: * typically called 'mymodule.settings'; some modules will have many. Within Chris@0: * a configuration object, configuration settings have data types (integer, Chris@0: * string, Boolean, etc.) and settings can also exist in a nested hierarchy, Chris@0: * known as a "mapping". Chris@0: * Chris@0: * Configuration can also be overridden on a global, per-language, or Chris@0: * per-module basis. See https://www.drupal.org/node/1928898 for more Chris@0: * information. Chris@0: * Chris@0: * @section sec_yaml Configuration YAML files Chris@0: * Whether or not configuration files are being used for the active Chris@0: * configuration storage on a particular site, configuration files are always Chris@0: * used for: Chris@0: * - Defining the default configuration for an extension (module, theme, or Chris@0: * profile), which is imported to the active storage when the extension is Chris@0: * enabled. These configuration items are located in the config/install Chris@0: * sub-directory of the extension. Note that changes to this configuration Chris@0: * after a module or theme is already enabled have no effect; to make a Chris@0: * configuration change after a module or theme is enabled, you would need to Chris@0: * uninstall/reinstall or use a hook_update_N() function. Chris@0: * - Defining optional configuration for a module or theme. Optional Chris@0: * configuration items are located in the config/optional sub-directory of the Chris@0: * extension. These configuration items have dependencies that are not Chris@0: * explicit dependencies of the extension, so they are only installed if all Chris@0: * dependencies are met. For example, in the scenario that module A defines a Chris@0: * dependency which requires module B, but module A is installed first and Chris@0: * module B some time later, then module A's config/optional directory will be Chris@0: * scanned at that time for newly met dependencies, and the configuration will Chris@0: * be installed then. If module B is never installed, the configuration item Chris@0: * will not be installed either. Chris@0: * - Exporting and importing configuration. Chris@0: * Chris@0: * The file storage format for configuration information in Drupal is Chris@0: * @link http://wikipedia.org/wiki/YAML YAML files. @endlink Configuration is Chris@0: * divided into files, each containing one configuration object. The file name Chris@0: * for a configuration object is equal to the unique name of the configuration, Chris@0: * with a '.yml' extension. The default configuration files for each module are Chris@0: * placed in the config/install directory under the top-level module directory, Chris@0: * so look there in most Core modules for examples. Chris@0: * Chris@0: * @section sec_schema Configuration schema and translation Chris@0: * Each configuration file has a specific structure, which is expressed as a Chris@0: * YAML-based configuration schema. The configuration schema details the Chris@0: * structure of the configuration, its data types, and which of its values need Chris@0: * to be translatable. Each module needs to define its configuration schema in Chris@0: * files in the config/schema directory under the top-level module directory, so Chris@0: * look there in most Core modules for examples. Chris@0: * Chris@0: * Configuration can be internationalized; see the Chris@0: * @link i18n Internationalization topic @endlink for more information. Data Chris@0: * types label, text, and date_format in configuration schema are translatable; Chris@0: * string is non-translatable text (the 'translatable' property on a schema Chris@0: * data type definition indicates that it is translatable). Chris@0: * Chris@0: * @section sec_simple Simple configuration Chris@0: * The simple configuration API should be used for information that will always Chris@0: * have exactly one copy or version. For instance, if your module has a Chris@0: * setting that is either on or off, then this is only defined once, and it Chris@0: * would be a Boolean-valued simple configuration setting. Chris@0: * Chris@0: * The first task in using the simple configuration API is to define the Chris@0: * configuration file structure, file name, and schema of your settings (see Chris@0: * @ref sec_yaml above). Once you have done that, you can retrieve the active Chris@0: * configuration object that corresponds to configuration file mymodule.foo.yml Chris@0: * with a call to: Chris@0: * @code Chris@0: * $config = \Drupal::config('mymodule.foo'); Chris@0: * @endcode Chris@0: * Chris@0: * This will be an object of class \Drupal\Core\Config\Config, which has methods Chris@0: * for getting configuration information. For instance, if your YAML file Chris@0: * structure looks like this: Chris@0: * @code Chris@0: * enabled: '0' Chris@0: * bar: Chris@0: * baz: 'string1' Chris@0: * boo: 34 Chris@0: * @endcode Chris@0: * you can make calls such as: Chris@0: * @code Chris@0: * // Get a single value. Chris@0: * $enabled = $config->get('enabled'); Chris@0: * // Get an associative array. Chris@0: * $bar = $config->get('bar'); Chris@0: * // Get one element of the array. Chris@0: * $bar_baz = $config->get('bar.baz'); Chris@0: * @endcode Chris@0: * Chris@0: * The Config object that was obtained and used in the previous examples does Chris@0: * not allow you to change configuration. If you want to change configuration, Chris@0: * you will instead need to get the Config object by making a call to Chris@0: * getEditable() on the config factory: Chris@0: * @code Chris@0: * $config =\Drupal::service('config.factory')->getEditable('mymodule.foo'); Chris@0: * @endcode Chris@0: * Chris@0: * Individual configuration values can be changed or added using the set() Chris@0: * method and saved using the save() method: Chris@0: * @code Chris@0: * // Set a scalar value. Chris@0: * $config->set('enabled', 1); Chris@0: * // Save the configuration. Chris@0: * $config->save(); Chris@0: * @endcode Chris@0: * Chris@0: * Configuration values can also be unset using the clear() method, which is Chris@0: * also chainable: Chris@0: * @code Chris@0: * $config->clear('bar.boo')->save(); Chris@0: * $config_data = $config->get('bar'); Chris@0: * @endcode Chris@0: * In this example $config_data would return an array with one key - 'baz' - Chris@0: * because 'boo' was unset. Chris@0: * Chris@0: * @section sec_entity Configuration entities Chris@0: * In contrast to the simple configuration settings described in the previous Chris@0: * section, if your module allows users to create zero or more items (where Chris@0: * "items" are things like content type definitions, view definitions, and the Chris@0: * like), then you need to define a configuration entity type to store your Chris@0: * configuration. Creating an entity type, loading entities, and querying them Chris@0: * are outlined in the @link entity_api Entity API topic. @endlink Here are a Chris@0: * few additional steps and notes specific to configuration entities: Chris@0: * - For examples, look for classes that implement Chris@0: * \Drupal\Core\Config\Entity\ConfigEntityInterface -- one good example is Chris@0: * the \Drupal\user\Entity\Role entity type. Chris@0: * - In the entity type annotation, you will need to define a 'config_prefix' Chris@0: * string. When Drupal stores a configuration item, it will be given a name Chris@0: * composed of your module name, your chosen config prefix, and the ID of Chris@0: * the individual item, separated by '.'. For example, in the Role entity, Chris@0: * the config prefix is 'role', so one configuration item might be named Chris@0: * user.role.anonymous, with configuration file user.role.anonymous.yml. Chris@0: * - You will need to define the schema for your configuration in your Chris@0: * modulename.schema.yml file, with an entry for 'modulename.config_prefix.*'. Chris@0: * For example, for the Role entity, the file user.schema.yml has an entry Chris@0: * user.role.*; see @ref sec_yaml above for more information. Chris@0: * - Your module can provide default/optional configuration entities in YAML Chris@0: * files; see @ref sec_yaml above for more information. Chris@0: * - Some configuration entities have dependencies on other configuration Chris@0: * entities, and module developers need to consider this so that configuration Chris@0: * can be imported, uninstalled, and synchronized in the right order. For Chris@0: * example, a field display configuration entity would need to depend on Chris@0: * field configuration, which depends on field and bundle configuration. Chris@0: * Configuration entity classes expose dependencies by overriding the Chris@0: * \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies() Chris@0: * method. Chris@0: * - On routes for paths starting with '/admin' or otherwise designated as Chris@0: * administration paths (such as node editing when it is set as an admin Chris@0: * operation), if they have configuration entity placeholders, configuration Chris@0: * entities are normally loaded in their original language, without Chris@0: * translations or other overrides. This is usually desirable, because most Chris@0: * admin paths are for editing configuration, and you need that to be in the Chris@0: * source language and to lack possibly dynamic overrides. If for some reason Chris@0: * you need to have your configuration entity loaded in the currently-selected Chris@0: * language on an admin path (for instance, if you go to Chris@0: * example.com/es/admin/your_path and you need the entity to be in Spanish), Chris@0: * then you can add a 'with_config_overrides' parameter option to your route. Chris@0: * The same applies if you need to load the entity with overrides (or Chris@0: * translated) on an admin path like '/node/add/article' (when configured to Chris@0: * be an admin path). Here's an example using the configurable_language config Chris@0: * entity: Chris@0: * @code Chris@0: * mymodule.myroute: Chris@0: * path: '/admin/mypath/{configurable_language}' Chris@0: * defaults: Chris@0: * _controller: '\Drupal\mymodule\MyController::myMethod' Chris@0: * options: Chris@0: * parameters: Chris@0: * configurable_language: Chris@0: * type: entity:configurable_language Chris@0: * with_config_overrides: TRUE Chris@0: * @endcode Chris@0: * With the route defined this way, the $configurable_language parameter to Chris@0: * your controller method will come in translated to the current language. Chris@0: * Without the parameter options section, it would be in the original Chris@0: * language, untranslated. Chris@0: * Chris@0: * @see i18n Chris@0: * Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup cache Cache API Chris@0: * @{ Chris@0: * Information about the Drupal Cache API Chris@0: * Chris@0: * @section basics Basics Chris@0: * Chris@0: * Note: If not specified, all of the methods mentioned here belong to Chris@0: * \Drupal\Core\Cache\CacheBackendInterface. Chris@0: * Chris@0: * The Cache API is used to store data that takes a long time to compute. Chris@16: * Caching can either be permanent or valid only for a certain time span, and Chris@0: * the cache can contain any type of data. Chris@0: * Chris@0: * To use the Cache API: Chris@0: * - Request a cache object through \Drupal::cache() or by injecting a cache Chris@0: * service. Chris@0: * - Define a Cache ID (cid) value for your data. A cid is a string, which must Chris@0: * contain enough information to uniquely identify the data. For example, if Chris@0: * your data contains translated strings, then your cid value must include the Chris@0: * interface text language selected for page. Chris@0: * - Call the get() method to attempt a cache read, to see if the cache already Chris@0: * contains your data. Chris@0: * - If your data is not already in the cache, compute it and add it to the Chris@0: * cache using the set() method. The third argument of set() can be used to Chris@0: * control the lifetime of your cache item. Chris@0: * Chris@0: * Example: Chris@0: * @code Chris@0: * $cid = 'mymodule_example:' . \Drupal::languageManager()->getCurrentLanguage()->getId(); Chris@0: * Chris@0: * $data = NULL; Chris@0: * if ($cache = \Drupal::cache()->get($cid)) { Chris@0: * $data = $cache->data; Chris@0: * } Chris@0: * else { Chris@0: * $data = my_module_complicated_calculation(); Chris@0: * \Drupal::cache()->set($cid, $data); Chris@0: * } Chris@0: * @endcode Chris@0: * Chris@0: * Note the use of $data and $cache->data in the above example. Calls to Chris@0: * \Drupal::cache()->get() return a record that contains the information stored Chris@0: * by \Drupal::cache()->set() in the data property as well as additional meta Chris@0: * information about the cached data. In order to make use of the cached data Chris@0: * you can access it via $cache->data. Chris@0: * Chris@0: * @section bins Cache bins Chris@0: * Chris@0: * Cache storage is separated into "bins", each containing various cache items. Chris@0: * Each bin can be configured separately; see @ref configuration. Chris@0: * Chris@0: * When you request a cache object, you can specify the bin name in your call to Chris@0: * \Drupal::cache(). Alternatively, you can request a bin by getting service Chris@0: * "cache.nameofbin" from the container. The default bin is called "default", with Chris@0: * service name "cache.default", it is used to store common and frequently used Chris@0: * caches. Chris@0: * Chris@0: * Other common cache bins are the following: Chris@0: * - bootstrap: Data needed from the beginning to the end of most requests, Chris@0: * that has a very strict limit on variations and is invalidated rarely. Chris@0: * - render: Contains cached HTML strings like cached pages and blocks, can Chris@0: * grow to large size. Chris@0: * - data: Contains data that can vary by path or similar context. Chris@0: * - discovery: Contains cached discovery data for things such as plugins, Chris@0: * views_data, or YAML discovered data such as library info. Chris@0: * Chris@0: * A module can define a cache bin by defining a service in its Chris@0: * modulename.services.yml file as follows (substituting the desired name for Chris@0: * "nameofbin"): Chris@0: * @code Chris@0: * cache.nameofbin: Chris@0: * class: Drupal\Core\Cache\CacheBackendInterface Chris@0: * tags: Chris@0: * - { name: cache.bin } Chris@0: * factory: cache_factory:get Chris@0: * arguments: [nameofbin] Chris@0: * @endcode Chris@0: * See the @link container Services topic @endlink for more on defining Chris@0: * services. Chris@0: * Chris@0: * @section delete Deletion Chris@0: * Chris@0: * There are two ways to remove an item from the cache: Chris@0: * - Deletion (using delete(), deleteMultiple() or deleteAll()) permanently Chris@0: * removes the item from the cache. Chris@0: * - Invalidation (using invalidate(), invalidateMultiple() or invalidateAll()) Chris@0: * is a "soft" delete that only marks items as "invalid", meaning "not fresh" Chris@0: * or "not fresh enough". Invalid items are not usually returned from the Chris@0: * cache, so in most ways they behave as if they have been deleted. However, Chris@0: * it is possible to retrieve invalid items, if they have not yet been Chris@0: * permanently removed by the garbage collector, by passing TRUE as the second Chris@0: * argument for get($cid, $allow_invalid). Chris@0: * Chris@0: * Use deletion if a cache item is no longer useful; for instance, if the item Chris@0: * contains references to data that has been deleted. Use invalidation if the Chris@0: * cached item may still be useful to some callers until it has been updated Chris@0: * with fresh data. The fact that it was fresh a short while ago may often be Chris@0: * sufficient. Chris@0: * Chris@0: * Invalidation is particularly useful to protect against stampedes. Rather than Chris@0: * having multiple concurrent requests updating the same cache item when it Chris@0: * expires or is deleted, there can be one request updating the cache, while the Chris@0: * other requests can proceed using the stale value. As soon as the cache item Chris@0: * has been updated, all future requests will use the updated value. Chris@0: * Chris@0: * @section tags Cache Tags Chris@0: * Chris@0: * The fourth argument of the set() method can be used to specify cache tags, Chris@0: * which are used to identify which data is included in each cache item. A cache Chris@0: * item can have multiple cache tags (an array of cache tags), and each cache Chris@0: * tag is a string. The convention is to generate cache tags of the form Chris@0: * [prefix]:[suffix]. Usually, you'll want to associate the cache tags of Chris@0: * entities, or entity listings. You won't have to manually construct cache tags Chris@0: * for them — just get their cache tags via Chris@0: * \Drupal\Core\Cache\CacheableDependencyInterface::getCacheTags() and Chris@0: * \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags(). Chris@0: * Data that has been tagged can be invalidated as a group: no matter the Cache Chris@0: * ID (cid) of the cache item, no matter in which cache bin a cache item lives; Chris@0: * as long as it is tagged with a certain cache tag, it will be invalidated. Chris@0: * Chris@0: * Because of that, cache tags are a solution to the cache invalidation problem: Chris@0: * - For caching to be effective, each cache item must only be invalidated when Chris@0: * absolutely necessary. (i.e. maximizing the cache hit ratio.) Chris@0: * - For caching to be correct, each cache item that depends on a certain thing Chris@0: * must be invalidated whenever that certain thing is modified. Chris@0: * Chris@0: * A typical scenario: a user has modified a node that appears in two views, Chris@0: * three blocks and on twelve pages. Without cache tags, we couldn't possibly Chris@0: * know which cache items to invalidate, so we'd have to invalidate everything: Chris@0: * we had to sacrifice effectiveness to achieve correctness. With cache tags, we Chris@0: * can have both. Chris@0: * Chris@0: * Example: Chris@0: * @code Chris@0: * // A cache item with nodes, users, and some custom module data. Chris@0: * $tags = array( Chris@0: * 'my_custom_tag', Chris@0: * 'node:1', Chris@0: * 'node:3', Chris@0: * 'user:7', Chris@0: * ); Chris@0: * \Drupal::cache()->set($cid, $data, CacheBackendInterface::CACHE_PERMANENT, $tags); Chris@0: * Chris@0: * // Invalidate all cache items with certain tags. Chris@0: * \Drupal\Core\Cache\Cache::invalidateTags(array('user:1')); Chris@0: * @endcode Chris@0: * Chris@0: * Drupal is a content management system, so naturally you want changes to your Chris@0: * content to be reflected everywhere, immediately. That's why we made sure that Chris@0: * every entity type in Drupal 8 automatically has support for cache tags: when Chris@0: * you save an entity, you can be sure that the cache items that have the Chris@0: * corresponding cache tags will be invalidated. Chris@0: * This also is the case when you define your own entity types: you'll get the Chris@0: * exact same cache tag invalidation as any of the built-in entity types, with Chris@0: * the ability to override any of the default behavior if needed. Chris@16: * See \Drupal\Core\Cache\CacheableDependencyInterface::getCacheTags(), Chris@0: * \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags(), Chris@0: * \Drupal\Core\Entity\Entity::invalidateTagsOnSave() and Chris@0: * \Drupal\Core\Entity\Entity::invalidateTagsOnDelete(). Chris@0: * Chris@0: * @section context Cache contexts Chris@0: * Chris@0: * Some computed data depends on contextual data, such as the user roles of the Chris@0: * logged-in user who is viewing a page, the language the page is being rendered Chris@0: * in, the theme being used, etc. When caching the output of such a calculation, Chris@0: * you must cache each variation separately, along with information about which Chris@16: * variation of the contextual data was used in the calculation. The next time Chris@0: * the computed data is needed, if the context matches that for an existing Chris@0: * cached data set, the cached data can be reused; if no context matches, a new Chris@0: * data set can be calculated and cached for later use. Chris@0: * Chris@0: * Cache contexts are services tagged with 'cache.context', whose classes Chris@0: * implement \Drupal\Core\Cache\Context\CacheContextInterface. See Chris@0: * https://www.drupal.org/developing/api/8/cache/contexts for more information Chris@0: * on cache contexts, including a list of the contexts that exist in Drupal Chris@0: * core, and information on how to define your own contexts. See the Chris@0: * @link container Services and the Dependency Injection Container @endlink Chris@0: * topic for more information about services. Chris@0: * Chris@0: * Typically, the cache context is specified as part of the #cache property Chris@0: * of a render array; see the Caching section of the Chris@0: * @link theme_render Render API overview topic @endlink for details. Chris@0: * Chris@0: * @section configuration Configuration Chris@0: * Chris@0: * By default cached data is stored in the database. This can be configured Chris@0: * though so that all cached data, or that of an individual cache bin, uses a Chris@0: * different cache backend, such as APCu or Memcache, for storage. Chris@0: * Chris@0: * In a settings.php file, you can override the service used for a particular Chris@0: * cache bin. For example, if your service implementation of Chris@0: * \Drupal\Core\Cache\CacheBackendInterface was called cache.custom, the Chris@0: * following line would make Drupal use it for the 'cache_render' bin: Chris@0: * @code Chris@0: * $settings['cache']['bins']['render'] = 'cache.custom'; Chris@0: * @endcode Chris@0: * Chris@0: * Additionally, you can register your cache implementation to be used by Chris@0: * default for all cache bins with: Chris@0: * @code Chris@0: * $settings['cache']['default'] = 'cache.custom'; Chris@0: * @endcode Chris@0: * Chris@0: * For cache bins that are stored in the database, the number of rows is limited Chris@0: * to 5000 by default. This can be changed for all database cache bins. For Chris@0: * example, to instead limit the number of rows to 50000: Chris@0: * @code Chris@0: * $settings['database_cache_max_rows']['default'] = 50000; Chris@0: * @endcode Chris@0: * Chris@0: * Or per bin (in this example we allow infinite entries): Chris@0: * @code Chris@0: * $settings['database_cache_max_rows']['bins']['dynamic_page_cache'] = -1; Chris@0: * @endcode Chris@0: * Chris@0: * For monitoring reasons it might be useful to figure out the amount of data Chris@0: * stored in tables. The following SQL snippet can be used for that: Chris@0: * @code Chris@0: * SELECT table_name AS `Table`, table_rows AS 'Num. of Rows', Chris@0: * ROUND(((data_length + index_length) / 1024 / 1024), 2) `Size in MB` FROM Chris@0: * information_schema.TABLES WHERE table_schema = '***DATABASE_NAME***' AND Chris@0: * table_name LIKE 'cache_%' ORDER BY (data_length + index_length) DESC Chris@0: * LIMIT 10; Chris@0: * @endcode Chris@0: * Chris@0: * @see \Drupal\Core\Cache\DatabaseBackend Chris@0: * Chris@0: * Finally, you can chain multiple cache backends together, see Chris@0: * \Drupal\Core\Cache\ChainedFastBackend and \Drupal\Core\Cache\BackendChain. Chris@0: * Chris@0: * @see https://www.drupal.org/node/1884796 Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup user_api User accounts, permissions, and roles Chris@0: * @{ Chris@0: * API for user accounts, access checking, roles, and permissions. Chris@0: * Chris@0: * @section sec_overview Overview and terminology Chris@0: * Drupal's permission system is based on the concepts of accounts, roles, Chris@0: * and permissions. Chris@0: * Chris@0: * Users (site visitors) have accounts, which include a user name, an email Chris@0: * address, a password (or some other means of authentication), and possibly Chris@0: * other fields (if defined on the site). Anonymous users have an implicit Chris@0: * account that does not have a real user name or any account information. Chris@0: * Chris@0: * Each user account is assigned one or more roles. The anonymous user account Chris@0: * automatically has the anonymous user role; real user accounts Chris@0: * automatically have the authenticated user role, plus any roles defined on Chris@0: * the site that they have been assigned. Chris@0: * Chris@0: * Each role, including the special anonymous and authenticated user roles, is Chris@0: * granted one or more named permissions, which allow them to perform certain Chris@0: * tasks or view certain content on the site. It is possible to designate a Chris@0: * role to be the "administrator" role; if this is set up, this role is Chris@0: * automatically granted all available permissions whenever a module is Chris@0: * enabled that defines permissions. Chris@0: * Chris@0: * All code in Drupal that allows users to perform tasks or view content must Chris@0: * check that the current user has the correct permission before allowing the Chris@0: * action. In the standard case, access checking consists of answering the Chris@0: * question "Does the current user have permission 'foo'?", and allowing or Chris@0: * denying access based on the answer. Note that access checking should nearly Chris@0: * always be done at the permission level, not by checking for a particular role Chris@0: * or user ID, so that site administrators can set up user accounts and roles Chris@0: * appropriately for their particular sites. Chris@0: * Chris@0: * @section sec_define Defining permissions Chris@0: * Modules define permissions via a $module.permissions.yml file. See Chris@0: * \Drupal\user\PermissionHandler for documentation of permissions.yml files. Chris@0: * Chris@0: * @section sec_access Access permission checking Chris@0: * Depending on the situation, there are several methods for ensuring that Chris@0: * access checks are done properly in Drupal: Chris@0: * - Routes: When you register a route, include a 'requirements' section that Chris@0: * either gives the machine name of the permission that is needed to visit the Chris@0: * URL of the route, or tells Drupal to use an access check method or service Chris@0: * to check access. See the @link menu Routing topic @endlink for more Chris@0: * information. Chris@0: * - Entities: Access for various entity operations is designated either with Chris@0: * simple permissions or access control handler classes in the entity Chris@0: * annotation. See the @link entity_api Entity API topic @endlink for more Chris@0: * information. Chris@0: * - Other code: There is a 'current_user' service, which can be injected into Chris@0: * classes to provide access to the current user account (see the Chris@0: * @link container Services and Dependency Injection topic @endlink for more Chris@0: * information on dependency injection). In code that cannot use dependency Chris@0: * injection, you can access this service and retrieve the current user Chris@0: * account object by calling \Drupal::currentUser(). Once you have a user Chris@0: * object for the current user (implementing \Drupal\user\UserInterface), you Chris@0: * can call inherited method Chris@0: * \Drupal\Core\Session\AccountInterface::hasPermission() to check Chris@0: * permissions, or pass this object into other functions/methods. Chris@0: * - Forms: Each element of a form array can have a Boolean '#access' property, Chris@0: * which determines whether that element is visible and/or usable. This is a Chris@0: * common need in forms, so the current user service (described above) is Chris@0: * injected into the form base class as method Chris@0: * \Drupal\Core\Form\FormBase::currentUser(). Chris@0: * Chris@0: * @section sec_entities User and role objects Chris@0: * User objects in Drupal are entity items, implementing Chris@0: * \Drupal\user\UserInterface. Role objects in Drupal are also entity items, Chris@0: * implementing \Drupal\user\RoleInterface. See the Chris@0: * @link entity_api Entity API topic @endlink for more information about Chris@0: * entities in general (including how to load, create, modify, and query them). Chris@0: * Chris@0: * Roles often need to be manipulated in automated test code, such as to add Chris@0: * permissions to them. Here's an example: Chris@0: * @code Chris@0: * $role = \Drupal\user\Entity\Role::load('authenticated'); Chris@0: * $role->grantPermission('access comments'); Chris@0: * $role->save(); Chris@0: * @endcode Chris@0: * Chris@0: * Other important interfaces: Chris@0: * - \Drupal\Core\Session\AccountInterface: The part of UserInterface that Chris@0: * deals with access checking. In writing code that checks access, your Chris@0: * method parameters should use this interface, not UserInterface. Chris@0: * - \Drupal\Core\Session\AccountProxyInterface: The interface for the Chris@0: * current_user service (described above). Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup container Services and Dependency Injection Container Chris@0: * @{ Chris@0: * Overview of the Dependency Injection Container and Services. Chris@0: * Chris@0: * @section sec_overview Overview of container, injection, and services Chris@0: * The Services and Dependency Injection Container concepts have been adopted by Chris@0: * Drupal from the @link http://symfony.com/ Symfony framework. @endlink A Chris@0: * "service" (such as accessing the database, sending email, or translating user Chris@0: * interface text) is defined (given a name and an interface or at least a Chris@0: * class that defines the methods that may be called), and a default class is Chris@0: * designated to provide the service. These two steps must be done together, and Chris@0: * can be done by Drupal Core or a module. Other modules can then define Chris@0: * alternative classes to provide the same services, overriding the default Chris@0: * classes. Classes and functions that need to use the service should always Chris@0: * instantiate the class via the dependency injection container (also known Chris@0: * simply as the "container"), rather than instantiating a particular service Chris@0: * provider class directly, so that they get the correct class (default or Chris@0: * overridden). Chris@0: * Chris@0: * See https://www.drupal.org/node/2133171 for more detailed information on Chris@0: * services and the dependency injection container. Chris@0: * Chris@0: * @section sec_discover Discovering existing services Chris@0: * Drupal core defines many core services in the core.services.yml file (in the Chris@0: * top-level core directory). Some Drupal Core modules and contributed modules Chris@0: * also define services in modulename.services.yml files. API reference sites Chris@0: * (such as https://api.drupal.org) generate lists of all existing services from Chris@0: * these files. Look for the Services link in the API Navigation block. Chris@0: * Alternatively you can look through the individual files manually. Chris@0: * Chris@0: * A typical service definition in a *.services.yml file looks like this: Chris@0: * @code Chris@0: * path.alias_manager: Chris@0: * class: Drupal\Core\Path\AliasManager Chris@0: * arguments: ['@path.crud', '@path.alias_whitelist', '@language_manager'] Chris@0: * @endcode Chris@0: * Some services use other services as factories; a typical service definition Chris@0: * is: Chris@0: * @code Chris@0: * cache.entity: Chris@0: * class: Drupal\Core\Cache\CacheBackendInterface Chris@0: * tags: Chris@0: * - { name: cache.bin } Chris@0: * factory: cache_factory:get Chris@0: * arguments: [entity] Chris@0: * @endcode Chris@0: * Chris@0: * The first line of a service definition gives the unique machine name of the Chris@0: * service. This is often prefixed by the module name if provided by a module; Chris@0: * however, by convention some service names are prefixed by a group name Chris@0: * instead, such as cache.* for cache bins and plugin.manager.* for plugin Chris@0: * managers. Chris@0: * Chris@0: * The class line either gives the default class that provides the service, or Chris@0: * if the service uses a factory class, the interface for the service. If the Chris@0: * class depends on other services, the arguments line lists the machine Chris@0: * names of the dependencies (preceded by '@'); objects for each of these Chris@0: * services are instantiated from the container and passed to the class Chris@0: * constructor when the service class is instantiated. Other arguments can also Chris@0: * be passed in; see the section at https://www.drupal.org/node/2133171 for more Chris@0: * detailed information. Chris@0: * Chris@0: * Services using factories can be defined as shown in the above example, if the Chris@0: * factory is itself a service. The factory can also be a class; details of how Chris@0: * to use service factories can be found in the section at Chris@0: * https://www.drupal.org/node/2133171. Chris@0: * Chris@0: * @section sec_container Accessing a service through the container Chris@0: * As noted above, if you need to use a service in your code, you should always Chris@0: * instantiate the service class via a call to the container, using the machine Chris@0: * name of the service, so that the default class can be overridden. There are Chris@0: * several ways to make sure this happens: Chris@0: * - For service-providing classes, see other sections of this documentation Chris@0: * describing how to pass services as arguments to the constructor. Chris@0: * - Plugin classes, controllers, and similar classes have create() or Chris@0: * createInstance() methods that are used to create an instance of the class. Chris@0: * These methods come from different interfaces, and have different Chris@0: * arguments, but they all include an argument $container of type Chris@0: * \Symfony\Component\DependencyInjection\ContainerInterface. Chris@0: * If you are defining one of these classes, in the create() or Chris@0: * createInstance() method, call $container->get('myservice.name') to Chris@0: * instantiate a service. The results of these calls are generally passed to Chris@0: * the class constructor and saved as member variables in the class. Chris@0: * - For functions and class methods that do not have access to either of Chris@0: * the above methods of dependency injection, you can use service location to Chris@0: * access services, via a call to the global \Drupal class. This class has Chris@0: * special methods for accessing commonly-used services, or you can call a Chris@0: * generic method to access any service. Examples: Chris@0: * @code Chris@0: * // Retrieve the entity.manager service object (special method exists). Chris@0: * $manager = \Drupal::entityManager(); Chris@0: * // Retrieve the service object for machine name 'foo.bar'. Chris@0: * $foobar = \Drupal::service('foo.bar'); Chris@0: * @endcode Chris@0: * Chris@0: * As a note, you should always use dependency injection (via service arguments Chris@0: * or create()/createInstance() methods) if possible to instantiate services, Chris@0: * rather than service location (via the \Drupal class), because: Chris@0: * - Dependency injection facilitates writing unit tests, since the container Chris@0: * argument can be mocked and the create() method can be bypassed by using Chris@0: * the class constructor. If you use the \Drupal class, unit tests are much Chris@0: * harder to write and your code has more dependencies. Chris@0: * - Having the service interfaces on the class constructor and member variables Chris@0: * is useful for IDE auto-complete and self-documentation. Chris@0: * Chris@0: * @section sec_define Defining a service Chris@0: * If your module needs to define a new service, here are the steps: Chris@0: * - Choose a unique machine name for your service. Typically, this should Chris@0: * start with your module name. Example: mymodule.myservice. Chris@0: * - Create a PHP interface to define what your service does. Chris@0: * - Create a default class implementing your interface that provides your Chris@0: * service. If your class needs to use existing services (such as database Chris@0: * access), be sure to make these services arguments to your class Chris@0: * constructor, and save them in member variables. Also, if the needed Chris@0: * services are provided by other modules and not Drupal Core, you'll want Chris@0: * these modules to be dependencies of your module. Chris@0: * - Add an entry to a modulename.services.yml file for the service. See Chris@0: * @ref sec_discover above, or existing *.services.yml files in Core, for the Chris@0: * syntax; it will start with your machine name, refer to your default class, Chris@0: * and list the services that need to be passed into your constructor. Chris@0: * Chris@0: * Services can also be defined dynamically, as in the Chris@0: * \Drupal\Core\CoreServiceProvider class, but this is less common for modules. Chris@0: * Chris@0: * @section sec_tags Service tags Chris@0: * Some services have tags, which are defined in the service definition. See Chris@0: * @link service_tag Service Tags @endlink for usage. Chris@0: * Chris@0: * @section sec_injection Overriding the default service class Chris@0: * Modules can override the default classes used for services. Here are the Chris@0: * steps: Chris@0: * - Define a class in the top-level namespace for your module Chris@0: * (Drupal\my_module), whose name is the camel-case version of your module's Chris@0: * machine name followed by "ServiceProvider" (for example, if your module Chris@0: * machine name is my_module, the class must be named Chris@0: * MyModuleServiceProvider). Chris@0: * - The class needs to implement Chris@0: * \Drupal\Core\DependencyInjection\ServiceModifierInterface, which is Chris@0: * typically done by extending Chris@0: * \Drupal\Core\DependencyInjection\ServiceProviderBase. Chris@0: * - The class needs to contain one method: alter(). This method does the Chris@0: * actual work of telling Drupal to use your class instead of the default. Chris@0: * Here's an example: Chris@0: * @code Chris@0: * public function alter(ContainerBuilder $container) { Chris@0: * // Override the language_manager class with a new class. Chris@0: * $definition = $container->getDefinition('language_manager'); Chris@0: * $definition->setClass('Drupal\my_module\MyLanguageManager'); Chris@0: * } Chris@0: * @endcode Chris@0: * Note that $container here is an instance of Chris@0: * \Drupal\Core\DependencyInjection\ContainerBuilder. Chris@0: * Chris@0: * @see https://www.drupal.org/node/2133171 Chris@0: * @see core.services.yml Chris@0: * @see \Drupal Chris@0: * @see \Symfony\Component\DependencyInjection\ContainerInterface Chris@0: * @see plugin_api Chris@0: * @see menu Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup listing_page_service Page header for Services page Chris@0: * @{ Chris@0: * Introduction to services Chris@0: * Chris@0: * A "service" (such as accessing the database, sending email, or translating Chris@0: * user interface text) can be defined by a module or Drupal core. Defining a Chris@0: * service means giving it a name and designating a default class to provide the Chris@0: * service; ideally, there should also be an interface that defines the methods Chris@0: * that may be called. Services are collected into the Dependency Injection Chris@0: * Container, and can be overridden to use different classes or different Chris@0: * instantiation by modules. See the Chris@0: * @link container Services and Dependency Injection Container topic @endlink Chris@0: * for details. Chris@0: * Chris@0: * Some services have tags, which are defined in the service definition. Tags Chris@0: * are used to define a group of related services, or to specify some aspect of Chris@0: * how the service behaves. See the Chris@0: * @link service_tag Service Tags topic @endlink for more information. Chris@0: * Chris@0: * @see container Chris@0: * @see service_tag Chris@0: * Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup typed_data Typed Data API Chris@0: * @{ Chris@0: * API for describing data based on a set of available data types. Chris@0: * Chris@0: * PHP has data types, such as int, string, float, array, etc., and it is an Chris@0: * object-oriented language that lets you define classes and interfaces. Chris@0: * However, in some cases, it is useful to be able to define an abstract Chris@0: * type (as in an interface, free of implementation details), that still has Chris@0: * properties (which an interface cannot) as well as meta-data. The Typed Data Chris@0: * API provides this abstraction. Chris@0: * Chris@0: * @section sec_overview Overview Chris@0: * Each data type in the Typed Data API is a plugin class (annotation class Chris@0: * example: \Drupal\Core\TypedData\Annotation\DataType); these plugins are Chris@0: * managed by the typed_data_manager service (by default Chris@0: * \Drupal\Core\TypedData\TypedDataManager). Each data object encapsulates a Chris@0: * single piece of data, provides access to the metadata, and provides Chris@0: * validation capability. Also, the typed data plugins have a shorthand Chris@0: * for easily accessing data values, described in @ref sec_tree. Chris@0: * Chris@0: * The metadata of a data object is defined by an object based on a class called Chris@0: * the definition class (see \Drupal\Core\TypedData\DataDefinitionInterface). Chris@0: * The class used can vary by data type and can be specified in the data type's Chris@0: * plugin definition, while the default is set in the $definition_class property Chris@0: * of the annotation class. The default class is Chris@0: * \Drupal\Core\TypedData\DataDefinition. For data types provided by a plugin Chris@0: * deriver, the plugin deriver can set the definition_class property too. Chris@0: * The metadata object provides information about the data, such as the data Chris@0: * type, whether it is translatable, the names of its properties (for complex Chris@0: * types), and who can access it. Chris@0: * Chris@0: * See https://www.drupal.org/node/1794140 for more information about the Typed Chris@0: * Data API. Chris@0: * Chris@0: * @section sec_varieties Varieties of typed data Chris@0: * There are three kinds of typed data: primitive, complex, and list. Chris@0: * Chris@0: * @subsection sub_primitive Primitive data types Chris@0: * Primitive data types wrap PHP data types and also serve as building blocks Chris@0: * for complex and list typed data. Each primitive data type has an interface Chris@0: * that extends \Drupal\Core\TypedData\PrimitiveInterface, with getValue() Chris@0: * and setValue() methods for accessing the data value, and a default plugin Chris@0: * implementation. Here's a list: Chris@0: * - \Drupal\Core\TypedData\Type\IntegerInterface: Plugin ID integer, Chris@0: * corresponds to PHP type int. Chris@0: * - \Drupal\Core\TypedData\Type\StringInterface: Plugin ID string, Chris@0: * corresponds to PHP type string. Chris@0: * - \Drupal\Core\TypedData\Type\FloatInterface: Plugin ID float, Chris@0: * corresponds to PHP type float. Chris@0: * - \Drupal\Core\TypedData\Type\BooleanInterface: Plugin ID bool, Chris@0: * corresponds to PHP type bool. Chris@0: * - \Drupal\Core\TypedData\Type\BinaryInterface: Plugin ID binary, Chris@0: * corresponds to a PHP file resource. Chris@0: * - \Drupal\Core\TypedData\Type\UriInterface: Plugin ID uri. Chris@0: * Chris@0: * @subsection sec_complex Complex data Chris@0: * Complex data types, with interface Chris@0: * \Drupal\Core\TypedData\ComplexDataInterface, represent data with named Chris@0: * properties; the properties can be accessed with get() and set() methods. Chris@0: * The value of each property is itself a typed data object, which can be Chris@0: * primitive, complex, or list data. Chris@0: * Chris@0: * The base type for most complex data is the Chris@0: * \Drupal\Core\TypedData\Plugin\DataType\Map class, which represents an Chris@0: * associative array. Map provides its own definition class in the annotation, Chris@0: * \Drupal\Core\TypedData\MapDataDefinition, and most complex data classes Chris@0: * extend this class. The getValue() and setValue() methods on the Map class Chris@0: * enforce the data definition and its property structure. Chris@0: * Chris@0: * The Drupal Field API uses complex typed data for its field items, with Chris@0: * definition class \Drupal\Core\Field\TypedData\FieldItemDataDefinition. Chris@0: * Chris@0: * @section sec_list Lists Chris@0: * List data types, with interface \Drupal\Core\TypedData\ListInterface, Chris@0: * represent data that is an ordered list of typed data, all of the same type. Chris@0: * More precisely, the plugins in the list must have the same base plugin ID; Chris@0: * however, some types (for example field items and entities) are provided by Chris@0: * plugin derivatives and the sub IDs can be different. Chris@0: * Chris@0: * @section sec_tree Tree handling Chris@0: * Typed data allows you to use shorthand to get data values nested in the Chris@0: * implicit tree structure of the data. For example, to get the value from Chris@0: * an entity field item, the Entity Field API allows you to call: Chris@0: * @code Chris@0: * $value = $entity->fieldName->propertyName; Chris@0: * @endcode Chris@0: * This is really shorthand for: Chris@0: * @code Chris@0: * $field_item_list = $entity->get('fieldName'); Chris@0: * $field_item = $field_item_list->get(0); Chris@0: * $property = $field_item->get('propertyName'); Chris@0: * $value = $property->getValue(); Chris@0: * @endcode Chris@0: * Some notes: Chris@0: * - $property, $field_item, and $field_item_list are all typed data objects, Chris@0: * while $value is a raw PHP value. Chris@0: * - You can call $property->getParent() to get $field_item, Chris@0: * $field_item->getParent() to get $field_item_list, or Chris@0: * $field_item_list->getParent() to get $typed_entity ($entity wrapped in a Chris@0: * typed data object). $typed_entity->getParent() is NULL. Chris@0: * - For all of these ->getRoot() returns $typed_entity. Chris@0: * - The langcode property is on $field_item_list, but you can access it Chris@0: * on $property as well, so that all items will report the same langcode. Chris@0: * - When the value of $property is changed by calling $property->setValue(), Chris@0: * $property->onChange() will fire, which in turn calls the parent object's Chris@0: * onChange() method and so on. This allows parent objects to react upon Chris@0: * changes of contained properties or list items. Chris@0: * Chris@0: * @section sec_defining Defining data types Chris@0: * To define a new data type: Chris@0: * - Create a class that implements one of the Typed Data interfaces. Chris@0: * Typically, you will want to extend one of the classes listed in the Chris@0: * sections above as a starting point. Chris@0: * - Make your class into a DataType plugin. To do that, put it in namespace Chris@0: * \Drupal\yourmodule\Plugin\DataType (where "yourmodule" is your module's Chris@0: * short name), and add annotation of type Chris@0: * \Drupal\Core\TypedData\Annotation\DataType to the documentation header. Chris@0: * See the @link plugin_api Plugin API topic @endlink and the Chris@0: * @link annotation Annotations topic @endlink for more information. Chris@0: * Chris@0: * @section sec_using Using data types Chris@0: * The data types of the Typed Data API can be used in several ways, once they Chris@0: * have been defined: Chris@0: * - In the Field API, data types can be used as the class in the property Chris@0: * definition of the field. See the @link field Field API topic @endlink for Chris@0: * more information. Chris@0: * - In configuration schema files, you can use the unique ID ('id' annotation) Chris@0: * from any DataType plugin class as the 'type' value for an entry. See the Chris@18: * @link config_api Configuration API topic @endlink for more information. Chris@0: * - If you need to create a typed data object in code, first get the Chris@0: * typed_data_manager service from the container or by calling Chris@0: * \Drupal::typedDataManager(). Then pass the plugin ID to Chris@0: * $manager::createDataDefinition() to create an appropriate data definition Chris@0: * object. Then pass the data definition object and the value of the data to Chris@0: * $manager::create() to create a typed data object. Chris@0: * Chris@0: * @see plugin_api Chris@0: * @see container Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup testing Automated tests Chris@0: * @{ Chris@18: * Overview of PHPUnit and Nightwatch automated tests. Chris@0: * Chris@0: * The Drupal project has embraced a philosophy of using automated tests, Chris@0: * consisting of both unit tests (which test the functionality of classes at a Chris@0: * low level) and functional tests (which test the functionality of Drupal Chris@0: * systems at a higher level, usually involving web output). The goal is to Chris@0: * have test coverage for all or most of the components and features, and to Chris@0: * run the automated tests before any code is changed or added, to make sure Chris@0: * it doesn't break any existing functionality (regression testing). Chris@0: * Chris@0: * In order to implement this philosophy, developers need to do the following: Chris@0: * - When making a patch to fix a bug, make sure that the bug fix patch includes Chris@0: * a test that fails without the code change and passes with the code change. Chris@0: * This helps reviewers understand what the bug is, demonstrates that the code Chris@0: * actually fixes the bug, and ensures the bug will not reappear due to later Chris@0: * code changes. Chris@0: * - When making a patch to implement a new feature, include new unit and/or Chris@0: * functional tests in the patch. This serves to both demonstrate that the Chris@0: * code actually works, and ensure that later changes do not break the new Chris@0: * functionality. Chris@0: * Chris@18: * @section write_test Writing tests Chris@18: * All PHP-based tests for Drupal core are written using the industry-standard Chris@18: * PHPUnit framework, with Drupal extensions. There are several categories of Chris@18: * tests; each has its own purpose, base class, namespace, and directory: Chris@18: * - Unit tests: Chris@18: * - Purpose: Test functionality of a class if the Drupal environment Chris@18: * (database, settings, etc.) and web browser are not needed for the test, Chris@18: * or if the Drupal environment can be replaced by a "mock" object. Chris@18: * - Base class: \Drupal\Tests\UnitTestCase Chris@18: * - Namespace: \Drupal\Tests\yourmodule\Unit (or a subdirectory) Chris@18: * - Directory location: yourmodule/tests/src/Unit (or a subdirectory) Chris@18: * - Kernel tests: Chris@18: * - Purpose: Test functionality of a class if the full Drupal environment Chris@18: * and web browser are not needed for the test, but the functionality has Chris@18: * significant Drupal dependencies that cannot easily be mocked. Kernel Chris@18: * tests can access services, the database, and a minimal mocked file Chris@18: * system, and they use an in-memory pseudo-installation. However, modules Chris@18: * are only installed to the point of having services and hooks, unless you Chris@18: * install them explicitly. Chris@18: * - Base class: \Drupal\KernelTests\KernelTestBase Chris@18: * - Namespace: \Drupal\Tests\yourmodule\Kernel (or a subdirectory) Chris@18: * - Directory location: yourmodule/tests/src/Kernel (or a subdirectory) Chris@18: * - Browser tests: Chris@18: * - Purpose: Test functionality with the full Drupal environment and an Chris@18: * internal simulated web browser, if JavaScript is not needed. Chris@18: * - Base class: \Drupal\Tests\BrowserTestBase Chris@18: * - Namespace: \Drupal\Tests\yourmodule\Functional (or a subdirectory) Chris@18: * - Directory location: yourmodule/tests/src/Functional (or a subdirectory) Chris@18: * - Browser tests with JavaScript: Chris@18: * - Purpose: Test functionality with the full Drupal environment and an Chris@18: * internal web browser that includes JavaScript execution. Chris@18: * - Base class: \Drupal\FunctionalJavascriptTests\WebDriverTestBase Chris@18: * - Namespace: \Drupal\Tests\yourmodule\FunctionalJavascript (or a Chris@18: * subdirectory) Chris@18: * - Directory location: yourmodule/tests/src/FunctionalJavascript (or a Chris@18: * subdirectory) Chris@18: * Chris@18: * Some notes about writing PHP test classes: Chris@18: * - The class needs a phpDoc comment block with a description and Chris@18: * @group annotation, which gives information about the test. Chris@18: * - For unit tests, this comment block should also have @coversDefaultClass Chris@18: * annotation. Chris@18: * - When writing tests, put the test code into public methods, each covering a Chris@18: * logical subset of the functionality that is being tested. Chris@18: * - The test methods must have names starting with 'test'. For unit tests, the Chris@18: * test methods need to have a phpDoc block with @covers annotation telling Chris@18: * which class method they are testing. Chris@18: * - In some cases, you may need to write a test module to support your test; Chris@18: * put such modules under the yourmodule/tests/modules directory. Chris@18: * Chris@18: * Besides the PHPUnit tests described above, Drupal Core also includes a few Chris@18: * JavaScript-only tests, which use the Nightwatch.js framework to test Chris@18: * JavaScript code using only JavaScript. These are located in Chris@18: * core/tests/Drupal/Nightwatch. Chris@18: * Chris@0: * For more details, see: Chris@18: * - core/tests/README.md for instructions on running tests Chris@0: * - https://www.drupal.org/phpunit for full documentation on how to write Chris@18: * and run PHPUnit tests for Drupal. Chris@0: * - http://phpunit.de for general information on the PHPUnit framework. Chris@0: * - @link oo_conventions Object-oriented programming topic @endlink for more Chris@0: * on PSR-4, namespaces, and where to place classes. Chris@18: * - http://nightwatchjs.org/ for information about Nightwatch testing for Chris@18: * JavaScript Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup php_assert PHP Runtime Assert Statements Chris@0: * @{ Chris@0: * Use of the assert() statement in Drupal. Chris@0: * Chris@0: * Unit tests also use the term "assertion" to refer to test conditions, so to Chris@0: * avoid confusion the term "runtime assertion" will be used for the assert() Chris@0: * statement throughout the documentation. Chris@0: * Chris@0: * A runtime assertion is a statement that is expected to always be true at Chris@0: * the point in the code it appears at. They are tested using PHP's internal Chris@0: * @link http://php.net/assert assert() @endlink statement. If an Chris@0: * assertion is ever FALSE it indicates an error in the code or in module or Chris@0: * theme configuration files. User-provided configuration files should be Chris@0: * verified with standard control structures at all times, not just checked in Chris@0: * development environments with assert() statements on. Chris@0: * Chris@0: * The Drupal project primarily uses runtime assertions to enforce the Chris@0: * expectations of the API by failing when incorrect calls are made by code Chris@0: * under development. While PHP type hinting does this for objects and arrays, Chris@0: * runtime assertions do this for scalars (strings, integers, floats, etc.) and Chris@0: * complex data structures such as cache and render arrays. They ensure that Chris@16: * methods' return values are the documented data types. They also verify that Chris@0: * objects have been properly configured and set up by the service container. Chris@14: * They supplement unit tests by checking scenarios that do not have unit tests Chris@14: * written for them. Chris@0: * Chris@14: * There are two php settings which affect runtime assertions. The first, Chris@14: * assert.exception, should always be set to 1. The second is zend.assertions. Chris@14: * Set this to -1 in production and 1 in development. Chris@0: * Chris@0: * See https://www.drupal.org/node/2492225 for more information on runtime Chris@0: * assertions. Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup info_types Information types Chris@0: * @{ Chris@0: * Types of information in Drupal. Chris@0: * Chris@0: * Drupal has several distinct types of information, each with its own methods Chris@0: * for storage and retrieval: Chris@0: * - Content: Information meant to be displayed on your site: articles, basic Chris@0: * pages, images, files, custom blocks, etc. Content is stored and accessed Chris@0: * using @link entity_api Entities @endlink. Chris@0: * - Session: Information about individual users' interactions with the site, Chris@0: * such as whether they are logged in. This is really "state" information, but Chris@16: * it is not stored the same way so it's a separate type here. Session data is Chris@16: * accessed via \Symfony\Component\HttpFoundation\Request::getSession(), which Chris@16: * returns an instance of Chris@0: * \Symfony\Component\HttpFoundation\Session\SessionInterface. Chris@16: * See the @link session Sessions topic @endlink for more information. Chris@0: * - State: Information of a temporary nature, generally machine-generated and Chris@0: * not human-edited, about the current state of your site. Examples: the time Chris@0: * when Cron was last run, whether node access permissions need rebuilding, Chris@0: * etc. See @link state_api the State API topic @endlink for more information. Chris@0: * - Configuration: Information about your site that is generally (or at least Chris@0: * can be) human-edited, but is not Content, and is meant to be relatively Chris@0: * permanent. Examples: the name of your site, the content types and views Chris@0: * you have defined, etc. See Chris@0: * @link config_api the Configuration API topic @endlink for more information. Chris@0: * Chris@0: * @see cache Chris@0: * @see i18n Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup extending Extending and altering Drupal Chris@0: * @{ Chris@0: * Overview of extensions and alteration methods for Drupal. Chris@0: * Chris@0: * @section sec_types Types of extensions Chris@0: * Drupal's core behavior can be extended and altered via these three basic Chris@0: * types of extensions: Chris@0: * - Themes: Themes alter the appearance of Drupal sites. They can include Chris@0: * template files, which alter the HTML markup and other raw output of the Chris@0: * site; CSS files, which alter the styling applied to the HTML; and Chris@0: * JavaScript, Flash, images, and other files. For more information, see the Chris@0: * @link theme_render Theme system and render API topic @endlink and Chris@0: * https://www.drupal.org/docs/8/theming Chris@0: * - Modules: Modules add to or alter the behavior and functionality of Drupal, Chris@0: * by using one or more of the methods listed below. For more information Chris@0: * about creating modules, see https://www.drupal.org/developing/modules/8 Chris@0: * - Installation profiles: Installation profiles can be used to Chris@0: * create distributions, which are complete specific-purpose packages of Chris@0: * Drupal including additional modules, themes, and data. For more Chris@0: * information, see https://www.drupal.org/developing/distributions. Chris@0: * Chris@0: * @section sec_alter Alteration methods for modules Chris@0: * Here is a list of the ways that modules can alter or extend Drupal's core Chris@0: * behavior, or the behavior of other modules: Chris@0: * - Hooks: Specially-named functions that a module defines, which are Chris@0: * discovered and called at specific times, usually to alter behavior or data. Chris@0: * See the @link hooks Hooks topic @endlink for more information. Chris@0: * - Plugins: Classes that a module defines, which are discovered and Chris@0: * instantiated at specific times to add functionality. See the Chris@0: * @link plugin_api Plugin API topic @endlink for more information. Chris@0: * - Entities: Special plugins that define entity types for storing new types Chris@0: * of content or configuration in Drupal. See the Chris@0: * @link entity_api Entity API topic @endlink for more information. Chris@0: * - Services: Classes that perform basic operations within Drupal, such as Chris@0: * accessing the database and sending email. See the Chris@0: * @link container Dependency Injection Container and Services topic @endlink Chris@0: * for more information. Chris@0: * - Routing: Providing or altering "routes", which are URLs that Drupal Chris@0: * responds to, or altering routing behavior with event listener classes. Chris@0: * See the @link menu Routing and menu topic @endlink for more information. Chris@0: * - Events: Modules can register as event subscribers; when an event is Chris@0: * dispatched, a method is called on each registered subscriber, allowing each Chris@0: * one to react. See the @link events Events topic @endlink for more Chris@0: * information. Chris@0: * Chris@0: * @section sec_sample *.info.yml files Chris@0: * Extensions must each be located in a directory whose name matches the short Chris@0: * name (or machine name) of the extension, and this directory must contain a Chris@0: * file named machine_name.info.yml (where machine_name is the machine name of Chris@0: * the extension). See \Drupal\Core\Extension\InfoParserInterface::parse() for Chris@0: * documentation of the format of .info.yml files. Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup plugin_api Plugin API Chris@0: * @{ Chris@0: * Using the Plugin API Chris@0: * Chris@0: * @section sec_overview Overview and terminology Chris@0: * Chris@0: * The basic idea of plugins is to allow a particular module or subsystem of Chris@0: * Drupal to provide functionality in an extensible, object-oriented way. The Chris@0: * controlling module or subsystem defines the basic framework (interface) for Chris@0: * the functionality, and other modules can create plugins (implementing the Chris@0: * interface) with particular behaviors. The controlling module instantiates Chris@0: * existing plugins as needed, and calls methods to invoke their functionality. Chris@0: * Examples of functionality in Drupal Core that use plugins include: the block Chris@0: * system (block types are plugins), the entity/field system (entity types, Chris@0: * field types, field formatters, and field widgets are plugins), the image Chris@0: * manipulation system (image effects and image toolkits are plugins), and the Chris@0: * search system (search page types are plugins). Chris@0: * Chris@0: * Plugins are grouped into plugin types, each generally defined by an Chris@0: * interface. Each plugin type is managed by a plugin manager service, which Chris@0: * uses a plugin discovery method to discover provided plugins of that type and Chris@0: * instantiate them using a plugin factory. Chris@0: * Chris@0: * Some plugin types make use of the following concepts or components: Chris@0: * - Plugin derivatives: Allows a single plugin class to present itself as Chris@0: * multiple plugins. Example: the Menu module provides a block for each Chris@0: * defined menu via a block plugin derivative. Chris@0: * - Plugin mapping: Allows a plugin class to map a configuration string to an Chris@0: * instance, and have the plugin automatically instantiated without writing Chris@0: * additional code. Chris@0: * - Plugin collections: Provide a way to lazily instantiate a set of plugin Chris@0: * instances from a single plugin definition. Chris@0: * Chris@0: * There are several things a module developer may need to do with plugins: Chris@0: * - Define a completely new plugin type: see @ref sec_define below. Chris@0: * - Create a plugin of an existing plugin type: see @ref sec_create below. Chris@0: * - Perform tasks that involve plugins: see @ref sec_use below. Chris@0: * Chris@0: * See https://www.drupal.org/developing/api/8/plugins for more detailed Chris@0: * documentation on the plugin system. There are also topics for a few Chris@0: * of the many existing types of plugins: Chris@0: * - @link block_api Block API @endlink Chris@0: * - @link entity_api Entity API @endlink Chris@0: * - @link field Various types of field-related plugins @endlink Chris@0: * - @link views_plugins Views plugins @endlink (has links to topics covering Chris@0: * various specific types of Views plugins). Chris@0: * - @link search Search page plugins @endlink Chris@0: * Chris@0: * @section sec_define Defining a new plugin type Chris@0: * To define a new plugin type: Chris@0: * - Define an interface for the plugin. This describes the common set of Chris@0: * behavior, and the methods you will call on each plugin class that is Chris@0: * instantiated. Usually this interface will extend one or more of the Chris@0: * following interfaces: Chris@0: * - \Drupal\Component\Plugin\PluginInspectionInterface Chris@18: * - \Drupal\Component\Plugin\ConfigurableInterface Chris@18: * - \Drupal\Component\Plugin\DependentPluginInterface Chris@0: * - \Drupal\Component\Plugin\ContextAwarePluginInterface Chris@0: * - \Drupal\Core\Plugin\PluginFormInterface Chris@0: * - \Drupal\Core\Executable\ExecutableInterface Chris@0: * - (optional) Create a base class that provides a partial implementation of Chris@0: * the interface, for the convenience of developers wishing to create plugins Chris@0: * of your type. The base class usually extends Chris@0: * \Drupal\Core\Plugin\PluginBase, or one of the base classes that extends Chris@0: * this class. Chris@0: * - Choose a method for plugin discovery, and define classes as necessary. Chris@0: * See @ref sub_discovery below. Chris@0: * - Create a plugin manager/factory class and service, which will discover and Chris@0: * instantiate plugins. See @ref sub_manager below. Chris@0: * - Use the plugin manager to instantiate plugins. Call methods on your plugin Chris@0: * interface to perform the tasks of your plugin type. Chris@0: * - (optional) If appropriate, define a plugin collection. See @ref Chris@0: * sub_collection below for more information. Chris@0: * Chris@0: * @subsection sub_discovery Plugin discovery Chris@0: * Plugin discovery is the process your plugin manager uses to discover the Chris@0: * individual plugins of your type that have been defined by your module and Chris@0: * other modules. Plugin discovery methods are classes that implement Chris@0: * \Drupal\Component\Plugin\Discovery\DiscoveryInterface. Most plugin types use Chris@0: * one of the following discovery mechanisms: Chris@0: * - Annotation: Plugin classes are annotated and placed in a defined namespace Chris@0: * subdirectory. Most Drupal Core plugins use this method of discovery. Chris@0: * - Hook: Plugin modules need to implement a hook to tell the manager about Chris@0: * their plugins. Chris@0: * - YAML: Plugins are listed in YAML files. Drupal Core uses this method for Chris@0: * discovering local tasks and local actions. This is mainly useful if all Chris@0: * plugins use the same class, so it is kind of like a global derivative. Chris@0: * - Static: Plugin classes are registered within the plugin manager class Chris@0: * itself. Static discovery is only useful if modules cannot define new Chris@0: * plugins of this type (if the list of available plugins is static). Chris@0: * Chris@0: * It is also possible to define your own custom discovery mechanism or mix Chris@0: * methods together. And there are many more details, such as annotation Chris@0: * decorators, that apply to some of the discovery methods. See Chris@0: * https://www.drupal.org/developing/api/8/plugins for more details. Chris@0: * Chris@0: * The remainder of this documentation will assume Annotation-based discovery, Chris@0: * since this is the most common method. Chris@0: * Chris@0: * @subsection sub_manager Defining a plugin manager class and service Chris@0: * To define an annotation-based plugin manager: Chris@0: * - Choose a namespace subdirectory for your plugin. For example, search page Chris@0: * plugins go in directory Plugin/Search under the module namespace. Chris@0: * - Define an annotation class for your plugin type. This class should extend Chris@0: * \Drupal\Component\Annotation\Plugin, and for most plugin types, it should Chris@0: * contain member variables corresponding to the annotations plugins will Chris@0: * need to provide. All plugins have at least $id: a unique string Chris@0: * identifier. Chris@0: * - Define an alter hook for altering the discovered plugin definitions. You Chris@0: * should document the hook in a *.api.php file. Chris@0: * - Define a plugin manager class. This class should implement Chris@0: * \Drupal\Component\Plugin\PluginManagerInterface; most plugin managers do Chris@0: * this by extending \Drupal\Core\Plugin\DefaultPluginManager. If you do Chris@0: * extend the default plugin manager, the only method you will probably need Chris@0: * to define is the class constructor, which will need to call the parent Chris@0: * constructor to provide information about the annotation class and plugin Chris@0: * namespace for discovery, set up the alter hook, and possibly set up Chris@0: * caching. See classes that extend DefaultPluginManager for examples. Chris@0: * - Define a service for your plugin manager. See the Chris@0: * @link container Services topic for more information. @endlink Your service Chris@0: * definition should look something like this, referencing your manager Chris@0: * class and the parent (default) plugin manager service to inherit Chris@0: * constructor arguments: Chris@0: * @code Chris@0: * plugin.manager.mymodule: Chris@0: * class: Drupal\mymodule\MyPluginManager Chris@0: * parent: default_plugin_manager Chris@0: * @endcode Chris@0: * - If your plugin is configurable, you will also need to define the Chris@0: * configuration schema and possibly a configuration entity type. See the Chris@0: * @link config_api Configuration API topic @endlink for more information. Chris@0: * Chris@0: * @subsection sub_collection Defining a plugin collection Chris@0: * Some configurable plugin types allow administrators to create zero or more Chris@0: * instances of each plugin, each with its own configuration. For example, Chris@0: * a single block plugin can be configured several times, to display in Chris@0: * different regions of a theme, with different visibility settings, a Chris@0: * different title, or other plugin-specific settings. To make this possible, Chris@0: * a plugin type can make use of what's known as a plugin collection. Chris@0: * Chris@0: * A plugin collection is a class that extends Chris@0: * \Drupal\Component\Plugin\LazyPluginCollection or one of its subclasses; there Chris@0: * are several examples in Drupal Core. If your plugin type uses a plugin Chris@0: * collection, it will usually also have a configuration entity, and the entity Chris@0: * class should implement Chris@0: * \Drupal\Core\Entity\EntityWithPluginCollectionInterface. Again, there are Chris@0: * several examples in Drupal Core; see also the @link config_api Configuration Chris@0: * API topic @endlink for more information about configuration entities. Chris@0: * Chris@0: * @section sec_create Creating a plugin of an existing type Chris@0: * Assuming the plugin type uses annotation-based discovery, in order to create Chris@0: * a plugin of an existing type, you will be creating a class. This class must: Chris@0: * - Implement the plugin interface, so that it has the required methods Chris@0: * defined. Usually, you'll want to extend the plugin base class, if one has Chris@0: * been provided. Chris@0: * - Have the right annotation in its documentation header. See the Chris@0: * @link annotation Annotation topic @endlink for more information about Chris@0: * annotation. Chris@0: * - Be in the right plugin namespace, in order to be discovered. Chris@0: * Often, the easiest way to make sure this happens is to find an existing Chris@0: * example of a working plugin class of the desired type, and copy it into your Chris@0: * module as a starting point. Chris@0: * Chris@0: * You can also create a plugin derivative, which allows your plugin class Chris@0: * to present itself to the user interface as multiple plugins. To do this, Chris@0: * in addition to the plugin class, you'll need to create a separate plugin Chris@0: * derivative class implementing Chris@0: * \Drupal\Component\Plugin\Derivative\DerivativeInterface. The classes Chris@0: * \Drupal\system\Plugin\Block\SystemMenuBlock (plugin class) and Chris@0: * \Drupal\system\Plugin\Derivative\SystemMenuBlock (derivative class) are a Chris@0: * good example to look at. Chris@0: * Chris@0: * @section sec_use Performing tasks involving plugins Chris@0: * Here are the steps to follow to perform a task that involves plugins: Chris@0: * - Locate the machine name of the plugin manager service, and instantiate the Chris@0: * service. See the @link container Services topic @endlink for more Chris@0: * information on how to do this. Chris@0: * - On the plugin manager class, use methods like getDefinition(), Chris@0: * getDefinitions(), or other methods specific to particular plugin managers Chris@0: * to retrieve information about either specific plugins or the entire list of Chris@0: * defined plugins. Chris@0: * - Call the createInstance() method on the plugin manager to instantiate Chris@0: * individual plugin objects. Chris@0: * - Call methods on the plugin objects to perform the desired tasks. Chris@0: * Chris@0: * @see annotation Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup oo_conventions Objected-oriented programming conventions Chris@0: * @{ Chris@0: * PSR-4, namespaces, class naming, and other conventions. Chris@0: * Chris@0: * A lot of the PHP code in Drupal is object oriented (OO), making use of Chris@0: * @link http://php.net/manual/language.oop5.php PHP classes, interfaces, and traits @endlink Chris@0: * (which are loosely referred to as "classes" in the rest of this topic). The Chris@0: * following conventions and standards apply to this version of Drupal: Chris@0: * - Each class must be in its own file. Chris@0: * - Classes must be namespaced. If a module defines a class, the namespace Chris@0: * must start with \Drupal\module_name. If it is defined by Drupal Core for Chris@0: * use across many modules, the namespace should be \Drupal\Core or Chris@0: * \Drupal\Component, with the exception of the global class \Drupal. See Chris@0: * https://www.drupal.org/node/1353118 for more about namespaces. Chris@0: * - In order for the PSR-4-based class auto-loader to find the class, it must Chris@0: * be located in a directory corresponding to the namespace. For Chris@0: * module-defined classes, if the namespace is \Drupal\module_name\foo\bar, Chris@0: * then the class goes under the main module directory in directory Chris@0: * src/foo/bar. For Drupal-wide classes, if the namespace is Chris@0: * \Drupal\Core\foo\bar, then it goes in directory Chris@0: * core/lib/Drupal/Core/foo/bar. See https://www.drupal.org/node/2156625 for Chris@0: * more information about PSR-4. Chris@0: * - Some classes have annotations added to their documentation headers. See Chris@0: * the @link annotation Annotation topic @endlink for more information. Chris@0: * - Standard plugin discovery requires particular namespaces and annotation Chris@0: * for most plugin classes. See the Chris@0: * @link plugin_api Plugin API topic @endlink for more information. Chris@0: * - There are project-wide coding standards for OO code, including naming: Chris@0: * https://www.drupal.org/node/608152 Chris@0: * - Documentation standards for classes are covered on: Chris@0: * https://www.drupal.org/coding-standards/docs#classes Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup listing_page_class Page header for Classes page Chris@0: * @{ Chris@0: * Introduction to classes Chris@0: * Chris@0: * A lot of the PHP code in Drupal is object oriented (OO), making use of Chris@0: * @link http://php.net/manual/language.oop5.php PHP classes, interfaces, and traits. @endlink Chris@0: * See the Chris@0: * @link oo_conventions Objected-oriented programming conventions @endlink Chris@0: * for more information. Chris@0: * Chris@0: * @see oo_conventions Chris@0: * Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup listing_page_namespace Page header for Namespaces page Chris@0: * @{ Chris@0: * Introduction to namespaces Chris@0: * Chris@0: * PHP classes, interfaces, and traits in Drupal are Chris@14: * @link http://php.net/manual/language.namespaces.rationale.php namespaced. @endlink Chris@0: * See the Chris@0: * @link oo_conventions Objected-oriented programming conventions @endlink Chris@0: * for more information. Chris@0: * Chris@0: * @see oo_conventions Chris@0: * Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup best_practices Best practices for developers Chris@0: * @{ Chris@0: * Overview of standards and best practices for developers Chris@0: * Chris@0: * Ideally, all code that is included in Drupal Core and contributed modules, Chris@0: * themes, and distributions will be secure, internationalized, maintainable, Chris@0: * and efficient. In order to facilitate this, the Drupal community has Chris@0: * developed a set of guidelines and standards for developers to follow. Most of Chris@0: * these standards can be found under Chris@0: * @link https://www.drupal.org/developing/best-practices Best practices on Drupal.org @endlink Chris@0: * Chris@0: * Standards and best practices that developers should be aware of include: Chris@0: * - Security: https://www.drupal.org/writing-secure-code and the Chris@0: * @link sanitization Sanitization functions topic @endlink Chris@0: * - Coding standards: https://www.drupal.org/coding-standards Chris@0: * and https://www.drupal.org/coding-standards/docs Chris@0: * - Accessibility: https://www.drupal.org/node/1637990 (modules) and Chris@0: * https://www.drupal.org/node/464472 (themes) Chris@0: * - Usability: https://www.drupal.org/ui-standards Chris@0: * - Internationalization: @link i18n Internationalization topic @endlink Chris@0: * - Automated testing: @link testing Automated tests topic @endlink Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup utility Utility classes and functions Chris@0: * @{ Chris@0: * Overview of utility classes and functions for developers. Chris@0: * Chris@0: * Drupal provides developers with a variety of utility functions that make it Chris@0: * easier and more efficient to perform tasks that are either really common, Chris@0: * tedious, or difficult. Utility functions help to reduce code duplication and Chris@0: * should be used in place of one-off code whenever possible. Chris@0: * Chris@0: * @see common.inc Chris@0: * @see file Chris@0: * @see format Chris@0: * @see php_wrappers Chris@0: * @see sanitization Chris@0: * @see transliteration Chris@0: * @see validation Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup hooks Hooks Chris@0: * @{ Chris@0: * Define functions that alter the behavior of Drupal core. Chris@0: * Chris@0: * One way for modules to alter the core behavior of Drupal (or another module) Chris@0: * is to use hooks. Hooks are specially-named functions that a module defines Chris@0: * (this is known as "implementing the hook"), which are discovered and called Chris@0: * at specific times to alter or add to the base behavior or data (this is Chris@0: * known as "invoking the hook"). Each hook has a name (example: Chris@0: * hook_batch_alter()), a defined set of parameters, and a defined return value. Chris@0: * Your modules can implement hooks that are defined by Drupal core or other Chris@0: * modules that they interact with. Your modules can also define their own Chris@0: * hooks, in order to let other modules interact with them. Chris@0: * Chris@0: * To implement a hook: Chris@0: * - Locate the documentation for the hook. Hooks are documented in *.api.php Chris@0: * files, by defining functions whose name starts with "hook_" (these Chris@0: * files and their functions are never loaded by Drupal -- they exist solely Chris@0: * for documentation). The function should have a documentation header, as Chris@0: * well as a sample function body. For example, in the core file Chris@0: * system.api.php, you can find hooks such as hook_batch_alter(). Also, if Chris@0: * you are viewing this documentation on an API reference site, the Core Chris@0: * hooks will be listed in this topic. Chris@0: * - Copy the function to your module's .module file. Chris@0: * - Change the name of the function, substituting your module's short name Chris@0: * (name of the module's directory, and .info.yml file without the extension) Chris@0: * for the "hook" part of the sample function name. For instance, to implement Chris@0: * hook_batch_alter(), you would rename it to my_module_batch_alter(). Chris@0: * - Edit the documentation for the function (normally, your implementation Chris@0: * should just have one line saying "Implements hook_batch_alter()."). Chris@0: * - Edit the body of the function, substituting in what you need your module Chris@0: * to do. Chris@0: * Chris@0: * To define a hook: Chris@0: * - Choose a unique name for your hook. It should start with "hook_", followed Chris@0: * by your module's short name. Chris@0: * - Provide documentation in a *.api.php file in your module's main Chris@0: * directory. See the "implementing" section above for details of what this Chris@0: * should contain (parameters, return value, and sample function body). Chris@0: * - Invoke the hook in your module's code. Chris@0: * Chris@0: * To invoke a hook, use methods on Chris@0: * \Drupal\Core\Extension\ModuleHandlerInterface such as alter(), invoke(), Chris@0: * and invokeAll(). You can obtain a module handler by calling Chris@0: * \Drupal::moduleHandler(), or getting the 'module_handler' service on an Chris@0: * injected container. Chris@0: * Chris@0: * @see extending Chris@0: * @see themeable Chris@0: * @see callbacks Chris@0: * @see \Drupal\Core\Extension\ModuleHandlerInterface Chris@0: * @see \Drupal::moduleHandler() Chris@0: * Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup callbacks Callbacks Chris@0: * @{ Chris@0: * Callback function signatures. Chris@0: * Chris@0: * Drupal's API sometimes uses callback functions to allow you to define how Chris@0: * some type of processing happens. A callback is a function with a defined Chris@0: * signature, which you define in a module. Then you pass the function name as Chris@0: * a parameter to a Drupal API function or return it as part of a hook Chris@0: * implementation return value, and your function is called at an appropriate Chris@0: * time. For instance, when setting up batch processing you might need to Chris@0: * provide a callback function for each processing step and/or a callback for Chris@0: * when processing is finished; you would do that by defining these functions Chris@0: * and passing their names into the batch setup function. Chris@0: * Chris@0: * Callback function signatures, like hook definitions, are described by Chris@0: * creating and documenting dummy functions in a *.api.php file; normally, the Chris@0: * dummy callback function's name should start with "callback_", and you should Chris@0: * document the parameters and return value and provide a sample function body. Chris@0: * Then your API documentation can refer to this callback function in its Chris@0: * documentation. A user of your API can usually name their callback function Chris@0: * anything they want, although a standard name would be to replace "callback_" Chris@0: * with the module name. Chris@0: * Chris@0: * @see hooks Chris@0: * @see themeable Chris@0: * Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup form_api Form generation Chris@0: * @{ Chris@0: * Describes how to generate and manipulate forms and process form submissions. Chris@0: * Chris@0: * Drupal provides a Form API in order to achieve consistency in its form Chris@0: * processing and presentation, while simplifying code and reducing the amount Chris@0: * of HTML that must be explicitly generated by a module. Chris@0: * Chris@0: * @section generating_forms Creating forms Chris@0: * Forms are defined as classes that implement the Chris@0: * \Drupal\Core\Form\FormInterface and are built using the Chris@0: * \Drupal\Core\Form\FormBuilder class. Drupal provides a couple of utility Chris@0: * classes that can be extended as a starting point for most basic forms, the Chris@0: * most commonly used of which is \Drupal\Core\Form\FormBase. FormBuilder Chris@0: * handles the low level processing of forms such as rendering the necessary Chris@0: * HTML, initial processing of incoming $_POST data, and delegating to your Chris@0: * implementation of FormInterface for validation and processing of submitted Chris@0: * data. Chris@0: * Chris@0: * Here is an example of a Form class: Chris@0: * @code Chris@0: * namespace Drupal\mymodule\Form; Chris@0: * Chris@0: * use Drupal\Core\Form\FormBase; Chris@0: * use Drupal\Core\Form\FormStateInterface; Chris@0: * Chris@0: * class ExampleForm extends FormBase { Chris@0: * public function getFormId() { Chris@0: * // Unique ID of the form. Chris@0: * return 'example_form'; Chris@0: * } Chris@0: * Chris@0: * public function buildForm(array $form, FormStateInterface $form_state) { Chris@0: * // Create a $form API array. Chris@0: * $form['phone_number'] = array( Chris@0: * '#type' => 'tel', Chris@0: * '#title' => $this->t('Your phone number'), Chris@0: * ); Chris@0: * $form['save'] = array( Chris@0: * '#type' => 'submit', Chris@0: * '#value' => $this->t('Save'), Chris@0: * ); Chris@0: * return $form; Chris@0: * } Chris@0: * Chris@0: * public function validateForm(array &$form, FormStateInterface $form_state) { Chris@0: * // Validate submitted form data. Chris@0: * } Chris@0: * Chris@0: * public function submitForm(array &$form, FormStateInterface $form_state) { Chris@0: * // Handle submitted form data. Chris@0: * } Chris@0: * } Chris@0: * @endcode Chris@0: * Chris@0: * @section retrieving_forms Retrieving and displaying forms Chris@0: * \Drupal::formBuilder()->getForm() should be used to handle retrieving, Chris@0: * processing, and displaying a rendered HTML form. Given the ExampleForm Chris@0: * defined above, Chris@0: * \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm') would Chris@0: * return the rendered HTML of the form defined by ExampleForm::buildForm(), or Chris@0: * call the validateForm() and submitForm(), methods depending on the current Chris@0: * processing state. Chris@0: * Chris@0: * The argument to \Drupal::formBuilder()->getForm() is the name of a class that Chris@0: * implements FormInterface. Any additional arguments passed to the getForm() Chris@0: * method will be passed along as additional arguments to the Chris@0: * ExampleForm::buildForm() method. Chris@0: * Chris@0: * For example: Chris@0: * @code Chris@0: * $extra = '612-123-4567'; Chris@0: * $form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm', $extra); Chris@0: * ... Chris@0: * public function buildForm(array $form, FormStateInterface $form_state, $extra = NULL) Chris@0: * $form['phone_number'] = array( Chris@0: * '#type' => 'tel', Chris@0: * '#title' => $this->t('Your phone number'), Chris@0: * '#value' => $extra, Chris@0: * ); Chris@0: * return $form; Chris@0: * } Chris@0: * @endcode Chris@0: * Chris@0: * Alternatively, forms can be built directly via the routing system which will Chris@0: * take care of calling \Drupal::formBuilder()->getForm(). The following example Chris@0: * demonstrates the use of a routing.yml file to display a form at the given Chris@0: * route. Chris@0: * Chris@0: * @code Chris@0: * example.form: Chris@0: * path: '/example-form' Chris@0: * defaults: Chris@0: * _title: 'Example form' Chris@0: * _form: '\Drupal\mymodule\Form\ExampleForm' Chris@0: * @endcode Chris@0: * Chris@0: * The $form argument to form-related functions is a specialized render array Chris@0: * containing the elements and properties of the form. For more about render Chris@0: * arrays, see the @link theme_render Render API topic. @endlink For more Chris@0: * detailed explanations of the Form API workflow, see the Chris@0: * @link https://www.drupal.org/node/2117411 Form API documentation section. @endlink Chris@0: * In addition, there is a set of Form API tutorials in the Chris@0: * @link https://www.drupal.org/project/examples Examples for Developers project. @endlink Chris@0: * Chris@0: * In the form builder, validation, submission, and other form methods, Chris@0: * $form_state is the primary influence on the processing of the form and is Chris@0: * passed to most methods, so they can use it to communicate with the form Chris@0: * system and each other. $form_state is an object that implements Chris@0: * \Drupal\Core\Form\FormStateInterface. Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup queue Queue operations Chris@0: * @{ Chris@0: * Queue items to allow later processing. Chris@0: * Chris@0: * The queue system allows placing items in a queue and processing them later. Chris@0: * The system tries to ensure that only one consumer can process an item. Chris@0: * Chris@0: * Before a queue can be used it needs to be created by Chris@0: * Drupal\Core\Queue\QueueInterface::createQueue(). Chris@0: * Chris@0: * Items can be added to the queue by passing an arbitrary data object to Chris@0: * Drupal\Core\Queue\QueueInterface::createItem(). Chris@0: * Chris@0: * To process an item, call Drupal\Core\Queue\QueueInterface::claimItem() and Chris@0: * specify how long you want to have a lease for working on that item. Chris@0: * When finished processing, the item needs to be deleted by calling Chris@0: * Drupal\Core\Queue\QueueInterface::deleteItem(). If the consumer dies, the Chris@0: * item will be made available again by the Drupal\Core\Queue\QueueInterface Chris@0: * implementation once the lease expires. Another consumer will then be able to Chris@0: * receive it when calling Drupal\Core\Queue\QueueInterface::claimItem(). Chris@0: * Due to this, the processing code should be aware that an item might be handed Chris@0: * over for processing more than once. Chris@0: * Chris@0: * The $item object used by the Drupal\Core\Queue\QueueInterface can contain Chris@0: * arbitrary metadata depending on the implementation. Systems using the Chris@0: * interface should only rely on the data property which will contain the Chris@0: * information passed to Drupal\Core\Queue\QueueInterface::createItem(). Chris@0: * The full queue item returned by Drupal\Core\Queue\QueueInterface::claimItem() Chris@0: * needs to be passed to Drupal\Core\Queue\QueueInterface::deleteItem() once Chris@0: * processing is completed. Chris@0: * Chris@0: * There are two kinds of queue backends available: reliable, which preserves Chris@0: * the order of messages and guarantees that every item will be executed at Chris@0: * least once. The non-reliable kind only does a best effort to preserve order Chris@0: * in messages and to execute them at least once but there is a small chance Chris@0: * that some items get lost. For example, some distributed back-ends like Chris@0: * Amazon SQS will be managing jobs for a large set of producers and consumers Chris@0: * where a strict FIFO ordering will likely not be preserved. Another example Chris@0: * would be an in-memory queue backend which might lose items if it crashes. Chris@0: * However, such a backend would be able to deal with significantly more writes Chris@0: * than a reliable queue and for many tasks this is more important. See Chris@0: * aggregator_cron() for an example of how to effectively use a non-reliable Chris@0: * queue. Another example is doing Twitter statistics -- the small possibility Chris@0: * of losing a few items is insignificant next to power of the queue being able Chris@0: * to keep up with writes. As described in the processing section, regardless Chris@0: * of the queue being reliable or not, the processing code should be aware that Chris@0: * an item might be handed over for processing more than once (because the Chris@0: * processing code might time out before it finishes). Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup annotation Annotations Chris@0: * @{ Chris@0: * Annotations for class discovery and metadata description. Chris@0: * Chris@0: * The Drupal plugin system has a set of reusable components that developers Chris@0: * can use, override, and extend in their modules. Most of the plugins use Chris@0: * annotations, which let classes register themselves as plugins and describe Chris@0: * their metadata. (Annotations can also be used for other purposes, though Chris@0: * at the moment, Drupal only uses them for the plugin system.) Chris@0: * Chris@0: * To annotate a class as a plugin, add code similar to the following to the Chris@0: * end of the documentation block immediately preceding the class declaration: Chris@0: * @code Chris@0: * * @ContentEntityType( Chris@0: * * id = "comment", Chris@0: * * label = @Translation("Comment"), Chris@0: * * ... Chris@0: * * base_table = "comment" Chris@0: * * ) Chris@0: * @endcode Chris@0: * Chris@0: * Note that you must use double quotes; single quotes will not work in Chris@0: * annotations. Chris@0: * Chris@0: * Some annotation types, which extend the "@ PluginID" annotation class, have Chris@0: * only a single 'id' key in their annotation. For these, it is possible to use Chris@0: * a shorthand annotation. For example: Chris@0: * @code Chris@0: * * @ViewsArea("entity") Chris@0: * @endcode Chris@0: * in place of Chris@0: * @code Chris@0: * * @ViewsArea( Chris@0: * * id = "entity" Chris@0: * *) Chris@0: * @endcode Chris@0: * Chris@0: * The available annotation classes are listed in this topic, and can be Chris@0: * identified when you are looking at the Drupal source code by having Chris@0: * "@ Annotation" in their documentation blocks (without the space after @). To Chris@0: * find examples of annotation for a particular annotation class, such as Chris@0: * EntityType, look for class files that have an @ annotation section using the Chris@0: * annotation class. Chris@0: * Chris@0: * @see plugin_translatable Chris@0: * @see plugin_context Chris@0: * Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @addtogroup hooks Chris@0: * @{ Chris@0: */ Chris@0: Chris@0: /** Chris@0: * Perform periodic actions. Chris@0: * Chris@0: * Modules that require some commands to be executed periodically can Chris@0: * implement hook_cron(). The engine will then call the hook whenever a cron Chris@0: * run happens, as defined by the administrator. Typical tasks managed by Chris@0: * hook_cron() are database maintenance, backups, recalculation of settings Chris@0: * or parameters, automated mailing, and retrieving remote data. Chris@0: * Chris@0: * Short-running or non-resource-intensive tasks can be executed directly in Chris@0: * the hook_cron() implementation. Chris@0: * Chris@0: * Long-running tasks and tasks that could time out, such as retrieving remote Chris@0: * data, sending email, and intensive file tasks, should use the queue API Chris@0: * instead of executing the tasks directly. To do this, first define one or Chris@0: * more queues via a \Drupal\Core\Annotation\QueueWorker plugin. Then, add items Chris@0: * that need to be processed to the defined queues. Chris@0: */ Chris@0: function hook_cron() { Chris@0: // Short-running operation example, not using a queue: Chris@0: // Delete all expired records since the last cron run. Chris@0: $expires = \Drupal::state()->get('mymodule.last_check', 0); Chris@0: \Drupal::database()->delete('mymodule_table') Chris@0: ->condition('expires', $expires, '>=') Chris@0: ->execute(); Chris@0: \Drupal::state()->set('mymodule.last_check', REQUEST_TIME); Chris@0: Chris@0: // Long-running operation example, leveraging a queue: Chris@0: // Queue news feeds for updates once their refresh interval has elapsed. Chris@0: $queue = \Drupal::queue('aggregator_feeds'); Chris@0: $ids = \Drupal::entityManager()->getStorage('aggregator_feed')->getFeedIdsToRefresh(); Chris@0: foreach (Feed::loadMultiple($ids) as $feed) { Chris@0: if ($queue->createItem($feed)) { Chris@0: // Add timestamp to avoid queueing item more than once. Chris@0: $feed->setQueuedTime(REQUEST_TIME); Chris@0: $feed->save(); Chris@0: } Chris@0: } Chris@0: $ids = \Drupal::entityQuery('aggregator_feed') Chris@0: ->condition('queued', REQUEST_TIME - (3600 * 6), '<') Chris@0: ->execute(); Chris@0: if ($ids) { Chris@0: $feeds = Feed::loadMultiple($ids); Chris@0: foreach ($feeds as $feed) { Chris@0: $feed->setQueuedTime(0); Chris@0: $feed->save(); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Alter available data types for typed data wrappers. Chris@0: * Chris@0: * @param array $data_types Chris@0: * An array of data type information. Chris@0: * Chris@0: * @see hook_data_type_info() Chris@0: */ Chris@0: function hook_data_type_info_alter(&$data_types) { Chris@0: $data_types['email']['class'] = '\Drupal\mymodule\Type\Email'; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Alter cron queue information before cron runs. Chris@0: * Chris@0: * Called by \Drupal\Core\Cron to allow modules to alter cron queue settings Chris@16: * before any jobs are processed. Chris@0: * Chris@0: * @param array $queues Chris@0: * An array of cron queue information. Chris@0: * Chris@16: * @see \Drupal\Core\Queue\QueueWorkerInterface Chris@0: * @see \Drupal\Core\Annotation\QueueWorker Chris@0: * @see \Drupal\Core\Cron Chris@0: */ Chris@0: function hook_queue_info_alter(&$queues) { Chris@0: // This site has many feeds so let's spend 90 seconds on each cron run Chris@0: // updating feeds instead of the default 60. Chris@0: $queues['aggregator_feeds']['cron']['time'] = 90; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Alter an email message created with MailManagerInterface->mail(). Chris@0: * Chris@17: * Hook hook_mail_alter() allows modification of email messages created and sent Chris@0: * with MailManagerInterface->mail(). Usage examples include adding and/or Chris@0: * changing message text, message fields, and message headers. Chris@0: * Chris@0: * Email messages sent using functions other than MailManagerInterface->mail() Chris@0: * will not invoke hook_mail_alter(). For example, a contributed module directly Chris@0: * calling the MailInterface->mail() or PHP mail() function will not invoke Chris@0: * this hook. All core modules use MailManagerInterface->mail() for messaging, Chris@0: * it is best practice but not mandatory in contributed modules. Chris@0: * Chris@0: * @param $message Chris@0: * An array containing the message data. Keys in this array include: Chris@0: * - 'id': Chris@0: * The MailManagerInterface->mail() id of the message. Look at module source Chris@0: * code or MailManagerInterface->mail() for possible id values. Chris@0: * - 'to': Chris@0: * The address or addresses the message will be sent to. The Chris@0: * formatting of this string must comply with RFC 2822. Chris@0: * - 'from': Chris@0: * The address the message will be marked as being from, which is Chris@0: * either a custom address or the site-wide default email address. Chris@0: * - 'subject': Chris@0: * Subject of the email to be sent. This must not contain any newline Chris@0: * characters, or the email may not be sent properly. Chris@0: * - 'body': Chris@0: * An array of strings or objects that implement Chris@0: * \Drupal\Component\Render\MarkupInterface containing the message text. The Chris@0: * message body is created by concatenating the individual array strings Chris@0: * into a single text string using "\n\n" as a separator. Chris@0: * - 'headers': Chris@0: * Associative array containing mail headers, such as From, Sender, Chris@0: * MIME-Version, Content-Type, etc. Chris@0: * - 'params': Chris@0: * An array of optional parameters supplied by the caller of Chris@0: * MailManagerInterface->mail() that is used to build the message before Chris@0: * hook_mail_alter() is invoked. Chris@0: * - 'language': Chris@0: * The language object used to build the message before hook_mail_alter() Chris@0: * is invoked. Chris@0: * - 'send': Chris@0: * Set to FALSE to abort sending this email message. Chris@0: * Chris@0: * @see \Drupal\Core\Mail\MailManagerInterface::mail() Chris@0: */ Chris@0: function hook_mail_alter(&$message) { Chris@0: if ($message['id'] == 'modulename_messagekey') { Chris@0: if (!example_notifications_optin($message['to'], $message['id'])) { Chris@0: // If the recipient has opted to not receive such messages, cancel Chris@0: // sending. Chris@0: $message['send'] = FALSE; Chris@0: return; Chris@0: } Chris@0: $message['body'][] = "--\nMail sent out from " . \Drupal::config('system.site')->get('name'); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Prepares a message based on parameters; Chris@0: * Chris@0: * This hook is called from MailManagerInterface->mail(). Note that hook_mail(), Chris@0: * unlike hook_mail_alter(), is only called on the $module argument to Chris@0: * MailManagerInterface->mail(), not all modules. Chris@0: * Chris@0: * @param $key Chris@0: * An identifier of the mail. Chris@0: * @param $message Chris@0: * An array to be filled in. Elements in this array include: Chris@0: * - id: An ID to identify the mail sent. Look at module source code or Chris@0: * MailManagerInterface->mail() for possible id values. Chris@0: * - to: The address or addresses the message will be sent to. The Chris@0: * formatting of this string must comply with RFC 2822. Chris@0: * - subject: Subject of the email to be sent. This must not contain any Chris@0: * newline characters, or the mail may not be sent properly. Chris@0: * MailManagerInterface->mail() sets this to an empty Chris@0: * string when the hook is invoked. Chris@0: * - body: An array of lines containing the message to be sent. Drupal will Chris@0: * format the correct line endings for you. MailManagerInterface->mail() Chris@0: * sets this to an empty array when the hook is invoked. The array may Chris@0: * contain either strings or objects implementing Chris@0: * \Drupal\Component\Render\MarkupInterface. Chris@0: * - from: The address the message will be marked as being from, which is Chris@0: * set by MailManagerInterface->mail() to either a custom address or the Chris@0: * site-wide default email address when the hook is invoked. Chris@0: * - headers: Associative array containing mail headers, such as From, Chris@0: * Sender, MIME-Version, Content-Type, etc. Chris@0: * MailManagerInterface->mail() pre-fills several headers in this array. Chris@0: * @param $params Chris@0: * An array of parameters supplied by the caller of Chris@0: * MailManagerInterface->mail(). Chris@0: * Chris@0: * @see \Drupal\Core\Mail\MailManagerInterface::mail() Chris@0: */ Chris@0: function hook_mail($key, &$message, $params) { Chris@0: $account = $params['account']; Chris@0: $context = $params['context']; Chris@0: $variables = [ Chris@0: '%site_name' => \Drupal::config('system.site')->get('name'), Chris@0: '%username' => $account->getDisplayName(), Chris@0: ]; Chris@0: if ($context['hook'] == 'taxonomy') { Chris@0: $entity = $params['entity']; Chris@0: $vocabulary = Vocabulary::load($entity->id()); Chris@0: $variables += [ Chris@0: '%term_name' => $entity->name, Chris@0: '%term_description' => $entity->description, Chris@0: '%term_id' => $entity->id(), Chris@0: '%vocabulary_name' => $vocabulary->label(), Chris@0: '%vocabulary_description' => $vocabulary->getDescription(), Chris@0: '%vocabulary_id' => $vocabulary->id(), Chris@0: ]; Chris@0: } Chris@0: Chris@0: // Node-based variable translation is only available if we have a node. Chris@0: if (isset($params['node'])) { Chris@0: /** @var \Drupal\node\NodeInterface $node */ Chris@0: $node = $params['node']; Chris@0: $variables += [ Chris@0: '%uid' => $node->getOwnerId(), Chris@18: '%url' => $node->toUrl('canonical', ['absolute' => TRUE])->toString(), Chris@0: '%node_type' => node_get_type_label($node), Chris@0: '%title' => $node->getTitle(), Chris@0: '%teaser' => $node->teaser, Chris@0: '%body' => $node->body, Chris@0: ]; Chris@0: } Chris@0: $subject = strtr($context['subject'], $variables); Chris@0: $body = strtr($context['message'], $variables); Chris@0: $message['subject'] .= str_replace(["\r", "\n"], '', $subject); Chris@0: $message['body'][] = MailFormatHelper::htmlToText($body); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Alter the list of mail backend plugin definitions. Chris@0: * Chris@0: * @param array $info Chris@0: * The mail backend plugin definitions to be altered. Chris@0: * Chris@0: * @see \Drupal\Core\Annotation\Mail Chris@0: * @see \Drupal\Core\Mail\MailManager Chris@0: */ Chris@0: function hook_mail_backend_info_alter(&$info) { Chris@0: unset($info['test_mail_collector']); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Alter the default country list. Chris@0: * Chris@0: * @param $countries Chris@0: * The associative array of countries keyed by two-letter country code. Chris@0: * Chris@0: * @see \Drupal\Core\Locale\CountryManager::getList() Chris@0: */ Chris@0: function hook_countries_alter(&$countries) { Chris@0: // Elbonia is now independent, so add it to the country list. Chris@0: $countries['EB'] = 'Elbonia'; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Alter display variant plugin definitions. Chris@0: * Chris@0: * @param array $definitions Chris@0: * The array of display variant definitions, keyed by plugin ID. Chris@0: * Chris@0: * @see \Drupal\Core\Display\VariantManager Chris@0: * @see \Drupal\Core\Display\Annotation\DisplayVariant Chris@0: */ Chris@0: function hook_display_variant_plugin_alter(array &$definitions) { Chris@0: $definitions['full_page']['admin_label'] = t('Block layout'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Allow modules to alter layout plugin definitions. Chris@0: * Chris@0: * @param \Drupal\Core\Layout\LayoutDefinition[] $definitions Chris@0: * The array of layout definitions, keyed by plugin ID. Chris@0: */ Chris@0: function hook_layout_alter(&$definitions) { Chris@0: // Remove a layout. Chris@0: unset($definitions['twocol']); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Flush all persistent and static caches. Chris@0: * Chris@0: * This hook asks your module to clear all of its static caches, Chris@0: * in order to ensure a clean environment for subsequently Chris@0: * invoked data rebuilds. Chris@0: * Chris@0: * Do NOT use this hook for rebuilding information. Only use it to flush custom Chris@0: * caches. Chris@0: * Chris@0: * Static caches using drupal_static() do not need to be reset manually. Chris@0: * However, all other static variables that do not use drupal_static() must be Chris@0: * manually reset. Chris@0: * Chris@0: * This hook is invoked by drupal_flush_all_caches(). It runs before module data Chris@0: * is updated and before hook_rebuild(). Chris@0: * Chris@0: * @see drupal_flush_all_caches() Chris@0: * @see hook_rebuild() Chris@0: */ Chris@0: function hook_cache_flush() { Chris@0: if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') { Chris@0: _update_cache_clear(); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Rebuild data based upon refreshed caches. Chris@0: * Chris@0: * This hook allows your module to rebuild its data based on the latest/current Chris@0: * module data. It runs after hook_cache_flush() and after all module data has Chris@0: * been updated. Chris@0: * Chris@0: * This hook is only invoked after the system has been completely cleared; Chris@0: * i.e., all previously cached data is known to be gone and every API in the Chris@0: * system is known to return current information, so your module can safely rely Chris@0: * on all available data to rebuild its own. Chris@0: * Chris@0: * @see hook_cache_flush() Chris@0: * @see drupal_flush_all_caches() Chris@0: */ Chris@0: function hook_rebuild() { Chris@0: $themes = \Drupal::service('theme_handler')->listInfo(); Chris@0: foreach ($themes as $theme) { Chris@0: _block_rehash($theme->getName()); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Alter the configuration synchronization steps. Chris@0: * Chris@0: * @param array $sync_steps Chris@0: * A one-dimensional array of \Drupal\Core\Config\ConfigImporter method names Chris@0: * or callables that are invoked to complete the import, in the order that Chris@0: * they will be processed. Each callable item defined in $sync_steps should Chris@0: * either be a global function or a public static method. The callable should Chris@0: * accept a $context array by reference. For example: Chris@16: * @code Chris@0: * function _additional_configuration_step(&$context) { Chris@0: * // Do stuff. Chris@0: * // If finished set $context['finished'] = 1. Chris@0: * } Chris@16: * @endcode Chris@0: * For more information on creating batches, see the Chris@0: * @link batch Batch operations @endlink documentation. Chris@0: * Chris@0: * @see callback_batch_operation() Chris@0: * @see \Drupal\Core\Config\ConfigImporter::initialize() Chris@0: */ Chris@0: function hook_config_import_steps_alter(&$sync_steps, \Drupal\Core\Config\ConfigImporter $config_importer) { Chris@0: $deletes = $config_importer->getUnprocessedConfiguration('delete'); Chris@0: if (isset($deletes['field.storage.node.body'])) { Chris@0: $sync_steps[] = '_additional_configuration_step'; Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Alter config typed data definitions. Chris@0: * Chris@0: * For example you can alter the typed data types representing each Chris@0: * configuration schema type to change default labels or form element renderers Chris@0: * used for configuration translation. Chris@0: * Chris@0: * If implementations of this hook add or remove configuration schema a Chris@0: * ConfigSchemaAlterException will be thrown. Keep in mind that there are tools Chris@0: * that may use the configuration schema for static analysis of configuration Chris@0: * files, like the string extractor for the localization system. Such systems Chris@0: * won't work with dynamically defined configuration schemas. Chris@0: * Chris@0: * For adding new data types use configuration schema YAML files instead. Chris@0: * Chris@0: * @param $definitions Chris@0: * Associative array of configuration type definitions keyed by schema type Chris@0: * names. The elements are themselves array with information about the type. Chris@0: * Chris@0: * @see \Drupal\Core\Config\TypedConfigManager Chris@0: * @see \Drupal\Core\Config\Schema\ConfigSchemaAlterException Chris@0: */ Chris@0: function hook_config_schema_info_alter(&$definitions) { Chris@0: // Enhance the text and date type definitions with classes to generate proper Chris@0: // form elements in ConfigTranslationFormBase. Other translatable types will Chris@0: // appear as a one line textfield. Chris@0: $definitions['text']['form_element_class'] = '\Drupal\config_translation\FormElement\Textarea'; Chris@0: $definitions['date_format']['form_element_class'] = '\Drupal\config_translation\FormElement\DateFormat'; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Alter validation constraint plugin definitions. Chris@0: * Chris@0: * @param array[] $definitions Chris@0: * The array of validation constraint definitions, keyed by plugin ID. Chris@0: * Chris@0: * @see \Drupal\Core\Validation\ConstraintManager Chris@0: * @see \Drupal\Core\Validation\Annotation\Constraint Chris@0: */ Chris@0: function hook_validation_constraint_alter(array &$definitions) { Chris@0: $definitions['Null']['class'] = '\Drupal\mymodule\Validator\Constraints\MyClass'; Chris@0: } Chris@0: Chris@0: /** Chris@0: * @} End of "addtogroup hooks". Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup ajax Ajax API Chris@0: * @{ Chris@0: * Overview for Drupal's Ajax API. Chris@0: * Chris@0: * @section sec_overview Overview of Ajax Chris@0: * Ajax is the process of dynamically updating parts of a page's HTML based on Chris@0: * data from the server. When a specified event takes place, a PHP callback is Chris@0: * triggered, which performs server-side logic and may return updated markup or Chris@0: * JavaScript commands to run. After the return, the browser runs the JavaScript Chris@0: * or updates the markup on the fly, with no full page refresh necessary. Chris@0: * Chris@0: * Many different events can trigger Ajax responses, including: Chris@0: * - Clicking a button Chris@0: * - Pressing a key Chris@0: * - Moving the mouse Chris@0: * Chris@0: * @section sec_framework Ajax responses in forms Chris@0: * Forms that use the Drupal Form API (see the Chris@0: * @link form_api Form API topic @endlink for more information about forms) can Chris@0: * trigger AJAX responses. Here is an outline of the steps: Chris@0: * - Add property '#ajax' to a form element in your form array, to trigger an Chris@0: * Ajax response. Chris@0: * - Write an Ajax callback to process the input and respond. Chris@0: * See sections below for details on these two steps. Chris@0: * Chris@0: * @subsection sub_form Adding Ajax triggers to a form Chris@0: * As an example of adding Ajax triggers to a form, consider editing a date Chris@0: * format, where the user is provided with a sample of the generated date output Chris@0: * as they type. To accomplish this, typing in the text field should trigger an Chris@0: * Ajax response. This is done in the text field form array element Chris@0: * in \Drupal\config_translation\FormElement\DateFormat::getFormElement(): Chris@0: * @code Chris@0: * '#ajax' => array( Chris@0: * 'callback' => 'Drupal\config_translation\FormElement\DateFormat::ajaxSample', Chris@0: * 'event' => 'keyup', Chris@0: * 'progress' => array( Chris@0: * 'type' => 'throbber', Chris@0: * 'message' => NULL, Chris@0: * ), Chris@0: * ), Chris@0: * @endcode Chris@0: * Chris@0: * As you can see from this example, the #ajax property for a form element is Chris@0: * an array. Here are the details of its elements, all of which are optional: Chris@0: * - callback: The callback to invoke to handle the server side of the Chris@0: * Ajax event. More information on callbacks is below in @ref sub_callback. Chris@0: * - wrapper: The HTML 'id' attribute of the area where the content returned by Chris@0: * the callback should be placed. Note that callbacks have a choice of Chris@0: * returning content or JavaScript commands; 'wrapper' is used for content Chris@0: * returns. Chris@0: * - method: The jQuery method for placing the new content (used with Chris@0: * 'wrapper'). Valid options are 'replaceWith' (default), 'append', 'prepend', Chris@0: * 'before', 'after', or 'html'. See Chris@0: * http://api.jquery.com/category/manipulation/ for more information on these Chris@0: * methods. Chris@0: * - effect: The jQuery effect to use when placing the new HTML (used with Chris@0: * 'wrapper'). Valid options are 'none' (default), 'slide', or 'fade'. Chris@0: * - speed: The effect speed to use (used with 'effect' and 'wrapper'). Valid Chris@0: * options are 'slow' (default), 'fast', or the number of milliseconds the Chris@0: * effect should run. Chris@0: * - event: The JavaScript event to respond to. This is selected automatically Chris@0: * for the type of form element; provide a value to override the default. Chris@0: * - prevent: A JavaScript event to prevent when the event is triggered. For Chris@0: * example, if you use event 'mousedown' on a button, you might want to Chris@0: * prevent 'click' events from also being triggered. Chris@0: * - progress: An array indicating how to show Ajax processing progress. Can Chris@0: * contain one or more of these elements: Chris@0: * - type: Type of indicator: 'throbber' (default) or 'bar'. Chris@0: * - message: Translated message to display. Chris@0: * - url: For a bar progress indicator, URL path for determining progress. Chris@0: * - interval: For a bar progress indicator, how often to update it. Chris@0: * - url: A \Drupal\Core\Url to which to submit the Ajax request. If omitted, Chris@0: * defaults to either the same URL as the form or link destination is for Chris@0: * someone with JavaScript disabled, or a slightly modified version (e.g., Chris@0: * with a query parameter added, removed, or changed) of that URL if Chris@0: * necessary to support Drupal's content negotiation. It is recommended to Chris@0: * omit this key and use Drupal's content negotiation rather than using Chris@0: * substantially different URLs between Ajax and non-Ajax. Chris@0: * Chris@0: * @subsection sub_callback Setting up a callback to process Ajax Chris@0: * Once you have set up your form to trigger an Ajax response (see @ref sub_form Chris@0: * above), you need to write some PHP code to process the response. If you use Chris@0: * 'path' in your Ajax set-up, your route controller will be triggered with only Chris@0: * the information you provide in the URL. If you use 'callback', your callback Chris@0: * method is a function, which will receive the $form and $form_state from the Chris@0: * triggering form. You can use $form_state to get information about the Chris@0: * data the user has entered into the form. For instance, in the above example Chris@0: * for the date format preview, Chris@0: * \Drupal\config_translation\FormElement\DateFormat\ajaxSample() does this to Chris@0: * get the format string entered by the user: Chris@0: * @code Chris@0: * $format_value = \Drupal\Component\Utility\NestedArray::getValue( Chris@0: * $form_state->getValues(), Chris@0: * $form_state->getTriggeringElement()['#array_parents']); Chris@0: * @endcode Chris@0: * Chris@0: * Once you have processed the input, you have your choice of returning HTML Chris@0: * markup or a set of Ajax commands. If you choose to return HTML markup, you Chris@0: * can return it as a string or a renderable array, and it will be placed in Chris@0: * the defined 'wrapper' element (see documentation above in @ref sub_form). Chris@17: * In addition, any messages returned by Chris@17: * \Drupal\Core\Messenger\Messenger::all(), themed as in Chris@0: * status-messages.html.twig, will be prepended. Chris@0: * Chris@0: * To return commands, you need to set up an object of class Chris@0: * \Drupal\Core\Ajax\AjaxResponse, and then use its addCommand() method to add Chris@0: * individual commands to it. In the date format preview example, the format Chris@0: * output is calculated, and then it is returned as replacement markup for a div Chris@0: * like this: Chris@0: * @code Chris@0: * $response = new AjaxResponse(); Chris@0: * $response->addCommand(new ReplaceCommand( Chris@0: * '#edit-date-format-suffix', Chris@0: * '' . $format . '')); Chris@0: * return $response; Chris@0: * @endcode Chris@0: * Chris@0: * The individual commands that you can return implement interface Chris@0: * \Drupal\Core\Ajax\CommandInterface. Available commands provide the ability Chris@0: * to pop up alerts, manipulate text and markup in various ways, redirect Chris@0: * to a new URL, and the generic \Drupal\Core\Ajax\InvokeCommand, which Chris@0: * invokes an arbitrary jQuery command. Chris@0: * Chris@0: * As noted above, status messages are prepended automatically if you use the Chris@0: * 'wrapper' method and return HTML markup. This is not the case if you return Chris@0: * commands, but if you would like to show status messages, you can add Chris@0: * @code Chris@0: * array('#type' => 'status_messages') Chris@0: * @endcode Chris@0: * to a render array, use drupal_render() to render it, and add a command to Chris@0: * place the messages in an appropriate location. Chris@0: * Chris@0: * @section sec_other Other methods for triggering Ajax Chris@0: * Here are some additional methods you can use to trigger Ajax responses in Chris@0: * Drupal: Chris@0: * - Add class 'use-ajax' to a link. The link will be loaded using an Ajax Chris@0: * call. When using this method, the href of the link can contain '/nojs/' as Chris@0: * part of the path. When the Ajax JavaScript processes the page, it will Chris@0: * convert this to '/ajax/'. The server is then able to easily tell if this Chris@0: * request was made through an actual Ajax request or in a degraded state, and Chris@0: * respond appropriately. Chris@0: * - Add class 'use-ajax-submit' to a submit button in a form. The form will Chris@0: * then be submitted via Ajax to the path specified in the #action. Like the Chris@0: * ajax-submit class on links, this path will have '/nojs/' replaced with Chris@0: * '/ajax/' so that the submit handler can tell if the form was submitted in a Chris@0: * degraded state or not. Chris@0: * - Add property '#autocomplete_route_name' to a text field in a form. The Chris@0: * route controller for this route must return an array of options for Chris@0: * autocomplete, as a \Symfony\Component\HttpFoundation\JsonResponse object. Chris@0: * See the @link menu Routing topic @endlink for more information about Chris@0: * routing. Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @} End of "defgroup ajax". Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup service_tag Service Tags Chris@0: * @{ Chris@0: * Service tags overview Chris@0: * Chris@0: * Some services have tags, which are defined in the service definition. Tags Chris@0: * are used to define a group of related services, or to specify some aspect of Chris@0: * how the service behaves. Typically, if you tag a service, your service class Chris@0: * must also implement a corresponding interface. Some common examples: Chris@0: * - access_check: Indicates a route access checking service; see the Chris@0: * @link menu Menu and routing system topic @endlink for more information. Chris@0: * - cache.bin: Indicates a cache bin service; see the Chris@0: * @link cache Cache topic @endlink for more information. Chris@0: * - event_subscriber: Indicates an event subscriber service. Event subscribers Chris@0: * can be used for dynamic routing and route altering; see the Chris@0: * @link menu Menu and routing system topic @endlink for more information. Chris@0: * They can also be used for other purposes; see Chris@0: * http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html Chris@0: * for more information. Chris@0: * - needs_destruction: Indicates that a destruct() method needs to be called Chris@0: * at the end of a request to finalize operations, if this service was Chris@0: * instantiated. Services should implement \Drupal\Core\DestructableInterface Chris@0: * in this case. Chris@0: * - context_provider: Indicates a block context provider, used for example Chris@0: * by block conditions. It has to implement Chris@0: * \Drupal\Core\Plugin\Context\ContextProviderInterface. Chris@0: * - http_client_middleware: Indicates that the service provides a guzzle Chris@0: * middleware, see Chris@0: * https://guzzle.readthedocs.org/en/latest/handlers-and-middleware.html for Chris@0: * more information. Chris@0: * Chris@0: * Creating a tag for a service does not do anything on its own, but tags Chris@0: * can be discovered or queried in a compiler pass when the container is built, Chris@0: * and a corresponding action can be taken. See Chris@0: * \Drupal\Core\Render\MainContent\MainContentRenderersPass for an example of Chris@0: * finding tagged services. Chris@0: * Chris@0: * See @link container Services and Dependency Injection Container @endlink for Chris@0: * information on services and the dependency injection container. Chris@0: * Chris@0: * @} Chris@0: */ Chris@0: Chris@0: /** Chris@0: * @defgroup events Events Chris@0: * @{ Chris@0: * Overview of event dispatch and subscribing Chris@0: * Chris@0: * @section sec_intro Introduction and terminology Chris@0: * Events are part of the Symfony framework: they allow for different components Chris@0: * of the system to interact and communicate with each other. Each event has a Chris@0: * unique string name. One system component dispatches the event at an Chris@0: * appropriate time; many events are dispatched by Drupal core and the Symfony Chris@0: * framework in every request. Other system components can register as event Chris@0: * subscribers; when an event is dispatched, a method is called on each Chris@0: * registered subscriber, allowing each one to react. For more on the general Chris@0: * concept of events, see Chris@0: * http://symfony.com/doc/current/components/event_dispatcher/introduction.html Chris@0: * Chris@0: * @section sec_dispatch Dispatching events Chris@0: * To dispatch an event, call the Chris@0: * \Symfony\Component\EventDispatcher\EventDispatcherInterface::dispatch() Chris@0: * method on the 'event_dispatcher' service (see the Chris@0: * @link container Services topic @endlink for more information about how to Chris@0: * interact with services). The first argument is the unique event name, which Chris@0: * you should normally define as a constant in a separate static class (see Chris@0: * \Symfony\Component\HttpKernel\KernelEvents and Chris@0: * \Drupal\Core\Config\ConfigEvents for examples). The second argument is a Chris@0: * \Symfony\Component\EventDispatcher\Event object; normally you will need to Chris@0: * extend this class, so that your event class can provide data to the event Chris@0: * subscribers. Chris@0: * Chris@0: * @section sec_subscribe Registering event subscribers Chris@0: * Here are the steps to register an event subscriber: Chris@0: * - Define a service in your module, tagged with 'event_subscriber' (see the Chris@0: * @link container Services topic @endlink for instructions). Chris@0: * - Define a class for your subscriber service that implements Chris@0: * \Symfony\Component\EventDispatcher\EventSubscriberInterface Chris@0: * - In your class, the getSubscribedEvents method returns a list of the events Chris@0: * this class is subscribed to, and which methods on the class should be Chris@0: * called for each one. Example: Chris@0: * @code Chris@0: * public static function getSubscribedEvents() { Chris@0: * // Subscribe to kernel terminate with priority 100. Chris@0: * $events[KernelEvents::TERMINATE][] = array('onTerminate', 100); Chris@0: * // Subscribe to kernel request with default priority of 0. Chris@0: * $events[KernelEvents::REQUEST][] = array('onRequest'); Chris@0: * return $events; Chris@0: * } Chris@0: * @endcode Chris@0: * - Write the methods that respond to the events; each one receives the Chris@0: * event object provided in the dispatch as its one argument. In the above Chris@0: * example, you would need to write onTerminate() and onRequest() methods. Chris@0: * Chris@0: * Note that in your getSubscribedEvents() method, you can optionally set the Chris@0: * priority of your event subscriber (see terminate example above). Event Chris@0: * subscribers with higher priority numbers get executed first; the default Chris@0: * priority is zero. If two event subscribers for the same event have the same Chris@0: * priority, the one defined in a module with a lower module weight will fire Chris@0: * first. Subscribers defined in the same services file are fired in Chris@0: * definition order. If order matters defining a priority is strongly advised Chris@0: * instead of relying on these two tie breaker rules as they might change in a Chris@0: * minor release. Chris@0: * @} Chris@0: */ Chris@16: Chris@16: /** Chris@16: * @defgroup session Sessions Chris@16: * @{ Chris@16: * Store and retrieve data associated with a user's browsing session. Chris@16: * Chris@16: * @section sec_intro Overview Chris@16: * The Drupal session management subsystem is built on top of the Symfony Chris@16: * session component. It is optimized in order to minimize the impact of Chris@16: * anonymous sessions on caching proxies. A session is only started if necessary Chris@16: * and the session cookie is removed from the browser as soon as the session Chris@16: * has no data. For this reason it is important for contributed and custom Chris@16: * code to remove session data if it is not used anymore. Chris@16: * Chris@16: * @section sec_usage Usage Chris@16: * Session data is accessed via the Chris@16: * \Symfony\Component\HttpFoundation\Request::getSession() Chris@16: * method, which returns an instance of Chris@16: * \Symfony\Component\HttpFoundation\Session\SessionInterface. The most Chris@16: * important methods on SessionInterface are set(), get(), and remove(). Chris@16: * Chris@16: * The following code fragment shows the implementation of a counter controller Chris@16: * relying on the session: Chris@16: * @code Chris@16: * public function counter(Request $request) { Chris@16: * $session = $request->getSession(); Chris@16: * $count = $session->get('mymodule.counter', 0) + 1; Chris@16: * $session->set('mymodule.counter', $count); Chris@16: * Chris@16: * return [ Chris@16: * '#markup' => $this->t('Page Views: @count', ['@count' => $count]), Chris@16: * '#cache' => [ Chris@16: * 'max-age' => 0, Chris@16: * ], Chris@16: * ]; Chris@16: * } Chris@16: * Chris@16: * public function reset(Request $request) { Chris@16: * $session = $request->getSession(); Chris@16: * $session->remove('mymodule.counter'); Chris@16: * } Chris@16: * @endcode Chris@16: * Chris@16: * It is important to keep the amount of data stored inside the session to a Chris@16: * minimum, as the complete session is loaded on every request. Also third Chris@16: * party session storage backends do not necessarily allow objects of unlimited Chris@16: * size. If it is necessary to collect a non-trivial amount of data specific to Chris@16: * a user's session, use the Key/Value store to save the serialized data and Chris@16: * only store the key to the entry in the session. Chris@16: * Chris@16: * @section sec_reserved Reserved attributes and namespacing Chris@16: * Contributed modules relying on the session are encouraged to namespace Chris@16: * session attributes by prefixing them with their project name or an Chris@16: * abbreviation thereof. Chris@16: * Chris@16: * Some attributes are reserved for Drupal core and must not be accessed from Chris@16: * within contributed and custom code. Reserved attributes include: Chris@16: * - uid: The user ID for an authenticated user. The value of this attribute Chris@16: * cannot be modified. Chris@16: * @} Chris@16: */