Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /**
|
Chris@0
|
4 * @file
|
Chris@0
|
5 * Documentation landing page and topics, plus core library hooks.
|
Chris@0
|
6 */
|
Chris@0
|
7
|
Chris@0
|
8 /**
|
Chris@0
|
9 * @mainpage
|
Chris@0
|
10 * Welcome to the Drupal API Documentation!
|
Chris@0
|
11 *
|
Chris@0
|
12 * This site is an API reference for Drupal, generated from comments embedded
|
Chris@0
|
13 * in the source code. More in-depth documentation can be found at
|
Chris@0
|
14 * https://www.drupal.org/developing/api.
|
Chris@0
|
15 *
|
Chris@0
|
16 * Here are some topics to help you get started developing with Drupal.
|
Chris@0
|
17 *
|
Chris@0
|
18 * @section essentials Essential background concepts
|
Chris@0
|
19 *
|
Chris@0
|
20 * - @link oo_conventions Object-oriented conventions used in Drupal @endlink
|
Chris@0
|
21 * - @link extending Extending and altering Drupal @endlink
|
Chris@0
|
22 * - @link best_practices Security and best practices @endlink
|
Chris@0
|
23 * - @link info_types Types of information in Drupal @endlink
|
Chris@0
|
24 *
|
Chris@0
|
25 * @section interface User interface
|
Chris@0
|
26 *
|
Chris@0
|
27 * - @link menu Menu entries, local tasks, and other links @endlink
|
Chris@0
|
28 * - @link routing Routing API and page controllers @endlink
|
Chris@0
|
29 * - @link form_api Forms @endlink
|
Chris@0
|
30 * - @link block_api Blocks @endlink
|
Chris@0
|
31 * - @link ajax Ajax @endlink
|
Chris@0
|
32 *
|
Chris@0
|
33 * @section store_retrieve Storing and retrieving data
|
Chris@0
|
34 *
|
Chris@0
|
35 * - @link entity_api Entities @endlink
|
Chris@0
|
36 * - @link field Fields @endlink
|
Chris@0
|
37 * - @link config_api Configuration API @endlink
|
Chris@0
|
38 * - @link state_api State API @endlink
|
Chris@0
|
39 * - @link views_overview Views @endlink
|
Chris@0
|
40 * - @link database Database abstraction layer @endlink
|
Chris@0
|
41 *
|
Chris@0
|
42 * @section other_essentials Other essential APIs
|
Chris@0
|
43 *
|
Chris@0
|
44 * - @link plugin_api Plugins @endlink
|
Chris@0
|
45 * - @link container Services and the Dependency Injection Container @endlink
|
Chris@0
|
46 * - @link events Events @endlink
|
Chris@0
|
47 * - @link i18n Internationalization @endlink
|
Chris@0
|
48 * - @link cache Caching @endlink
|
Chris@0
|
49 * - @link utility Utility classes and functions @endlink
|
Chris@0
|
50 * - @link user_api User accounts, permissions, and roles @endlink
|
Chris@0
|
51 * - @link theme_render Render API @endlink
|
Chris@0
|
52 * - @link themeable Theme system @endlink
|
Chris@0
|
53 * - @link update_api Update API @endlink
|
Chris@0
|
54 * - @link migration Migration @endlink
|
Chris@0
|
55 *
|
Chris@0
|
56 * @section additional Additional topics
|
Chris@0
|
57 *
|
Chris@0
|
58 * - @link batch Batch API @endlink
|
Chris@0
|
59 * - @link queue Queue API @endlink
|
Chris@0
|
60 * - @link typed_data Typed Data @endlink
|
Chris@0
|
61 * - @link testing Automated tests @endlink
|
Chris@0
|
62 * - @link php_assert PHP Runtime Assert Statements @endlink
|
Chris@0
|
63 * - @link third_party Integrating third-party applications @endlink
|
Chris@0
|
64 *
|
Chris@0
|
65 * @section more_info Further information
|
Chris@0
|
66 *
|
Chris@0
|
67 * - @link https://api.drupal.org/api/drupal/groups/8 All topics @endlink
|
Chris@0
|
68 * - @link https://www.drupal.org/project/examples Examples project (sample modules) @endlink
|
Chris@0
|
69 * - @link https://www.drupal.org/list-changes API change notices @endlink
|
Chris@0
|
70 * - @link https://www.drupal.org/developing/api/8 Drupal 8 API longer references @endlink
|
Chris@0
|
71 */
|
Chris@0
|
72
|
Chris@0
|
73 /**
|
Chris@0
|
74 * @defgroup third_party REST and Application Integration
|
Chris@0
|
75 * @{
|
Chris@0
|
76 * Integrating third-party applications using REST and related operations.
|
Chris@0
|
77 *
|
Chris@0
|
78 * @section sec_overview Overview of web services
|
Chris@0
|
79 * Web services make it possible for applications and web sites to read and
|
Chris@0
|
80 * update information from other web sites. There are several standard
|
Chris@0
|
81 * techniques for providing web services, including:
|
Chris@0
|
82 * - SOAP: http://wikipedia.org/wiki/SOAP
|
Chris@0
|
83 * - XML-RPC: http://wikipedia.org/wiki/XML-RPC
|
Chris@0
|
84 * - REST: http://wikipedia.org/wiki/Representational_state_transfer
|
Chris@0
|
85 * Drupal sites can both provide web services and integrate third-party web
|
Chris@0
|
86 * services.
|
Chris@0
|
87 *
|
Chris@0
|
88 * @section sec_rest_overview Overview of REST
|
Chris@0
|
89 * The REST technique uses basic HTTP requests to obtain and update data, where
|
Chris@0
|
90 * each web service defines a specific API (HTTP GET and/or POST parameters and
|
Chris@0
|
91 * returned response) for its HTTP requests. REST requests are separated into
|
Chris@0
|
92 * several types, known as methods, including:
|
Chris@0
|
93 * - GET: Requests to obtain data.
|
Chris@0
|
94 * - POST: Requests to update or create data.
|
Chris@0
|
95 * - PUT: Requests to update or create data (limited support, currently unused
|
Chris@0
|
96 * by entity resources).
|
Chris@0
|
97 * - PATCH: Requests to update a subset of data, such as one field.
|
Chris@0
|
98 * - DELETE: Requests to delete data.
|
Chris@0
|
99 * The Drupal Core REST module provides support for GET, POST, PATCH, and DELETE
|
Chris@0
|
100 * quests on entities, GET requests on the database log from the Database
|
Chris@0
|
101 * Logging module, and a plugin framework for providing REST support for other
|
Chris@0
|
102 * data and other methods.
|
Chris@0
|
103 *
|
Chris@0
|
104 * REST requests can be authenticated. The Drupal Core Basic Auth module
|
Chris@0
|
105 * provides authentication using the HTTP Basic protocol; the contributed module
|
Chris@0
|
106 * OAuth (https://www.drupal.org/project/oauth) implements the OAuth
|
Chris@0
|
107 * authentication protocol. You can also use cookie-based authentication, which
|
Chris@0
|
108 * would require users to be logged into the Drupal site while using the
|
Chris@0
|
109 * application on the third-party site that is using the REST service.
|
Chris@0
|
110 *
|
Chris@0
|
111 * @section sec_rest Enabling REST for entities and the log
|
Chris@0
|
112 * Here are the steps to take to use the REST operations provided by Drupal
|
Chris@0
|
113 * Core:
|
Chris@0
|
114 * - Enable the REST module, plus Basic Auth (or another authentication method)
|
Chris@0
|
115 * and HAL.
|
Chris@0
|
116 * - Node entity support is configured by default. If you would like to support
|
Chris@0
|
117 * other types of entities, you can copy
|
Chris@0
|
118 * core/modules/rest/config/install/rest.settings.yml to your sync
|
Chris@0
|
119 * configuration directory, appropriately modified for other entity types,
|
Chris@0
|
120 * and import it. Support for GET on the log from the Database Logging module
|
Chris@0
|
121 * can also be enabled in this way; in this case, the 'entity:node' line
|
Chris@0
|
122 * in the configuration would be replaced by the appropriate plugin ID,
|
Chris@0
|
123 * 'dblog'.
|
Chris@0
|
124 * - Set up permissions to allow the desired REST operations for a role, and set
|
Chris@0
|
125 * up one or more user accounts to perform the operations.
|
Chris@0
|
126 * - To perform a REST operation, send a request to either the canonical URL
|
Chris@0
|
127 * for an entity (such as node/12345 for a node), or if the entity does not
|
Chris@0
|
128 * have a canonical URL, a URL like entity/(type)/(ID). The URL for a log
|
Chris@0
|
129 * entry is dblog/(ID). The request must have the following properties:
|
Chris@0
|
130 * - The request method must be set to the REST method you are using (POST,
|
Chris@0
|
131 * GET, PATCH, etc.).
|
Chris@0
|
132 * - The content type for the data you send, or the accept type for the
|
Chris@0
|
133 * data you are receiving, must be set to 'application/hal+json'.
|
Chris@0
|
134 * - If you are sending data, it must be JSON-encoded.
|
Chris@0
|
135 * - You'll also need to make sure the authentication information is sent
|
Chris@0
|
136 * with the request, unless you have allowed access to anonymous users.
|
Chris@0
|
137 *
|
Chris@0
|
138 * For more detailed information on setting up REST, see
|
Chris@0
|
139 * https://www.drupal.org/documentation/modules/rest.
|
Chris@0
|
140 *
|
Chris@0
|
141 * @section sec_plugins Defining new REST plugins
|
Chris@0
|
142 * The REST framework in the REST module has support built in for entities, but
|
Chris@0
|
143 * it is also an extensible plugin-based system. REST plugins implement
|
Chris@0
|
144 * interface \Drupal\rest\Plugin\ResourceInterface, and generally extend base
|
Chris@0
|
145 * class \Drupal\rest\Plugin\ResourceBase. They are annotated with
|
Chris@0
|
146 * \Drupal\rest\Annotation\RestResource annotation, and must be in plugin
|
Chris@0
|
147 * namespace subdirectory Plugin\rest\resource. For more information on how to
|
Chris@0
|
148 * create plugins, see the @link plugin_api Plugin API topic. @endlink
|
Chris@0
|
149 *
|
Chris@0
|
150 * If you create a new REST plugin, you will also need to enable it by
|
Chris@0
|
151 * providing default configuration or configuration import, as outlined in
|
Chris@0
|
152 * @ref sec_rest above.
|
Chris@0
|
153 *
|
Chris@0
|
154 * @section sec_integrate Integrating data from other sites into Drupal
|
Chris@0
|
155 * If you want to integrate data from other web sites into Drupal, here are
|
Chris@0
|
156 * some notes:
|
Chris@0
|
157 * - There are contributed modules available for integrating many third-party
|
Chris@0
|
158 * sites into Drupal. Search on https://www.drupal.org/project/project_module
|
Chris@0
|
159 * - If there is not an existing module, you will need to find documentation on
|
Chris@0
|
160 * the specific web services API for the site you are trying to integrate.
|
Chris@0
|
161 * - There are several classes and functions that are useful for interacting
|
Chris@0
|
162 * with web services:
|
Chris@0
|
163 * - You should make requests using the 'http_client' service, which
|
Chris@0
|
164 * implements \GuzzleHttp\ClientInterface. See the
|
Chris@0
|
165 * @link container Services topic @endlink for more information on
|
Chris@0
|
166 * services. If you cannot use dependency injection to retrieve this
|
Chris@0
|
167 * service, the \Drupal::httpClient() method is available. A good example
|
Chris@0
|
168 * of how to use this service can be found in
|
Chris@0
|
169 * \Drupal\aggregator\Plugin\aggregator\fetcher\DefaultFetcher
|
Chris@0
|
170 * - \Drupal\Component\Serialization\Json (JSON encoding and decoding).
|
Chris@0
|
171 * - PHP has functions and classes for parsing XML; see
|
Chris@0
|
172 * http://php.net/manual/refs.xml.php
|
Chris@0
|
173 * @}
|
Chris@0
|
174 */
|
Chris@0
|
175
|
Chris@0
|
176 /**
|
Chris@0
|
177 * @defgroup state_api State API
|
Chris@0
|
178 * @{
|
Chris@0
|
179 * Information about the State API.
|
Chris@0
|
180 *
|
Chris@0
|
181 * The State API is one of several methods in Drupal for storing information.
|
Chris@0
|
182 * See the @link info_types Information types topic @endlink for an
|
Chris@0
|
183 * overview of the different types of information.
|
Chris@0
|
184 *
|
Chris@0
|
185 * The basic entry point into the State API is \Drupal::state(), which returns
|
Chris@0
|
186 * an object of class \Drupal\Core\State\StateInterface. This class has
|
Chris@0
|
187 * methods for storing and retrieving state information; each piece of state
|
Chris@0
|
188 * information is associated with a string-valued key. Example:
|
Chris@0
|
189 * @code
|
Chris@0
|
190 * // Get the state class.
|
Chris@0
|
191 * $state = \Drupal::state();
|
Chris@0
|
192 * // Find out when cron was last run; the key is 'system.cron_last'.
|
Chris@0
|
193 * $time = $state->get('system.cron_last');
|
Chris@0
|
194 * // Set the cron run time to the current request time.
|
Chris@0
|
195 * $state->set('system.cron_last', REQUEST_TIME);
|
Chris@0
|
196 * @endcode
|
Chris@0
|
197 *
|
Chris@0
|
198 * For more on the State API, see https://www.drupal.org/developing/api/8/state
|
Chris@0
|
199 * @}
|
Chris@0
|
200 */
|
Chris@0
|
201
|
Chris@0
|
202 /**
|
Chris@0
|
203 * @defgroup config_api Configuration API
|
Chris@0
|
204 * @{
|
Chris@0
|
205 * Information about the Configuration API.
|
Chris@0
|
206 *
|
Chris@0
|
207 * The Configuration API is one of several methods in Drupal for storing
|
Chris@0
|
208 * information. See the @link info_types Information types topic @endlink for
|
Chris@0
|
209 * an overview of the different types of information. The sections below have
|
Chris@0
|
210 * more information about the configuration API; see
|
Chris@0
|
211 * https://www.drupal.org/developing/api/8/configuration for more details.
|
Chris@0
|
212 *
|
Chris@0
|
213 * @section sec_storage Configuration storage
|
Chris@0
|
214 * In Drupal, there is a concept of the "active" configuration, which is the
|
Chris@0
|
215 * configuration that is currently in use for a site. The storage used for the
|
Chris@0
|
216 * active configuration is configurable: it could be in the database, in files
|
Chris@0
|
217 * in a particular directory, or in other storage backends; the default storage
|
Chris@0
|
218 * is in the database. Module developers must use the configuration API to
|
Chris@0
|
219 * access the active configuration, rather than being concerned about the
|
Chris@0
|
220 * details of where and how it is stored.
|
Chris@0
|
221 *
|
Chris@0
|
222 * Configuration is divided into individual objects, each of which has a
|
Chris@0
|
223 * unique name or key. Some modules will have only one configuration object,
|
Chris@0
|
224 * typically called 'mymodule.settings'; some modules will have many. Within
|
Chris@0
|
225 * a configuration object, configuration settings have data types (integer,
|
Chris@0
|
226 * string, Boolean, etc.) and settings can also exist in a nested hierarchy,
|
Chris@0
|
227 * known as a "mapping".
|
Chris@0
|
228 *
|
Chris@0
|
229 * Configuration can also be overridden on a global, per-language, or
|
Chris@0
|
230 * per-module basis. See https://www.drupal.org/node/1928898 for more
|
Chris@0
|
231 * information.
|
Chris@0
|
232 *
|
Chris@0
|
233 * @section sec_yaml Configuration YAML files
|
Chris@0
|
234 * Whether or not configuration files are being used for the active
|
Chris@0
|
235 * configuration storage on a particular site, configuration files are always
|
Chris@0
|
236 * used for:
|
Chris@0
|
237 * - Defining the default configuration for an extension (module, theme, or
|
Chris@0
|
238 * profile), which is imported to the active storage when the extension is
|
Chris@0
|
239 * enabled. These configuration items are located in the config/install
|
Chris@0
|
240 * sub-directory of the extension. Note that changes to this configuration
|
Chris@0
|
241 * after a module or theme is already enabled have no effect; to make a
|
Chris@0
|
242 * configuration change after a module or theme is enabled, you would need to
|
Chris@0
|
243 * uninstall/reinstall or use a hook_update_N() function.
|
Chris@0
|
244 * - Defining optional configuration for a module or theme. Optional
|
Chris@0
|
245 * configuration items are located in the config/optional sub-directory of the
|
Chris@0
|
246 * extension. These configuration items have dependencies that are not
|
Chris@0
|
247 * explicit dependencies of the extension, so they are only installed if all
|
Chris@0
|
248 * dependencies are met. For example, in the scenario that module A defines a
|
Chris@0
|
249 * dependency which requires module B, but module A is installed first and
|
Chris@0
|
250 * module B some time later, then module A's config/optional directory will be
|
Chris@0
|
251 * scanned at that time for newly met dependencies, and the configuration will
|
Chris@0
|
252 * be installed then. If module B is never installed, the configuration item
|
Chris@0
|
253 * will not be installed either.
|
Chris@0
|
254 * - Exporting and importing configuration.
|
Chris@0
|
255 *
|
Chris@0
|
256 * The file storage format for configuration information in Drupal is
|
Chris@0
|
257 * @link http://wikipedia.org/wiki/YAML YAML files. @endlink Configuration is
|
Chris@0
|
258 * divided into files, each containing one configuration object. The file name
|
Chris@0
|
259 * for a configuration object is equal to the unique name of the configuration,
|
Chris@0
|
260 * with a '.yml' extension. The default configuration files for each module are
|
Chris@0
|
261 * placed in the config/install directory under the top-level module directory,
|
Chris@0
|
262 * so look there in most Core modules for examples.
|
Chris@0
|
263 *
|
Chris@0
|
264 * @section sec_schema Configuration schema and translation
|
Chris@0
|
265 * Each configuration file has a specific structure, which is expressed as a
|
Chris@0
|
266 * YAML-based configuration schema. The configuration schema details the
|
Chris@0
|
267 * structure of the configuration, its data types, and which of its values need
|
Chris@0
|
268 * to be translatable. Each module needs to define its configuration schema in
|
Chris@0
|
269 * files in the config/schema directory under the top-level module directory, so
|
Chris@0
|
270 * look there in most Core modules for examples.
|
Chris@0
|
271 *
|
Chris@0
|
272 * Configuration can be internationalized; see the
|
Chris@0
|
273 * @link i18n Internationalization topic @endlink for more information. Data
|
Chris@0
|
274 * types label, text, and date_format in configuration schema are translatable;
|
Chris@0
|
275 * string is non-translatable text (the 'translatable' property on a schema
|
Chris@0
|
276 * data type definition indicates that it is translatable).
|
Chris@0
|
277 *
|
Chris@0
|
278 * @section sec_simple Simple configuration
|
Chris@0
|
279 * The simple configuration API should be used for information that will always
|
Chris@0
|
280 * have exactly one copy or version. For instance, if your module has a
|
Chris@0
|
281 * setting that is either on or off, then this is only defined once, and it
|
Chris@0
|
282 * would be a Boolean-valued simple configuration setting.
|
Chris@0
|
283 *
|
Chris@0
|
284 * The first task in using the simple configuration API is to define the
|
Chris@0
|
285 * configuration file structure, file name, and schema of your settings (see
|
Chris@0
|
286 * @ref sec_yaml above). Once you have done that, you can retrieve the active
|
Chris@0
|
287 * configuration object that corresponds to configuration file mymodule.foo.yml
|
Chris@0
|
288 * with a call to:
|
Chris@0
|
289 * @code
|
Chris@0
|
290 * $config = \Drupal::config('mymodule.foo');
|
Chris@0
|
291 * @endcode
|
Chris@0
|
292 *
|
Chris@0
|
293 * This will be an object of class \Drupal\Core\Config\Config, which has methods
|
Chris@0
|
294 * for getting configuration information. For instance, if your YAML file
|
Chris@0
|
295 * structure looks like this:
|
Chris@0
|
296 * @code
|
Chris@0
|
297 * enabled: '0'
|
Chris@0
|
298 * bar:
|
Chris@0
|
299 * baz: 'string1'
|
Chris@0
|
300 * boo: 34
|
Chris@0
|
301 * @endcode
|
Chris@0
|
302 * you can make calls such as:
|
Chris@0
|
303 * @code
|
Chris@0
|
304 * // Get a single value.
|
Chris@0
|
305 * $enabled = $config->get('enabled');
|
Chris@0
|
306 * // Get an associative array.
|
Chris@0
|
307 * $bar = $config->get('bar');
|
Chris@0
|
308 * // Get one element of the array.
|
Chris@0
|
309 * $bar_baz = $config->get('bar.baz');
|
Chris@0
|
310 * @endcode
|
Chris@0
|
311 *
|
Chris@0
|
312 * The Config object that was obtained and used in the previous examples does
|
Chris@0
|
313 * not allow you to change configuration. If you want to change configuration,
|
Chris@0
|
314 * you will instead need to get the Config object by making a call to
|
Chris@0
|
315 * getEditable() on the config factory:
|
Chris@0
|
316 * @code
|
Chris@0
|
317 * $config =\Drupal::service('config.factory')->getEditable('mymodule.foo');
|
Chris@0
|
318 * @endcode
|
Chris@0
|
319 *
|
Chris@0
|
320 * Individual configuration values can be changed or added using the set()
|
Chris@0
|
321 * method and saved using the save() method:
|
Chris@0
|
322 * @code
|
Chris@0
|
323 * // Set a scalar value.
|
Chris@0
|
324 * $config->set('enabled', 1);
|
Chris@0
|
325 * // Save the configuration.
|
Chris@0
|
326 * $config->save();
|
Chris@0
|
327 * @endcode
|
Chris@0
|
328 *
|
Chris@0
|
329 * Configuration values can also be unset using the clear() method, which is
|
Chris@0
|
330 * also chainable:
|
Chris@0
|
331 * @code
|
Chris@0
|
332 * $config->clear('bar.boo')->save();
|
Chris@0
|
333 * $config_data = $config->get('bar');
|
Chris@0
|
334 * @endcode
|
Chris@0
|
335 * In this example $config_data would return an array with one key - 'baz' -
|
Chris@0
|
336 * because 'boo' was unset.
|
Chris@0
|
337 *
|
Chris@0
|
338 * @section sec_entity Configuration entities
|
Chris@0
|
339 * In contrast to the simple configuration settings described in the previous
|
Chris@0
|
340 * section, if your module allows users to create zero or more items (where
|
Chris@0
|
341 * "items" are things like content type definitions, view definitions, and the
|
Chris@0
|
342 * like), then you need to define a configuration entity type to store your
|
Chris@0
|
343 * configuration. Creating an entity type, loading entities, and querying them
|
Chris@0
|
344 * are outlined in the @link entity_api Entity API topic. @endlink Here are a
|
Chris@0
|
345 * few additional steps and notes specific to configuration entities:
|
Chris@0
|
346 * - For examples, look for classes that implement
|
Chris@0
|
347 * \Drupal\Core\Config\Entity\ConfigEntityInterface -- one good example is
|
Chris@0
|
348 * the \Drupal\user\Entity\Role entity type.
|
Chris@0
|
349 * - In the entity type annotation, you will need to define a 'config_prefix'
|
Chris@0
|
350 * string. When Drupal stores a configuration item, it will be given a name
|
Chris@0
|
351 * composed of your module name, your chosen config prefix, and the ID of
|
Chris@0
|
352 * the individual item, separated by '.'. For example, in the Role entity,
|
Chris@0
|
353 * the config prefix is 'role', so one configuration item might be named
|
Chris@0
|
354 * user.role.anonymous, with configuration file user.role.anonymous.yml.
|
Chris@0
|
355 * - You will need to define the schema for your configuration in your
|
Chris@0
|
356 * modulename.schema.yml file, with an entry for 'modulename.config_prefix.*'.
|
Chris@0
|
357 * For example, for the Role entity, the file user.schema.yml has an entry
|
Chris@0
|
358 * user.role.*; see @ref sec_yaml above for more information.
|
Chris@0
|
359 * - Your module can provide default/optional configuration entities in YAML
|
Chris@0
|
360 * files; see @ref sec_yaml above for more information.
|
Chris@0
|
361 * - Some configuration entities have dependencies on other configuration
|
Chris@0
|
362 * entities, and module developers need to consider this so that configuration
|
Chris@0
|
363 * can be imported, uninstalled, and synchronized in the right order. For
|
Chris@0
|
364 * example, a field display configuration entity would need to depend on
|
Chris@0
|
365 * field configuration, which depends on field and bundle configuration.
|
Chris@0
|
366 * Configuration entity classes expose dependencies by overriding the
|
Chris@0
|
367 * \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies()
|
Chris@0
|
368 * method.
|
Chris@0
|
369 * - On routes for paths starting with '/admin' or otherwise designated as
|
Chris@0
|
370 * administration paths (such as node editing when it is set as an admin
|
Chris@0
|
371 * operation), if they have configuration entity placeholders, configuration
|
Chris@0
|
372 * entities are normally loaded in their original language, without
|
Chris@0
|
373 * translations or other overrides. This is usually desirable, because most
|
Chris@0
|
374 * admin paths are for editing configuration, and you need that to be in the
|
Chris@0
|
375 * source language and to lack possibly dynamic overrides. If for some reason
|
Chris@0
|
376 * you need to have your configuration entity loaded in the currently-selected
|
Chris@0
|
377 * language on an admin path (for instance, if you go to
|
Chris@0
|
378 * example.com/es/admin/your_path and you need the entity to be in Spanish),
|
Chris@0
|
379 * then you can add a 'with_config_overrides' parameter option to your route.
|
Chris@0
|
380 * The same applies if you need to load the entity with overrides (or
|
Chris@0
|
381 * translated) on an admin path like '/node/add/article' (when configured to
|
Chris@0
|
382 * be an admin path). Here's an example using the configurable_language config
|
Chris@0
|
383 * entity:
|
Chris@0
|
384 * @code
|
Chris@0
|
385 * mymodule.myroute:
|
Chris@0
|
386 * path: '/admin/mypath/{configurable_language}'
|
Chris@0
|
387 * defaults:
|
Chris@0
|
388 * _controller: '\Drupal\mymodule\MyController::myMethod'
|
Chris@0
|
389 * options:
|
Chris@0
|
390 * parameters:
|
Chris@0
|
391 * configurable_language:
|
Chris@0
|
392 * type: entity:configurable_language
|
Chris@0
|
393 * with_config_overrides: TRUE
|
Chris@0
|
394 * @endcode
|
Chris@0
|
395 * With the route defined this way, the $configurable_language parameter to
|
Chris@0
|
396 * your controller method will come in translated to the current language.
|
Chris@0
|
397 * Without the parameter options section, it would be in the original
|
Chris@0
|
398 * language, untranslated.
|
Chris@0
|
399 *
|
Chris@0
|
400 * @see i18n
|
Chris@0
|
401 *
|
Chris@0
|
402 * @}
|
Chris@0
|
403 */
|
Chris@0
|
404
|
Chris@0
|
405 /**
|
Chris@0
|
406 * @defgroup cache Cache API
|
Chris@0
|
407 * @{
|
Chris@0
|
408 * Information about the Drupal Cache API
|
Chris@0
|
409 *
|
Chris@0
|
410 * @section basics Basics
|
Chris@0
|
411 *
|
Chris@0
|
412 * Note: If not specified, all of the methods mentioned here belong to
|
Chris@0
|
413 * \Drupal\Core\Cache\CacheBackendInterface.
|
Chris@0
|
414 *
|
Chris@0
|
415 * The Cache API is used to store data that takes a long time to compute.
|
Chris@16
|
416 * Caching can either be permanent or valid only for a certain time span, and
|
Chris@0
|
417 * the cache can contain any type of data.
|
Chris@0
|
418 *
|
Chris@0
|
419 * To use the Cache API:
|
Chris@0
|
420 * - Request a cache object through \Drupal::cache() or by injecting a cache
|
Chris@0
|
421 * service.
|
Chris@0
|
422 * - Define a Cache ID (cid) value for your data. A cid is a string, which must
|
Chris@0
|
423 * contain enough information to uniquely identify the data. For example, if
|
Chris@0
|
424 * your data contains translated strings, then your cid value must include the
|
Chris@0
|
425 * interface text language selected for page.
|
Chris@0
|
426 * - Call the get() method to attempt a cache read, to see if the cache already
|
Chris@0
|
427 * contains your data.
|
Chris@0
|
428 * - If your data is not already in the cache, compute it and add it to the
|
Chris@0
|
429 * cache using the set() method. The third argument of set() can be used to
|
Chris@0
|
430 * control the lifetime of your cache item.
|
Chris@0
|
431 *
|
Chris@0
|
432 * Example:
|
Chris@0
|
433 * @code
|
Chris@0
|
434 * $cid = 'mymodule_example:' . \Drupal::languageManager()->getCurrentLanguage()->getId();
|
Chris@0
|
435 *
|
Chris@0
|
436 * $data = NULL;
|
Chris@0
|
437 * if ($cache = \Drupal::cache()->get($cid)) {
|
Chris@0
|
438 * $data = $cache->data;
|
Chris@0
|
439 * }
|
Chris@0
|
440 * else {
|
Chris@0
|
441 * $data = my_module_complicated_calculation();
|
Chris@0
|
442 * \Drupal::cache()->set($cid, $data);
|
Chris@0
|
443 * }
|
Chris@0
|
444 * @endcode
|
Chris@0
|
445 *
|
Chris@0
|
446 * Note the use of $data and $cache->data in the above example. Calls to
|
Chris@0
|
447 * \Drupal::cache()->get() return a record that contains the information stored
|
Chris@0
|
448 * by \Drupal::cache()->set() in the data property as well as additional meta
|
Chris@0
|
449 * information about the cached data. In order to make use of the cached data
|
Chris@0
|
450 * you can access it via $cache->data.
|
Chris@0
|
451 *
|
Chris@0
|
452 * @section bins Cache bins
|
Chris@0
|
453 *
|
Chris@0
|
454 * Cache storage is separated into "bins", each containing various cache items.
|
Chris@0
|
455 * Each bin can be configured separately; see @ref configuration.
|
Chris@0
|
456 *
|
Chris@0
|
457 * When you request a cache object, you can specify the bin name in your call to
|
Chris@0
|
458 * \Drupal::cache(). Alternatively, you can request a bin by getting service
|
Chris@0
|
459 * "cache.nameofbin" from the container. The default bin is called "default", with
|
Chris@0
|
460 * service name "cache.default", it is used to store common and frequently used
|
Chris@0
|
461 * caches.
|
Chris@0
|
462 *
|
Chris@0
|
463 * Other common cache bins are the following:
|
Chris@0
|
464 * - bootstrap: Data needed from the beginning to the end of most requests,
|
Chris@0
|
465 * that has a very strict limit on variations and is invalidated rarely.
|
Chris@0
|
466 * - render: Contains cached HTML strings like cached pages and blocks, can
|
Chris@0
|
467 * grow to large size.
|
Chris@0
|
468 * - data: Contains data that can vary by path or similar context.
|
Chris@0
|
469 * - discovery: Contains cached discovery data for things such as plugins,
|
Chris@0
|
470 * views_data, or YAML discovered data such as library info.
|
Chris@0
|
471 *
|
Chris@0
|
472 * A module can define a cache bin by defining a service in its
|
Chris@0
|
473 * modulename.services.yml file as follows (substituting the desired name for
|
Chris@0
|
474 * "nameofbin"):
|
Chris@0
|
475 * @code
|
Chris@0
|
476 * cache.nameofbin:
|
Chris@0
|
477 * class: Drupal\Core\Cache\CacheBackendInterface
|
Chris@0
|
478 * tags:
|
Chris@0
|
479 * - { name: cache.bin }
|
Chris@0
|
480 * factory: cache_factory:get
|
Chris@0
|
481 * arguments: [nameofbin]
|
Chris@0
|
482 * @endcode
|
Chris@0
|
483 * See the @link container Services topic @endlink for more on defining
|
Chris@0
|
484 * services.
|
Chris@0
|
485 *
|
Chris@0
|
486 * @section delete Deletion
|
Chris@0
|
487 *
|
Chris@0
|
488 * There are two ways to remove an item from the cache:
|
Chris@0
|
489 * - Deletion (using delete(), deleteMultiple() or deleteAll()) permanently
|
Chris@0
|
490 * removes the item from the cache.
|
Chris@0
|
491 * - Invalidation (using invalidate(), invalidateMultiple() or invalidateAll())
|
Chris@0
|
492 * is a "soft" delete that only marks items as "invalid", meaning "not fresh"
|
Chris@0
|
493 * or "not fresh enough". Invalid items are not usually returned from the
|
Chris@0
|
494 * cache, so in most ways they behave as if they have been deleted. However,
|
Chris@0
|
495 * it is possible to retrieve invalid items, if they have not yet been
|
Chris@0
|
496 * permanently removed by the garbage collector, by passing TRUE as the second
|
Chris@0
|
497 * argument for get($cid, $allow_invalid).
|
Chris@0
|
498 *
|
Chris@0
|
499 * Use deletion if a cache item is no longer useful; for instance, if the item
|
Chris@0
|
500 * contains references to data that has been deleted. Use invalidation if the
|
Chris@0
|
501 * cached item may still be useful to some callers until it has been updated
|
Chris@0
|
502 * with fresh data. The fact that it was fresh a short while ago may often be
|
Chris@0
|
503 * sufficient.
|
Chris@0
|
504 *
|
Chris@0
|
505 * Invalidation is particularly useful to protect against stampedes. Rather than
|
Chris@0
|
506 * having multiple concurrent requests updating the same cache item when it
|
Chris@0
|
507 * expires or is deleted, there can be one request updating the cache, while the
|
Chris@0
|
508 * other requests can proceed using the stale value. As soon as the cache item
|
Chris@0
|
509 * has been updated, all future requests will use the updated value.
|
Chris@0
|
510 *
|
Chris@0
|
511 * @section tags Cache Tags
|
Chris@0
|
512 *
|
Chris@0
|
513 * The fourth argument of the set() method can be used to specify cache tags,
|
Chris@0
|
514 * which are used to identify which data is included in each cache item. A cache
|
Chris@0
|
515 * item can have multiple cache tags (an array of cache tags), and each cache
|
Chris@0
|
516 * tag is a string. The convention is to generate cache tags of the form
|
Chris@0
|
517 * [prefix]:[suffix]. Usually, you'll want to associate the cache tags of
|
Chris@0
|
518 * entities, or entity listings. You won't have to manually construct cache tags
|
Chris@0
|
519 * for them — just get their cache tags via
|
Chris@0
|
520 * \Drupal\Core\Cache\CacheableDependencyInterface::getCacheTags() and
|
Chris@0
|
521 * \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags().
|
Chris@0
|
522 * Data that has been tagged can be invalidated as a group: no matter the Cache
|
Chris@0
|
523 * ID (cid) of the cache item, no matter in which cache bin a cache item lives;
|
Chris@0
|
524 * as long as it is tagged with a certain cache tag, it will be invalidated.
|
Chris@0
|
525 *
|
Chris@0
|
526 * Because of that, cache tags are a solution to the cache invalidation problem:
|
Chris@0
|
527 * - For caching to be effective, each cache item must only be invalidated when
|
Chris@0
|
528 * absolutely necessary. (i.e. maximizing the cache hit ratio.)
|
Chris@0
|
529 * - For caching to be correct, each cache item that depends on a certain thing
|
Chris@0
|
530 * must be invalidated whenever that certain thing is modified.
|
Chris@0
|
531 *
|
Chris@0
|
532 * A typical scenario: a user has modified a node that appears in two views,
|
Chris@0
|
533 * three blocks and on twelve pages. Without cache tags, we couldn't possibly
|
Chris@0
|
534 * know which cache items to invalidate, so we'd have to invalidate everything:
|
Chris@0
|
535 * we had to sacrifice effectiveness to achieve correctness. With cache tags, we
|
Chris@0
|
536 * can have both.
|
Chris@0
|
537 *
|
Chris@0
|
538 * Example:
|
Chris@0
|
539 * @code
|
Chris@0
|
540 * // A cache item with nodes, users, and some custom module data.
|
Chris@0
|
541 * $tags = array(
|
Chris@0
|
542 * 'my_custom_tag',
|
Chris@0
|
543 * 'node:1',
|
Chris@0
|
544 * 'node:3',
|
Chris@0
|
545 * 'user:7',
|
Chris@0
|
546 * );
|
Chris@0
|
547 * \Drupal::cache()->set($cid, $data, CacheBackendInterface::CACHE_PERMANENT, $tags);
|
Chris@0
|
548 *
|
Chris@0
|
549 * // Invalidate all cache items with certain tags.
|
Chris@0
|
550 * \Drupal\Core\Cache\Cache::invalidateTags(array('user:1'));
|
Chris@0
|
551 * @endcode
|
Chris@0
|
552 *
|
Chris@0
|
553 * Drupal is a content management system, so naturally you want changes to your
|
Chris@0
|
554 * content to be reflected everywhere, immediately. That's why we made sure that
|
Chris@0
|
555 * every entity type in Drupal 8 automatically has support for cache tags: when
|
Chris@0
|
556 * you save an entity, you can be sure that the cache items that have the
|
Chris@0
|
557 * corresponding cache tags will be invalidated.
|
Chris@0
|
558 * This also is the case when you define your own entity types: you'll get the
|
Chris@0
|
559 * exact same cache tag invalidation as any of the built-in entity types, with
|
Chris@0
|
560 * the ability to override any of the default behavior if needed.
|
Chris@16
|
561 * See \Drupal\Core\Cache\CacheableDependencyInterface::getCacheTags(),
|
Chris@0
|
562 * \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags(),
|
Chris@0
|
563 * \Drupal\Core\Entity\Entity::invalidateTagsOnSave() and
|
Chris@0
|
564 * \Drupal\Core\Entity\Entity::invalidateTagsOnDelete().
|
Chris@0
|
565 *
|
Chris@0
|
566 * @section context Cache contexts
|
Chris@0
|
567 *
|
Chris@0
|
568 * Some computed data depends on contextual data, such as the user roles of the
|
Chris@0
|
569 * logged-in user who is viewing a page, the language the page is being rendered
|
Chris@0
|
570 * in, the theme being used, etc. When caching the output of such a calculation,
|
Chris@0
|
571 * you must cache each variation separately, along with information about which
|
Chris@16
|
572 * variation of the contextual data was used in the calculation. The next time
|
Chris@0
|
573 * the computed data is needed, if the context matches that for an existing
|
Chris@0
|
574 * cached data set, the cached data can be reused; if no context matches, a new
|
Chris@0
|
575 * data set can be calculated and cached for later use.
|
Chris@0
|
576 *
|
Chris@0
|
577 * Cache contexts are services tagged with 'cache.context', whose classes
|
Chris@0
|
578 * implement \Drupal\Core\Cache\Context\CacheContextInterface. See
|
Chris@0
|
579 * https://www.drupal.org/developing/api/8/cache/contexts for more information
|
Chris@0
|
580 * on cache contexts, including a list of the contexts that exist in Drupal
|
Chris@0
|
581 * core, and information on how to define your own contexts. See the
|
Chris@0
|
582 * @link container Services and the Dependency Injection Container @endlink
|
Chris@0
|
583 * topic for more information about services.
|
Chris@0
|
584 *
|
Chris@0
|
585 * Typically, the cache context is specified as part of the #cache property
|
Chris@0
|
586 * of a render array; see the Caching section of the
|
Chris@0
|
587 * @link theme_render Render API overview topic @endlink for details.
|
Chris@0
|
588 *
|
Chris@0
|
589 * @section configuration Configuration
|
Chris@0
|
590 *
|
Chris@0
|
591 * By default cached data is stored in the database. This can be configured
|
Chris@0
|
592 * though so that all cached data, or that of an individual cache bin, uses a
|
Chris@0
|
593 * different cache backend, such as APCu or Memcache, for storage.
|
Chris@0
|
594 *
|
Chris@0
|
595 * In a settings.php file, you can override the service used for a particular
|
Chris@0
|
596 * cache bin. For example, if your service implementation of
|
Chris@0
|
597 * \Drupal\Core\Cache\CacheBackendInterface was called cache.custom, the
|
Chris@0
|
598 * following line would make Drupal use it for the 'cache_render' bin:
|
Chris@0
|
599 * @code
|
Chris@0
|
600 * $settings['cache']['bins']['render'] = 'cache.custom';
|
Chris@0
|
601 * @endcode
|
Chris@0
|
602 *
|
Chris@0
|
603 * Additionally, you can register your cache implementation to be used by
|
Chris@0
|
604 * default for all cache bins with:
|
Chris@0
|
605 * @code
|
Chris@0
|
606 * $settings['cache']['default'] = 'cache.custom';
|
Chris@0
|
607 * @endcode
|
Chris@0
|
608 *
|
Chris@0
|
609 * For cache bins that are stored in the database, the number of rows is limited
|
Chris@0
|
610 * to 5000 by default. This can be changed for all database cache bins. For
|
Chris@0
|
611 * example, to instead limit the number of rows to 50000:
|
Chris@0
|
612 * @code
|
Chris@0
|
613 * $settings['database_cache_max_rows']['default'] = 50000;
|
Chris@0
|
614 * @endcode
|
Chris@0
|
615 *
|
Chris@0
|
616 * Or per bin (in this example we allow infinite entries):
|
Chris@0
|
617 * @code
|
Chris@0
|
618 * $settings['database_cache_max_rows']['bins']['dynamic_page_cache'] = -1;
|
Chris@0
|
619 * @endcode
|
Chris@0
|
620 *
|
Chris@0
|
621 * For monitoring reasons it might be useful to figure out the amount of data
|
Chris@0
|
622 * stored in tables. The following SQL snippet can be used for that:
|
Chris@0
|
623 * @code
|
Chris@0
|
624 * SELECT table_name AS `Table`, table_rows AS 'Num. of Rows',
|
Chris@0
|
625 * ROUND(((data_length + index_length) / 1024 / 1024), 2) `Size in MB` FROM
|
Chris@0
|
626 * information_schema.TABLES WHERE table_schema = '***DATABASE_NAME***' AND
|
Chris@0
|
627 * table_name LIKE 'cache_%' ORDER BY (data_length + index_length) DESC
|
Chris@0
|
628 * LIMIT 10;
|
Chris@0
|
629 * @endcode
|
Chris@0
|
630 *
|
Chris@0
|
631 * @see \Drupal\Core\Cache\DatabaseBackend
|
Chris@0
|
632 *
|
Chris@0
|
633 * Finally, you can chain multiple cache backends together, see
|
Chris@0
|
634 * \Drupal\Core\Cache\ChainedFastBackend and \Drupal\Core\Cache\BackendChain.
|
Chris@0
|
635 *
|
Chris@0
|
636 * @see https://www.drupal.org/node/1884796
|
Chris@0
|
637 * @}
|
Chris@0
|
638 */
|
Chris@0
|
639
|
Chris@0
|
640 /**
|
Chris@0
|
641 * @defgroup user_api User accounts, permissions, and roles
|
Chris@0
|
642 * @{
|
Chris@0
|
643 * API for user accounts, access checking, roles, and permissions.
|
Chris@0
|
644 *
|
Chris@0
|
645 * @section sec_overview Overview and terminology
|
Chris@0
|
646 * Drupal's permission system is based on the concepts of accounts, roles,
|
Chris@0
|
647 * and permissions.
|
Chris@0
|
648 *
|
Chris@0
|
649 * Users (site visitors) have accounts, which include a user name, an email
|
Chris@0
|
650 * address, a password (or some other means of authentication), and possibly
|
Chris@0
|
651 * other fields (if defined on the site). Anonymous users have an implicit
|
Chris@0
|
652 * account that does not have a real user name or any account information.
|
Chris@0
|
653 *
|
Chris@0
|
654 * Each user account is assigned one or more roles. The anonymous user account
|
Chris@0
|
655 * automatically has the anonymous user role; real user accounts
|
Chris@0
|
656 * automatically have the authenticated user role, plus any roles defined on
|
Chris@0
|
657 * the site that they have been assigned.
|
Chris@0
|
658 *
|
Chris@0
|
659 * Each role, including the special anonymous and authenticated user roles, is
|
Chris@0
|
660 * granted one or more named permissions, which allow them to perform certain
|
Chris@0
|
661 * tasks or view certain content on the site. It is possible to designate a
|
Chris@0
|
662 * role to be the "administrator" role; if this is set up, this role is
|
Chris@0
|
663 * automatically granted all available permissions whenever a module is
|
Chris@0
|
664 * enabled that defines permissions.
|
Chris@0
|
665 *
|
Chris@0
|
666 * All code in Drupal that allows users to perform tasks or view content must
|
Chris@0
|
667 * check that the current user has the correct permission before allowing the
|
Chris@0
|
668 * action. In the standard case, access checking consists of answering the
|
Chris@0
|
669 * question "Does the current user have permission 'foo'?", and allowing or
|
Chris@0
|
670 * denying access based on the answer. Note that access checking should nearly
|
Chris@0
|
671 * always be done at the permission level, not by checking for a particular role
|
Chris@0
|
672 * or user ID, so that site administrators can set up user accounts and roles
|
Chris@0
|
673 * appropriately for their particular sites.
|
Chris@0
|
674 *
|
Chris@0
|
675 * @section sec_define Defining permissions
|
Chris@0
|
676 * Modules define permissions via a $module.permissions.yml file. See
|
Chris@0
|
677 * \Drupal\user\PermissionHandler for documentation of permissions.yml files.
|
Chris@0
|
678 *
|
Chris@0
|
679 * @section sec_access Access permission checking
|
Chris@0
|
680 * Depending on the situation, there are several methods for ensuring that
|
Chris@0
|
681 * access checks are done properly in Drupal:
|
Chris@0
|
682 * - Routes: When you register a route, include a 'requirements' section that
|
Chris@0
|
683 * either gives the machine name of the permission that is needed to visit the
|
Chris@0
|
684 * URL of the route, or tells Drupal to use an access check method or service
|
Chris@0
|
685 * to check access. See the @link menu Routing topic @endlink for more
|
Chris@0
|
686 * information.
|
Chris@0
|
687 * - Entities: Access for various entity operations is designated either with
|
Chris@0
|
688 * simple permissions or access control handler classes in the entity
|
Chris@0
|
689 * annotation. See the @link entity_api Entity API topic @endlink for more
|
Chris@0
|
690 * information.
|
Chris@0
|
691 * - Other code: There is a 'current_user' service, which can be injected into
|
Chris@0
|
692 * classes to provide access to the current user account (see the
|
Chris@0
|
693 * @link container Services and Dependency Injection topic @endlink for more
|
Chris@0
|
694 * information on dependency injection). In code that cannot use dependency
|
Chris@0
|
695 * injection, you can access this service and retrieve the current user
|
Chris@0
|
696 * account object by calling \Drupal::currentUser(). Once you have a user
|
Chris@0
|
697 * object for the current user (implementing \Drupal\user\UserInterface), you
|
Chris@0
|
698 * can call inherited method
|
Chris@0
|
699 * \Drupal\Core\Session\AccountInterface::hasPermission() to check
|
Chris@0
|
700 * permissions, or pass this object into other functions/methods.
|
Chris@0
|
701 * - Forms: Each element of a form array can have a Boolean '#access' property,
|
Chris@0
|
702 * which determines whether that element is visible and/or usable. This is a
|
Chris@0
|
703 * common need in forms, so the current user service (described above) is
|
Chris@0
|
704 * injected into the form base class as method
|
Chris@0
|
705 * \Drupal\Core\Form\FormBase::currentUser().
|
Chris@0
|
706 *
|
Chris@0
|
707 * @section sec_entities User and role objects
|
Chris@0
|
708 * User objects in Drupal are entity items, implementing
|
Chris@0
|
709 * \Drupal\user\UserInterface. Role objects in Drupal are also entity items,
|
Chris@0
|
710 * implementing \Drupal\user\RoleInterface. See the
|
Chris@0
|
711 * @link entity_api Entity API topic @endlink for more information about
|
Chris@0
|
712 * entities in general (including how to load, create, modify, and query them).
|
Chris@0
|
713 *
|
Chris@0
|
714 * Roles often need to be manipulated in automated test code, such as to add
|
Chris@0
|
715 * permissions to them. Here's an example:
|
Chris@0
|
716 * @code
|
Chris@0
|
717 * $role = \Drupal\user\Entity\Role::load('authenticated');
|
Chris@0
|
718 * $role->grantPermission('access comments');
|
Chris@0
|
719 * $role->save();
|
Chris@0
|
720 * @endcode
|
Chris@0
|
721 *
|
Chris@0
|
722 * Other important interfaces:
|
Chris@0
|
723 * - \Drupal\Core\Session\AccountInterface: The part of UserInterface that
|
Chris@0
|
724 * deals with access checking. In writing code that checks access, your
|
Chris@0
|
725 * method parameters should use this interface, not UserInterface.
|
Chris@0
|
726 * - \Drupal\Core\Session\AccountProxyInterface: The interface for the
|
Chris@0
|
727 * current_user service (described above).
|
Chris@0
|
728 * @}
|
Chris@0
|
729 */
|
Chris@0
|
730
|
Chris@0
|
731 /**
|
Chris@0
|
732 * @defgroup container Services and Dependency Injection Container
|
Chris@0
|
733 * @{
|
Chris@0
|
734 * Overview of the Dependency Injection Container and Services.
|
Chris@0
|
735 *
|
Chris@0
|
736 * @section sec_overview Overview of container, injection, and services
|
Chris@0
|
737 * The Services and Dependency Injection Container concepts have been adopted by
|
Chris@0
|
738 * Drupal from the @link http://symfony.com/ Symfony framework. @endlink A
|
Chris@0
|
739 * "service" (such as accessing the database, sending email, or translating user
|
Chris@0
|
740 * interface text) is defined (given a name and an interface or at least a
|
Chris@0
|
741 * class that defines the methods that may be called), and a default class is
|
Chris@0
|
742 * designated to provide the service. These two steps must be done together, and
|
Chris@0
|
743 * can be done by Drupal Core or a module. Other modules can then define
|
Chris@0
|
744 * alternative classes to provide the same services, overriding the default
|
Chris@0
|
745 * classes. Classes and functions that need to use the service should always
|
Chris@0
|
746 * instantiate the class via the dependency injection container (also known
|
Chris@0
|
747 * simply as the "container"), rather than instantiating a particular service
|
Chris@0
|
748 * provider class directly, so that they get the correct class (default or
|
Chris@0
|
749 * overridden).
|
Chris@0
|
750 *
|
Chris@0
|
751 * See https://www.drupal.org/node/2133171 for more detailed information on
|
Chris@0
|
752 * services and the dependency injection container.
|
Chris@0
|
753 *
|
Chris@0
|
754 * @section sec_discover Discovering existing services
|
Chris@0
|
755 * Drupal core defines many core services in the core.services.yml file (in the
|
Chris@0
|
756 * top-level core directory). Some Drupal Core modules and contributed modules
|
Chris@0
|
757 * also define services in modulename.services.yml files. API reference sites
|
Chris@0
|
758 * (such as https://api.drupal.org) generate lists of all existing services from
|
Chris@0
|
759 * these files. Look for the Services link in the API Navigation block.
|
Chris@0
|
760 * Alternatively you can look through the individual files manually.
|
Chris@0
|
761 *
|
Chris@0
|
762 * A typical service definition in a *.services.yml file looks like this:
|
Chris@0
|
763 * @code
|
Chris@0
|
764 * path.alias_manager:
|
Chris@0
|
765 * class: Drupal\Core\Path\AliasManager
|
Chris@0
|
766 * arguments: ['@path.crud', '@path.alias_whitelist', '@language_manager']
|
Chris@0
|
767 * @endcode
|
Chris@0
|
768 * Some services use other services as factories; a typical service definition
|
Chris@0
|
769 * is:
|
Chris@0
|
770 * @code
|
Chris@0
|
771 * cache.entity:
|
Chris@0
|
772 * class: Drupal\Core\Cache\CacheBackendInterface
|
Chris@0
|
773 * tags:
|
Chris@0
|
774 * - { name: cache.bin }
|
Chris@0
|
775 * factory: cache_factory:get
|
Chris@0
|
776 * arguments: [entity]
|
Chris@0
|
777 * @endcode
|
Chris@0
|
778 *
|
Chris@0
|
779 * The first line of a service definition gives the unique machine name of the
|
Chris@0
|
780 * service. This is often prefixed by the module name if provided by a module;
|
Chris@0
|
781 * however, by convention some service names are prefixed by a group name
|
Chris@0
|
782 * instead, such as cache.* for cache bins and plugin.manager.* for plugin
|
Chris@0
|
783 * managers.
|
Chris@0
|
784 *
|
Chris@0
|
785 * The class line either gives the default class that provides the service, or
|
Chris@0
|
786 * if the service uses a factory class, the interface for the service. If the
|
Chris@0
|
787 * class depends on other services, the arguments line lists the machine
|
Chris@0
|
788 * names of the dependencies (preceded by '@'); objects for each of these
|
Chris@0
|
789 * services are instantiated from the container and passed to the class
|
Chris@0
|
790 * constructor when the service class is instantiated. Other arguments can also
|
Chris@0
|
791 * be passed in; see the section at https://www.drupal.org/node/2133171 for more
|
Chris@0
|
792 * detailed information.
|
Chris@0
|
793 *
|
Chris@0
|
794 * Services using factories can be defined as shown in the above example, if the
|
Chris@0
|
795 * factory is itself a service. The factory can also be a class; details of how
|
Chris@0
|
796 * to use service factories can be found in the section at
|
Chris@0
|
797 * https://www.drupal.org/node/2133171.
|
Chris@0
|
798 *
|
Chris@0
|
799 * @section sec_container Accessing a service through the container
|
Chris@0
|
800 * As noted above, if you need to use a service in your code, you should always
|
Chris@0
|
801 * instantiate the service class via a call to the container, using the machine
|
Chris@0
|
802 * name of the service, so that the default class can be overridden. There are
|
Chris@0
|
803 * several ways to make sure this happens:
|
Chris@0
|
804 * - For service-providing classes, see other sections of this documentation
|
Chris@0
|
805 * describing how to pass services as arguments to the constructor.
|
Chris@0
|
806 * - Plugin classes, controllers, and similar classes have create() or
|
Chris@0
|
807 * createInstance() methods that are used to create an instance of the class.
|
Chris@0
|
808 * These methods come from different interfaces, and have different
|
Chris@0
|
809 * arguments, but they all include an argument $container of type
|
Chris@0
|
810 * \Symfony\Component\DependencyInjection\ContainerInterface.
|
Chris@0
|
811 * If you are defining one of these classes, in the create() or
|
Chris@0
|
812 * createInstance() method, call $container->get('myservice.name') to
|
Chris@0
|
813 * instantiate a service. The results of these calls are generally passed to
|
Chris@0
|
814 * the class constructor and saved as member variables in the class.
|
Chris@0
|
815 * - For functions and class methods that do not have access to either of
|
Chris@0
|
816 * the above methods of dependency injection, you can use service location to
|
Chris@0
|
817 * access services, via a call to the global \Drupal class. This class has
|
Chris@0
|
818 * special methods for accessing commonly-used services, or you can call a
|
Chris@0
|
819 * generic method to access any service. Examples:
|
Chris@0
|
820 * @code
|
Chris@0
|
821 * // Retrieve the entity.manager service object (special method exists).
|
Chris@0
|
822 * $manager = \Drupal::entityManager();
|
Chris@0
|
823 * // Retrieve the service object for machine name 'foo.bar'.
|
Chris@0
|
824 * $foobar = \Drupal::service('foo.bar');
|
Chris@0
|
825 * @endcode
|
Chris@0
|
826 *
|
Chris@0
|
827 * As a note, you should always use dependency injection (via service arguments
|
Chris@0
|
828 * or create()/createInstance() methods) if possible to instantiate services,
|
Chris@0
|
829 * rather than service location (via the \Drupal class), because:
|
Chris@0
|
830 * - Dependency injection facilitates writing unit tests, since the container
|
Chris@0
|
831 * argument can be mocked and the create() method can be bypassed by using
|
Chris@0
|
832 * the class constructor. If you use the \Drupal class, unit tests are much
|
Chris@0
|
833 * harder to write and your code has more dependencies.
|
Chris@0
|
834 * - Having the service interfaces on the class constructor and member variables
|
Chris@0
|
835 * is useful for IDE auto-complete and self-documentation.
|
Chris@0
|
836 *
|
Chris@0
|
837 * @section sec_define Defining a service
|
Chris@0
|
838 * If your module needs to define a new service, here are the steps:
|
Chris@0
|
839 * - Choose a unique machine name for your service. Typically, this should
|
Chris@0
|
840 * start with your module name. Example: mymodule.myservice.
|
Chris@0
|
841 * - Create a PHP interface to define what your service does.
|
Chris@0
|
842 * - Create a default class implementing your interface that provides your
|
Chris@0
|
843 * service. If your class needs to use existing services (such as database
|
Chris@0
|
844 * access), be sure to make these services arguments to your class
|
Chris@0
|
845 * constructor, and save them in member variables. Also, if the needed
|
Chris@0
|
846 * services are provided by other modules and not Drupal Core, you'll want
|
Chris@0
|
847 * these modules to be dependencies of your module.
|
Chris@0
|
848 * - Add an entry to a modulename.services.yml file for the service. See
|
Chris@0
|
849 * @ref sec_discover above, or existing *.services.yml files in Core, for the
|
Chris@0
|
850 * syntax; it will start with your machine name, refer to your default class,
|
Chris@0
|
851 * and list the services that need to be passed into your constructor.
|
Chris@0
|
852 *
|
Chris@0
|
853 * Services can also be defined dynamically, as in the
|
Chris@0
|
854 * \Drupal\Core\CoreServiceProvider class, but this is less common for modules.
|
Chris@0
|
855 *
|
Chris@0
|
856 * @section sec_tags Service tags
|
Chris@0
|
857 * Some services have tags, which are defined in the service definition. See
|
Chris@0
|
858 * @link service_tag Service Tags @endlink for usage.
|
Chris@0
|
859 *
|
Chris@0
|
860 * @section sec_injection Overriding the default service class
|
Chris@0
|
861 * Modules can override the default classes used for services. Here are the
|
Chris@0
|
862 * steps:
|
Chris@0
|
863 * - Define a class in the top-level namespace for your module
|
Chris@0
|
864 * (Drupal\my_module), whose name is the camel-case version of your module's
|
Chris@0
|
865 * machine name followed by "ServiceProvider" (for example, if your module
|
Chris@0
|
866 * machine name is my_module, the class must be named
|
Chris@0
|
867 * MyModuleServiceProvider).
|
Chris@0
|
868 * - The class needs to implement
|
Chris@0
|
869 * \Drupal\Core\DependencyInjection\ServiceModifierInterface, which is
|
Chris@0
|
870 * typically done by extending
|
Chris@0
|
871 * \Drupal\Core\DependencyInjection\ServiceProviderBase.
|
Chris@0
|
872 * - The class needs to contain one method: alter(). This method does the
|
Chris@0
|
873 * actual work of telling Drupal to use your class instead of the default.
|
Chris@0
|
874 * Here's an example:
|
Chris@0
|
875 * @code
|
Chris@0
|
876 * public function alter(ContainerBuilder $container) {
|
Chris@0
|
877 * // Override the language_manager class with a new class.
|
Chris@0
|
878 * $definition = $container->getDefinition('language_manager');
|
Chris@0
|
879 * $definition->setClass('Drupal\my_module\MyLanguageManager');
|
Chris@0
|
880 * }
|
Chris@0
|
881 * @endcode
|
Chris@0
|
882 * Note that $container here is an instance of
|
Chris@0
|
883 * \Drupal\Core\DependencyInjection\ContainerBuilder.
|
Chris@0
|
884 *
|
Chris@0
|
885 * @see https://www.drupal.org/node/2133171
|
Chris@0
|
886 * @see core.services.yml
|
Chris@0
|
887 * @see \Drupal
|
Chris@0
|
888 * @see \Symfony\Component\DependencyInjection\ContainerInterface
|
Chris@0
|
889 * @see plugin_api
|
Chris@0
|
890 * @see menu
|
Chris@0
|
891 * @}
|
Chris@0
|
892 */
|
Chris@0
|
893
|
Chris@0
|
894 /**
|
Chris@0
|
895 * @defgroup listing_page_service Page header for Services page
|
Chris@0
|
896 * @{
|
Chris@0
|
897 * Introduction to services
|
Chris@0
|
898 *
|
Chris@0
|
899 * A "service" (such as accessing the database, sending email, or translating
|
Chris@0
|
900 * user interface text) can be defined by a module or Drupal core. Defining a
|
Chris@0
|
901 * service means giving it a name and designating a default class to provide the
|
Chris@0
|
902 * service; ideally, there should also be an interface that defines the methods
|
Chris@0
|
903 * that may be called. Services are collected into the Dependency Injection
|
Chris@0
|
904 * Container, and can be overridden to use different classes or different
|
Chris@0
|
905 * instantiation by modules. See the
|
Chris@0
|
906 * @link container Services and Dependency Injection Container topic @endlink
|
Chris@0
|
907 * for details.
|
Chris@0
|
908 *
|
Chris@0
|
909 * Some services have tags, which are defined in the service definition. Tags
|
Chris@0
|
910 * are used to define a group of related services, or to specify some aspect of
|
Chris@0
|
911 * how the service behaves. See the
|
Chris@0
|
912 * @link service_tag Service Tags topic @endlink for more information.
|
Chris@0
|
913 *
|
Chris@0
|
914 * @see container
|
Chris@0
|
915 * @see service_tag
|
Chris@0
|
916 *
|
Chris@0
|
917 * @}
|
Chris@0
|
918 */
|
Chris@0
|
919
|
Chris@0
|
920 /**
|
Chris@0
|
921 * @defgroup typed_data Typed Data API
|
Chris@0
|
922 * @{
|
Chris@0
|
923 * API for describing data based on a set of available data types.
|
Chris@0
|
924 *
|
Chris@0
|
925 * PHP has data types, such as int, string, float, array, etc., and it is an
|
Chris@0
|
926 * object-oriented language that lets you define classes and interfaces.
|
Chris@0
|
927 * However, in some cases, it is useful to be able to define an abstract
|
Chris@0
|
928 * type (as in an interface, free of implementation details), that still has
|
Chris@0
|
929 * properties (which an interface cannot) as well as meta-data. The Typed Data
|
Chris@0
|
930 * API provides this abstraction.
|
Chris@0
|
931 *
|
Chris@0
|
932 * @section sec_overview Overview
|
Chris@0
|
933 * Each data type in the Typed Data API is a plugin class (annotation class
|
Chris@0
|
934 * example: \Drupal\Core\TypedData\Annotation\DataType); these plugins are
|
Chris@0
|
935 * managed by the typed_data_manager service (by default
|
Chris@0
|
936 * \Drupal\Core\TypedData\TypedDataManager). Each data object encapsulates a
|
Chris@0
|
937 * single piece of data, provides access to the metadata, and provides
|
Chris@0
|
938 * validation capability. Also, the typed data plugins have a shorthand
|
Chris@0
|
939 * for easily accessing data values, described in @ref sec_tree.
|
Chris@0
|
940 *
|
Chris@0
|
941 * The metadata of a data object is defined by an object based on a class called
|
Chris@0
|
942 * the definition class (see \Drupal\Core\TypedData\DataDefinitionInterface).
|
Chris@0
|
943 * The class used can vary by data type and can be specified in the data type's
|
Chris@0
|
944 * plugin definition, while the default is set in the $definition_class property
|
Chris@0
|
945 * of the annotation class. The default class is
|
Chris@0
|
946 * \Drupal\Core\TypedData\DataDefinition. For data types provided by a plugin
|
Chris@0
|
947 * deriver, the plugin deriver can set the definition_class property too.
|
Chris@0
|
948 * The metadata object provides information about the data, such as the data
|
Chris@0
|
949 * type, whether it is translatable, the names of its properties (for complex
|
Chris@0
|
950 * types), and who can access it.
|
Chris@0
|
951 *
|
Chris@0
|
952 * See https://www.drupal.org/node/1794140 for more information about the Typed
|
Chris@0
|
953 * Data API.
|
Chris@0
|
954 *
|
Chris@0
|
955 * @section sec_varieties Varieties of typed data
|
Chris@0
|
956 * There are three kinds of typed data: primitive, complex, and list.
|
Chris@0
|
957 *
|
Chris@0
|
958 * @subsection sub_primitive Primitive data types
|
Chris@0
|
959 * Primitive data types wrap PHP data types and also serve as building blocks
|
Chris@0
|
960 * for complex and list typed data. Each primitive data type has an interface
|
Chris@0
|
961 * that extends \Drupal\Core\TypedData\PrimitiveInterface, with getValue()
|
Chris@0
|
962 * and setValue() methods for accessing the data value, and a default plugin
|
Chris@0
|
963 * implementation. Here's a list:
|
Chris@0
|
964 * - \Drupal\Core\TypedData\Type\IntegerInterface: Plugin ID integer,
|
Chris@0
|
965 * corresponds to PHP type int.
|
Chris@0
|
966 * - \Drupal\Core\TypedData\Type\StringInterface: Plugin ID string,
|
Chris@0
|
967 * corresponds to PHP type string.
|
Chris@0
|
968 * - \Drupal\Core\TypedData\Type\FloatInterface: Plugin ID float,
|
Chris@0
|
969 * corresponds to PHP type float.
|
Chris@0
|
970 * - \Drupal\Core\TypedData\Type\BooleanInterface: Plugin ID bool,
|
Chris@0
|
971 * corresponds to PHP type bool.
|
Chris@0
|
972 * - \Drupal\Core\TypedData\Type\BinaryInterface: Plugin ID binary,
|
Chris@0
|
973 * corresponds to a PHP file resource.
|
Chris@0
|
974 * - \Drupal\Core\TypedData\Type\UriInterface: Plugin ID uri.
|
Chris@0
|
975 *
|
Chris@0
|
976 * @subsection sec_complex Complex data
|
Chris@0
|
977 * Complex data types, with interface
|
Chris@0
|
978 * \Drupal\Core\TypedData\ComplexDataInterface, represent data with named
|
Chris@0
|
979 * properties; the properties can be accessed with get() and set() methods.
|
Chris@0
|
980 * The value of each property is itself a typed data object, which can be
|
Chris@0
|
981 * primitive, complex, or list data.
|
Chris@0
|
982 *
|
Chris@0
|
983 * The base type for most complex data is the
|
Chris@0
|
984 * \Drupal\Core\TypedData\Plugin\DataType\Map class, which represents an
|
Chris@0
|
985 * associative array. Map provides its own definition class in the annotation,
|
Chris@0
|
986 * \Drupal\Core\TypedData\MapDataDefinition, and most complex data classes
|
Chris@0
|
987 * extend this class. The getValue() and setValue() methods on the Map class
|
Chris@0
|
988 * enforce the data definition and its property structure.
|
Chris@0
|
989 *
|
Chris@0
|
990 * The Drupal Field API uses complex typed data for its field items, with
|
Chris@0
|
991 * definition class \Drupal\Core\Field\TypedData\FieldItemDataDefinition.
|
Chris@0
|
992 *
|
Chris@0
|
993 * @section sec_list Lists
|
Chris@0
|
994 * List data types, with interface \Drupal\Core\TypedData\ListInterface,
|
Chris@0
|
995 * represent data that is an ordered list of typed data, all of the same type.
|
Chris@0
|
996 * More precisely, the plugins in the list must have the same base plugin ID;
|
Chris@0
|
997 * however, some types (for example field items and entities) are provided by
|
Chris@0
|
998 * plugin derivatives and the sub IDs can be different.
|
Chris@0
|
999 *
|
Chris@0
|
1000 * @section sec_tree Tree handling
|
Chris@0
|
1001 * Typed data allows you to use shorthand to get data values nested in the
|
Chris@0
|
1002 * implicit tree structure of the data. For example, to get the value from
|
Chris@0
|
1003 * an entity field item, the Entity Field API allows you to call:
|
Chris@0
|
1004 * @code
|
Chris@0
|
1005 * $value = $entity->fieldName->propertyName;
|
Chris@0
|
1006 * @endcode
|
Chris@0
|
1007 * This is really shorthand for:
|
Chris@0
|
1008 * @code
|
Chris@0
|
1009 * $field_item_list = $entity->get('fieldName');
|
Chris@0
|
1010 * $field_item = $field_item_list->get(0);
|
Chris@0
|
1011 * $property = $field_item->get('propertyName');
|
Chris@0
|
1012 * $value = $property->getValue();
|
Chris@0
|
1013 * @endcode
|
Chris@0
|
1014 * Some notes:
|
Chris@0
|
1015 * - $property, $field_item, and $field_item_list are all typed data objects,
|
Chris@0
|
1016 * while $value is a raw PHP value.
|
Chris@0
|
1017 * - You can call $property->getParent() to get $field_item,
|
Chris@0
|
1018 * $field_item->getParent() to get $field_item_list, or
|
Chris@0
|
1019 * $field_item_list->getParent() to get $typed_entity ($entity wrapped in a
|
Chris@0
|
1020 * typed data object). $typed_entity->getParent() is NULL.
|
Chris@0
|
1021 * - For all of these ->getRoot() returns $typed_entity.
|
Chris@0
|
1022 * - The langcode property is on $field_item_list, but you can access it
|
Chris@0
|
1023 * on $property as well, so that all items will report the same langcode.
|
Chris@0
|
1024 * - When the value of $property is changed by calling $property->setValue(),
|
Chris@0
|
1025 * $property->onChange() will fire, which in turn calls the parent object's
|
Chris@0
|
1026 * onChange() method and so on. This allows parent objects to react upon
|
Chris@0
|
1027 * changes of contained properties or list items.
|
Chris@0
|
1028 *
|
Chris@0
|
1029 * @section sec_defining Defining data types
|
Chris@0
|
1030 * To define a new data type:
|
Chris@0
|
1031 * - Create a class that implements one of the Typed Data interfaces.
|
Chris@0
|
1032 * Typically, you will want to extend one of the classes listed in the
|
Chris@0
|
1033 * sections above as a starting point.
|
Chris@0
|
1034 * - Make your class into a DataType plugin. To do that, put it in namespace
|
Chris@0
|
1035 * \Drupal\yourmodule\Plugin\DataType (where "yourmodule" is your module's
|
Chris@0
|
1036 * short name), and add annotation of type
|
Chris@0
|
1037 * \Drupal\Core\TypedData\Annotation\DataType to the documentation header.
|
Chris@0
|
1038 * See the @link plugin_api Plugin API topic @endlink and the
|
Chris@0
|
1039 * @link annotation Annotations topic @endlink for more information.
|
Chris@0
|
1040 *
|
Chris@0
|
1041 * @section sec_using Using data types
|
Chris@0
|
1042 * The data types of the Typed Data API can be used in several ways, once they
|
Chris@0
|
1043 * have been defined:
|
Chris@0
|
1044 * - In the Field API, data types can be used as the class in the property
|
Chris@0
|
1045 * definition of the field. See the @link field Field API topic @endlink for
|
Chris@0
|
1046 * more information.
|
Chris@0
|
1047 * - In configuration schema files, you can use the unique ID ('id' annotation)
|
Chris@0
|
1048 * from any DataType plugin class as the 'type' value for an entry. See the
|
Chris@18
|
1049 * @link config_api Configuration API topic @endlink for more information.
|
Chris@0
|
1050 * - If you need to create a typed data object in code, first get the
|
Chris@0
|
1051 * typed_data_manager service from the container or by calling
|
Chris@0
|
1052 * \Drupal::typedDataManager(). Then pass the plugin ID to
|
Chris@0
|
1053 * $manager::createDataDefinition() to create an appropriate data definition
|
Chris@0
|
1054 * object. Then pass the data definition object and the value of the data to
|
Chris@0
|
1055 * $manager::create() to create a typed data object.
|
Chris@0
|
1056 *
|
Chris@0
|
1057 * @see plugin_api
|
Chris@0
|
1058 * @see container
|
Chris@0
|
1059 * @}
|
Chris@0
|
1060 */
|
Chris@0
|
1061
|
Chris@0
|
1062 /**
|
Chris@0
|
1063 * @defgroup testing Automated tests
|
Chris@0
|
1064 * @{
|
Chris@18
|
1065 * Overview of PHPUnit and Nightwatch automated tests.
|
Chris@0
|
1066 *
|
Chris@0
|
1067 * The Drupal project has embraced a philosophy of using automated tests,
|
Chris@0
|
1068 * consisting of both unit tests (which test the functionality of classes at a
|
Chris@0
|
1069 * low level) and functional tests (which test the functionality of Drupal
|
Chris@0
|
1070 * systems at a higher level, usually involving web output). The goal is to
|
Chris@0
|
1071 * have test coverage for all or most of the components and features, and to
|
Chris@0
|
1072 * run the automated tests before any code is changed or added, to make sure
|
Chris@0
|
1073 * it doesn't break any existing functionality (regression testing).
|
Chris@0
|
1074 *
|
Chris@0
|
1075 * In order to implement this philosophy, developers need to do the following:
|
Chris@0
|
1076 * - When making a patch to fix a bug, make sure that the bug fix patch includes
|
Chris@0
|
1077 * a test that fails without the code change and passes with the code change.
|
Chris@0
|
1078 * This helps reviewers understand what the bug is, demonstrates that the code
|
Chris@0
|
1079 * actually fixes the bug, and ensures the bug will not reappear due to later
|
Chris@0
|
1080 * code changes.
|
Chris@0
|
1081 * - When making a patch to implement a new feature, include new unit and/or
|
Chris@0
|
1082 * functional tests in the patch. This serves to both demonstrate that the
|
Chris@0
|
1083 * code actually works, and ensure that later changes do not break the new
|
Chris@0
|
1084 * functionality.
|
Chris@0
|
1085 *
|
Chris@18
|
1086 * @section write_test Writing tests
|
Chris@18
|
1087 * All PHP-based tests for Drupal core are written using the industry-standard
|
Chris@18
|
1088 * PHPUnit framework, with Drupal extensions. There are several categories of
|
Chris@18
|
1089 * tests; each has its own purpose, base class, namespace, and directory:
|
Chris@18
|
1090 * - Unit tests:
|
Chris@18
|
1091 * - Purpose: Test functionality of a class if the Drupal environment
|
Chris@18
|
1092 * (database, settings, etc.) and web browser are not needed for the test,
|
Chris@18
|
1093 * or if the Drupal environment can be replaced by a "mock" object.
|
Chris@18
|
1094 * - Base class: \Drupal\Tests\UnitTestCase
|
Chris@18
|
1095 * - Namespace: \Drupal\Tests\yourmodule\Unit (or a subdirectory)
|
Chris@18
|
1096 * - Directory location: yourmodule/tests/src/Unit (or a subdirectory)
|
Chris@18
|
1097 * - Kernel tests:
|
Chris@18
|
1098 * - Purpose: Test functionality of a class if the full Drupal environment
|
Chris@18
|
1099 * and web browser are not needed for the test, but the functionality has
|
Chris@18
|
1100 * significant Drupal dependencies that cannot easily be mocked. Kernel
|
Chris@18
|
1101 * tests can access services, the database, and a minimal mocked file
|
Chris@18
|
1102 * system, and they use an in-memory pseudo-installation. However, modules
|
Chris@18
|
1103 * are only installed to the point of having services and hooks, unless you
|
Chris@18
|
1104 * install them explicitly.
|
Chris@18
|
1105 * - Base class: \Drupal\KernelTests\KernelTestBase
|
Chris@18
|
1106 * - Namespace: \Drupal\Tests\yourmodule\Kernel (or a subdirectory)
|
Chris@18
|
1107 * - Directory location: yourmodule/tests/src/Kernel (or a subdirectory)
|
Chris@18
|
1108 * - Browser tests:
|
Chris@18
|
1109 * - Purpose: Test functionality with the full Drupal environment and an
|
Chris@18
|
1110 * internal simulated web browser, if JavaScript is not needed.
|
Chris@18
|
1111 * - Base class: \Drupal\Tests\BrowserTestBase
|
Chris@18
|
1112 * - Namespace: \Drupal\Tests\yourmodule\Functional (or a subdirectory)
|
Chris@18
|
1113 * - Directory location: yourmodule/tests/src/Functional (or a subdirectory)
|
Chris@18
|
1114 * - Browser tests with JavaScript:
|
Chris@18
|
1115 * - Purpose: Test functionality with the full Drupal environment and an
|
Chris@18
|
1116 * internal web browser that includes JavaScript execution.
|
Chris@18
|
1117 * - Base class: \Drupal\FunctionalJavascriptTests\WebDriverTestBase
|
Chris@18
|
1118 * - Namespace: \Drupal\Tests\yourmodule\FunctionalJavascript (or a
|
Chris@18
|
1119 * subdirectory)
|
Chris@18
|
1120 * - Directory location: yourmodule/tests/src/FunctionalJavascript (or a
|
Chris@18
|
1121 * subdirectory)
|
Chris@18
|
1122 *
|
Chris@18
|
1123 * Some notes about writing PHP test classes:
|
Chris@18
|
1124 * - The class needs a phpDoc comment block with a description and
|
Chris@18
|
1125 * @group annotation, which gives information about the test.
|
Chris@18
|
1126 * - For unit tests, this comment block should also have @coversDefaultClass
|
Chris@18
|
1127 * annotation.
|
Chris@18
|
1128 * - When writing tests, put the test code into public methods, each covering a
|
Chris@18
|
1129 * logical subset of the functionality that is being tested.
|
Chris@18
|
1130 * - The test methods must have names starting with 'test'. For unit tests, the
|
Chris@18
|
1131 * test methods need to have a phpDoc block with @covers annotation telling
|
Chris@18
|
1132 * which class method they are testing.
|
Chris@18
|
1133 * - In some cases, you may need to write a test module to support your test;
|
Chris@18
|
1134 * put such modules under the yourmodule/tests/modules directory.
|
Chris@18
|
1135 *
|
Chris@18
|
1136 * Besides the PHPUnit tests described above, Drupal Core also includes a few
|
Chris@18
|
1137 * JavaScript-only tests, which use the Nightwatch.js framework to test
|
Chris@18
|
1138 * JavaScript code using only JavaScript. These are located in
|
Chris@18
|
1139 * core/tests/Drupal/Nightwatch.
|
Chris@18
|
1140 *
|
Chris@0
|
1141 * For more details, see:
|
Chris@18
|
1142 * - core/tests/README.md for instructions on running tests
|
Chris@0
|
1143 * - https://www.drupal.org/phpunit for full documentation on how to write
|
Chris@18
|
1144 * and run PHPUnit tests for Drupal.
|
Chris@0
|
1145 * - http://phpunit.de for general information on the PHPUnit framework.
|
Chris@0
|
1146 * - @link oo_conventions Object-oriented programming topic @endlink for more
|
Chris@0
|
1147 * on PSR-4, namespaces, and where to place classes.
|
Chris@18
|
1148 * - http://nightwatchjs.org/ for information about Nightwatch testing for
|
Chris@18
|
1149 * JavaScript
|
Chris@0
|
1150 * @}
|
Chris@0
|
1151 */
|
Chris@0
|
1152
|
Chris@0
|
1153 /**
|
Chris@0
|
1154 * @defgroup php_assert PHP Runtime Assert Statements
|
Chris@0
|
1155 * @{
|
Chris@0
|
1156 * Use of the assert() statement in Drupal.
|
Chris@0
|
1157 *
|
Chris@0
|
1158 * Unit tests also use the term "assertion" to refer to test conditions, so to
|
Chris@0
|
1159 * avoid confusion the term "runtime assertion" will be used for the assert()
|
Chris@0
|
1160 * statement throughout the documentation.
|
Chris@0
|
1161 *
|
Chris@0
|
1162 * A runtime assertion is a statement that is expected to always be true at
|
Chris@0
|
1163 * the point in the code it appears at. They are tested using PHP's internal
|
Chris@0
|
1164 * @link http://php.net/assert assert() @endlink statement. If an
|
Chris@0
|
1165 * assertion is ever FALSE it indicates an error in the code or in module or
|
Chris@0
|
1166 * theme configuration files. User-provided configuration files should be
|
Chris@0
|
1167 * verified with standard control structures at all times, not just checked in
|
Chris@0
|
1168 * development environments with assert() statements on.
|
Chris@0
|
1169 *
|
Chris@0
|
1170 * The Drupal project primarily uses runtime assertions to enforce the
|
Chris@0
|
1171 * expectations of the API by failing when incorrect calls are made by code
|
Chris@0
|
1172 * under development. While PHP type hinting does this for objects and arrays,
|
Chris@0
|
1173 * runtime assertions do this for scalars (strings, integers, floats, etc.) and
|
Chris@0
|
1174 * complex data structures such as cache and render arrays. They ensure that
|
Chris@16
|
1175 * methods' return values are the documented data types. They also verify that
|
Chris@0
|
1176 * objects have been properly configured and set up by the service container.
|
Chris@14
|
1177 * They supplement unit tests by checking scenarios that do not have unit tests
|
Chris@14
|
1178 * written for them.
|
Chris@0
|
1179 *
|
Chris@14
|
1180 * There are two php settings which affect runtime assertions. The first,
|
Chris@14
|
1181 * assert.exception, should always be set to 1. The second is zend.assertions.
|
Chris@14
|
1182 * Set this to -1 in production and 1 in development.
|
Chris@0
|
1183 *
|
Chris@0
|
1184 * See https://www.drupal.org/node/2492225 for more information on runtime
|
Chris@0
|
1185 * assertions.
|
Chris@0
|
1186 * @}
|
Chris@0
|
1187 */
|
Chris@0
|
1188
|
Chris@0
|
1189 /**
|
Chris@0
|
1190 * @defgroup info_types Information types
|
Chris@0
|
1191 * @{
|
Chris@0
|
1192 * Types of information in Drupal.
|
Chris@0
|
1193 *
|
Chris@0
|
1194 * Drupal has several distinct types of information, each with its own methods
|
Chris@0
|
1195 * for storage and retrieval:
|
Chris@0
|
1196 * - Content: Information meant to be displayed on your site: articles, basic
|
Chris@0
|
1197 * pages, images, files, custom blocks, etc. Content is stored and accessed
|
Chris@0
|
1198 * using @link entity_api Entities @endlink.
|
Chris@0
|
1199 * - Session: Information about individual users' interactions with the site,
|
Chris@0
|
1200 * such as whether they are logged in. This is really "state" information, but
|
Chris@16
|
1201 * it is not stored the same way so it's a separate type here. Session data is
|
Chris@16
|
1202 * accessed via \Symfony\Component\HttpFoundation\Request::getSession(), which
|
Chris@16
|
1203 * returns an instance of
|
Chris@0
|
1204 * \Symfony\Component\HttpFoundation\Session\SessionInterface.
|
Chris@16
|
1205 * See the @link session Sessions topic @endlink for more information.
|
Chris@0
|
1206 * - State: Information of a temporary nature, generally machine-generated and
|
Chris@0
|
1207 * not human-edited, about the current state of your site. Examples: the time
|
Chris@0
|
1208 * when Cron was last run, whether node access permissions need rebuilding,
|
Chris@0
|
1209 * etc. See @link state_api the State API topic @endlink for more information.
|
Chris@0
|
1210 * - Configuration: Information about your site that is generally (or at least
|
Chris@0
|
1211 * can be) human-edited, but is not Content, and is meant to be relatively
|
Chris@0
|
1212 * permanent. Examples: the name of your site, the content types and views
|
Chris@0
|
1213 * you have defined, etc. See
|
Chris@0
|
1214 * @link config_api the Configuration API topic @endlink for more information.
|
Chris@0
|
1215 *
|
Chris@0
|
1216 * @see cache
|
Chris@0
|
1217 * @see i18n
|
Chris@0
|
1218 * @}
|
Chris@0
|
1219 */
|
Chris@0
|
1220
|
Chris@0
|
1221 /**
|
Chris@0
|
1222 * @defgroup extending Extending and altering Drupal
|
Chris@0
|
1223 * @{
|
Chris@0
|
1224 * Overview of extensions and alteration methods for Drupal.
|
Chris@0
|
1225 *
|
Chris@0
|
1226 * @section sec_types Types of extensions
|
Chris@0
|
1227 * Drupal's core behavior can be extended and altered via these three basic
|
Chris@0
|
1228 * types of extensions:
|
Chris@0
|
1229 * - Themes: Themes alter the appearance of Drupal sites. They can include
|
Chris@0
|
1230 * template files, which alter the HTML markup and other raw output of the
|
Chris@0
|
1231 * site; CSS files, which alter the styling applied to the HTML; and
|
Chris@0
|
1232 * JavaScript, Flash, images, and other files. For more information, see the
|
Chris@0
|
1233 * @link theme_render Theme system and render API topic @endlink and
|
Chris@0
|
1234 * https://www.drupal.org/docs/8/theming
|
Chris@0
|
1235 * - Modules: Modules add to or alter the behavior and functionality of Drupal,
|
Chris@0
|
1236 * by using one or more of the methods listed below. For more information
|
Chris@0
|
1237 * about creating modules, see https://www.drupal.org/developing/modules/8
|
Chris@0
|
1238 * - Installation profiles: Installation profiles can be used to
|
Chris@0
|
1239 * create distributions, which are complete specific-purpose packages of
|
Chris@0
|
1240 * Drupal including additional modules, themes, and data. For more
|
Chris@0
|
1241 * information, see https://www.drupal.org/developing/distributions.
|
Chris@0
|
1242 *
|
Chris@0
|
1243 * @section sec_alter Alteration methods for modules
|
Chris@0
|
1244 * Here is a list of the ways that modules can alter or extend Drupal's core
|
Chris@0
|
1245 * behavior, or the behavior of other modules:
|
Chris@0
|
1246 * - Hooks: Specially-named functions that a module defines, which are
|
Chris@0
|
1247 * discovered and called at specific times, usually to alter behavior or data.
|
Chris@0
|
1248 * See the @link hooks Hooks topic @endlink for more information.
|
Chris@0
|
1249 * - Plugins: Classes that a module defines, which are discovered and
|
Chris@0
|
1250 * instantiated at specific times to add functionality. See the
|
Chris@0
|
1251 * @link plugin_api Plugin API topic @endlink for more information.
|
Chris@0
|
1252 * - Entities: Special plugins that define entity types for storing new types
|
Chris@0
|
1253 * of content or configuration in Drupal. See the
|
Chris@0
|
1254 * @link entity_api Entity API topic @endlink for more information.
|
Chris@0
|
1255 * - Services: Classes that perform basic operations within Drupal, such as
|
Chris@0
|
1256 * accessing the database and sending email. See the
|
Chris@0
|
1257 * @link container Dependency Injection Container and Services topic @endlink
|
Chris@0
|
1258 * for more information.
|
Chris@0
|
1259 * - Routing: Providing or altering "routes", which are URLs that Drupal
|
Chris@0
|
1260 * responds to, or altering routing behavior with event listener classes.
|
Chris@0
|
1261 * See the @link menu Routing and menu topic @endlink for more information.
|
Chris@0
|
1262 * - Events: Modules can register as event subscribers; when an event is
|
Chris@0
|
1263 * dispatched, a method is called on each registered subscriber, allowing each
|
Chris@0
|
1264 * one to react. See the @link events Events topic @endlink for more
|
Chris@0
|
1265 * information.
|
Chris@0
|
1266 *
|
Chris@0
|
1267 * @section sec_sample *.info.yml files
|
Chris@0
|
1268 * Extensions must each be located in a directory whose name matches the short
|
Chris@0
|
1269 * name (or machine name) of the extension, and this directory must contain a
|
Chris@0
|
1270 * file named machine_name.info.yml (where machine_name is the machine name of
|
Chris@0
|
1271 * the extension). See \Drupal\Core\Extension\InfoParserInterface::parse() for
|
Chris@0
|
1272 * documentation of the format of .info.yml files.
|
Chris@0
|
1273 * @}
|
Chris@0
|
1274 */
|
Chris@0
|
1275
|
Chris@0
|
1276 /**
|
Chris@0
|
1277 * @defgroup plugin_api Plugin API
|
Chris@0
|
1278 * @{
|
Chris@0
|
1279 * Using the Plugin API
|
Chris@0
|
1280 *
|
Chris@0
|
1281 * @section sec_overview Overview and terminology
|
Chris@0
|
1282 *
|
Chris@0
|
1283 * The basic idea of plugins is to allow a particular module or subsystem of
|
Chris@0
|
1284 * Drupal to provide functionality in an extensible, object-oriented way. The
|
Chris@0
|
1285 * controlling module or subsystem defines the basic framework (interface) for
|
Chris@0
|
1286 * the functionality, and other modules can create plugins (implementing the
|
Chris@0
|
1287 * interface) with particular behaviors. The controlling module instantiates
|
Chris@0
|
1288 * existing plugins as needed, and calls methods to invoke their functionality.
|
Chris@0
|
1289 * Examples of functionality in Drupal Core that use plugins include: the block
|
Chris@0
|
1290 * system (block types are plugins), the entity/field system (entity types,
|
Chris@0
|
1291 * field types, field formatters, and field widgets are plugins), the image
|
Chris@0
|
1292 * manipulation system (image effects and image toolkits are plugins), and the
|
Chris@0
|
1293 * search system (search page types are plugins).
|
Chris@0
|
1294 *
|
Chris@0
|
1295 * Plugins are grouped into plugin types, each generally defined by an
|
Chris@0
|
1296 * interface. Each plugin type is managed by a plugin manager service, which
|
Chris@0
|
1297 * uses a plugin discovery method to discover provided plugins of that type and
|
Chris@0
|
1298 * instantiate them using a plugin factory.
|
Chris@0
|
1299 *
|
Chris@0
|
1300 * Some plugin types make use of the following concepts or components:
|
Chris@0
|
1301 * - Plugin derivatives: Allows a single plugin class to present itself as
|
Chris@0
|
1302 * multiple plugins. Example: the Menu module provides a block for each
|
Chris@0
|
1303 * defined menu via a block plugin derivative.
|
Chris@0
|
1304 * - Plugin mapping: Allows a plugin class to map a configuration string to an
|
Chris@0
|
1305 * instance, and have the plugin automatically instantiated without writing
|
Chris@0
|
1306 * additional code.
|
Chris@0
|
1307 * - Plugin collections: Provide a way to lazily instantiate a set of plugin
|
Chris@0
|
1308 * instances from a single plugin definition.
|
Chris@0
|
1309 *
|
Chris@0
|
1310 * There are several things a module developer may need to do with plugins:
|
Chris@0
|
1311 * - Define a completely new plugin type: see @ref sec_define below.
|
Chris@0
|
1312 * - Create a plugin of an existing plugin type: see @ref sec_create below.
|
Chris@0
|
1313 * - Perform tasks that involve plugins: see @ref sec_use below.
|
Chris@0
|
1314 *
|
Chris@0
|
1315 * See https://www.drupal.org/developing/api/8/plugins for more detailed
|
Chris@0
|
1316 * documentation on the plugin system. There are also topics for a few
|
Chris@0
|
1317 * of the many existing types of plugins:
|
Chris@0
|
1318 * - @link block_api Block API @endlink
|
Chris@0
|
1319 * - @link entity_api Entity API @endlink
|
Chris@0
|
1320 * - @link field Various types of field-related plugins @endlink
|
Chris@0
|
1321 * - @link views_plugins Views plugins @endlink (has links to topics covering
|
Chris@0
|
1322 * various specific types of Views plugins).
|
Chris@0
|
1323 * - @link search Search page plugins @endlink
|
Chris@0
|
1324 *
|
Chris@0
|
1325 * @section sec_define Defining a new plugin type
|
Chris@0
|
1326 * To define a new plugin type:
|
Chris@0
|
1327 * - Define an interface for the plugin. This describes the common set of
|
Chris@0
|
1328 * behavior, and the methods you will call on each plugin class that is
|
Chris@0
|
1329 * instantiated. Usually this interface will extend one or more of the
|
Chris@0
|
1330 * following interfaces:
|
Chris@0
|
1331 * - \Drupal\Component\Plugin\PluginInspectionInterface
|
Chris@18
|
1332 * - \Drupal\Component\Plugin\ConfigurableInterface
|
Chris@18
|
1333 * - \Drupal\Component\Plugin\DependentPluginInterface
|
Chris@0
|
1334 * - \Drupal\Component\Plugin\ContextAwarePluginInterface
|
Chris@0
|
1335 * - \Drupal\Core\Plugin\PluginFormInterface
|
Chris@0
|
1336 * - \Drupal\Core\Executable\ExecutableInterface
|
Chris@0
|
1337 * - (optional) Create a base class that provides a partial implementation of
|
Chris@0
|
1338 * the interface, for the convenience of developers wishing to create plugins
|
Chris@0
|
1339 * of your type. The base class usually extends
|
Chris@0
|
1340 * \Drupal\Core\Plugin\PluginBase, or one of the base classes that extends
|
Chris@0
|
1341 * this class.
|
Chris@0
|
1342 * - Choose a method for plugin discovery, and define classes as necessary.
|
Chris@0
|
1343 * See @ref sub_discovery below.
|
Chris@0
|
1344 * - Create a plugin manager/factory class and service, which will discover and
|
Chris@0
|
1345 * instantiate plugins. See @ref sub_manager below.
|
Chris@0
|
1346 * - Use the plugin manager to instantiate plugins. Call methods on your plugin
|
Chris@0
|
1347 * interface to perform the tasks of your plugin type.
|
Chris@0
|
1348 * - (optional) If appropriate, define a plugin collection. See @ref
|
Chris@0
|
1349 * sub_collection below for more information.
|
Chris@0
|
1350 *
|
Chris@0
|
1351 * @subsection sub_discovery Plugin discovery
|
Chris@0
|
1352 * Plugin discovery is the process your plugin manager uses to discover the
|
Chris@0
|
1353 * individual plugins of your type that have been defined by your module and
|
Chris@0
|
1354 * other modules. Plugin discovery methods are classes that implement
|
Chris@0
|
1355 * \Drupal\Component\Plugin\Discovery\DiscoveryInterface. Most plugin types use
|
Chris@0
|
1356 * one of the following discovery mechanisms:
|
Chris@0
|
1357 * - Annotation: Plugin classes are annotated and placed in a defined namespace
|
Chris@0
|
1358 * subdirectory. Most Drupal Core plugins use this method of discovery.
|
Chris@0
|
1359 * - Hook: Plugin modules need to implement a hook to tell the manager about
|
Chris@0
|
1360 * their plugins.
|
Chris@0
|
1361 * - YAML: Plugins are listed in YAML files. Drupal Core uses this method for
|
Chris@0
|
1362 * discovering local tasks and local actions. This is mainly useful if all
|
Chris@0
|
1363 * plugins use the same class, so it is kind of like a global derivative.
|
Chris@0
|
1364 * - Static: Plugin classes are registered within the plugin manager class
|
Chris@0
|
1365 * itself. Static discovery is only useful if modules cannot define new
|
Chris@0
|
1366 * plugins of this type (if the list of available plugins is static).
|
Chris@0
|
1367 *
|
Chris@0
|
1368 * It is also possible to define your own custom discovery mechanism or mix
|
Chris@0
|
1369 * methods together. And there are many more details, such as annotation
|
Chris@0
|
1370 * decorators, that apply to some of the discovery methods. See
|
Chris@0
|
1371 * https://www.drupal.org/developing/api/8/plugins for more details.
|
Chris@0
|
1372 *
|
Chris@0
|
1373 * The remainder of this documentation will assume Annotation-based discovery,
|
Chris@0
|
1374 * since this is the most common method.
|
Chris@0
|
1375 *
|
Chris@0
|
1376 * @subsection sub_manager Defining a plugin manager class and service
|
Chris@0
|
1377 * To define an annotation-based plugin manager:
|
Chris@0
|
1378 * - Choose a namespace subdirectory for your plugin. For example, search page
|
Chris@0
|
1379 * plugins go in directory Plugin/Search under the module namespace.
|
Chris@0
|
1380 * - Define an annotation class for your plugin type. This class should extend
|
Chris@0
|
1381 * \Drupal\Component\Annotation\Plugin, and for most plugin types, it should
|
Chris@0
|
1382 * contain member variables corresponding to the annotations plugins will
|
Chris@0
|
1383 * need to provide. All plugins have at least $id: a unique string
|
Chris@0
|
1384 * identifier.
|
Chris@0
|
1385 * - Define an alter hook for altering the discovered plugin definitions. You
|
Chris@0
|
1386 * should document the hook in a *.api.php file.
|
Chris@0
|
1387 * - Define a plugin manager class. This class should implement
|
Chris@0
|
1388 * \Drupal\Component\Plugin\PluginManagerInterface; most plugin managers do
|
Chris@0
|
1389 * this by extending \Drupal\Core\Plugin\DefaultPluginManager. If you do
|
Chris@0
|
1390 * extend the default plugin manager, the only method you will probably need
|
Chris@0
|
1391 * to define is the class constructor, which will need to call the parent
|
Chris@0
|
1392 * constructor to provide information about the annotation class and plugin
|
Chris@0
|
1393 * namespace for discovery, set up the alter hook, and possibly set up
|
Chris@0
|
1394 * caching. See classes that extend DefaultPluginManager for examples.
|
Chris@0
|
1395 * - Define a service for your plugin manager. See the
|
Chris@0
|
1396 * @link container Services topic for more information. @endlink Your service
|
Chris@0
|
1397 * definition should look something like this, referencing your manager
|
Chris@0
|
1398 * class and the parent (default) plugin manager service to inherit
|
Chris@0
|
1399 * constructor arguments:
|
Chris@0
|
1400 * @code
|
Chris@0
|
1401 * plugin.manager.mymodule:
|
Chris@0
|
1402 * class: Drupal\mymodule\MyPluginManager
|
Chris@0
|
1403 * parent: default_plugin_manager
|
Chris@0
|
1404 * @endcode
|
Chris@0
|
1405 * - If your plugin is configurable, you will also need to define the
|
Chris@0
|
1406 * configuration schema and possibly a configuration entity type. See the
|
Chris@0
|
1407 * @link config_api Configuration API topic @endlink for more information.
|
Chris@0
|
1408 *
|
Chris@0
|
1409 * @subsection sub_collection Defining a plugin collection
|
Chris@0
|
1410 * Some configurable plugin types allow administrators to create zero or more
|
Chris@0
|
1411 * instances of each plugin, each with its own configuration. For example,
|
Chris@0
|
1412 * a single block plugin can be configured several times, to display in
|
Chris@0
|
1413 * different regions of a theme, with different visibility settings, a
|
Chris@0
|
1414 * different title, or other plugin-specific settings. To make this possible,
|
Chris@0
|
1415 * a plugin type can make use of what's known as a plugin collection.
|
Chris@0
|
1416 *
|
Chris@0
|
1417 * A plugin collection is a class that extends
|
Chris@0
|
1418 * \Drupal\Component\Plugin\LazyPluginCollection or one of its subclasses; there
|
Chris@0
|
1419 * are several examples in Drupal Core. If your plugin type uses a plugin
|
Chris@0
|
1420 * collection, it will usually also have a configuration entity, and the entity
|
Chris@0
|
1421 * class should implement
|
Chris@0
|
1422 * \Drupal\Core\Entity\EntityWithPluginCollectionInterface. Again, there are
|
Chris@0
|
1423 * several examples in Drupal Core; see also the @link config_api Configuration
|
Chris@0
|
1424 * API topic @endlink for more information about configuration entities.
|
Chris@0
|
1425 *
|
Chris@0
|
1426 * @section sec_create Creating a plugin of an existing type
|
Chris@0
|
1427 * Assuming the plugin type uses annotation-based discovery, in order to create
|
Chris@0
|
1428 * a plugin of an existing type, you will be creating a class. This class must:
|
Chris@0
|
1429 * - Implement the plugin interface, so that it has the required methods
|
Chris@0
|
1430 * defined. Usually, you'll want to extend the plugin base class, if one has
|
Chris@0
|
1431 * been provided.
|
Chris@0
|
1432 * - Have the right annotation in its documentation header. See the
|
Chris@0
|
1433 * @link annotation Annotation topic @endlink for more information about
|
Chris@0
|
1434 * annotation.
|
Chris@0
|
1435 * - Be in the right plugin namespace, in order to be discovered.
|
Chris@0
|
1436 * Often, the easiest way to make sure this happens is to find an existing
|
Chris@0
|
1437 * example of a working plugin class of the desired type, and copy it into your
|
Chris@0
|
1438 * module as a starting point.
|
Chris@0
|
1439 *
|
Chris@0
|
1440 * You can also create a plugin derivative, which allows your plugin class
|
Chris@0
|
1441 * to present itself to the user interface as multiple plugins. To do this,
|
Chris@0
|
1442 * in addition to the plugin class, you'll need to create a separate plugin
|
Chris@0
|
1443 * derivative class implementing
|
Chris@0
|
1444 * \Drupal\Component\Plugin\Derivative\DerivativeInterface. The classes
|
Chris@0
|
1445 * \Drupal\system\Plugin\Block\SystemMenuBlock (plugin class) and
|
Chris@0
|
1446 * \Drupal\system\Plugin\Derivative\SystemMenuBlock (derivative class) are a
|
Chris@0
|
1447 * good example to look at.
|
Chris@0
|
1448 *
|
Chris@0
|
1449 * @section sec_use Performing tasks involving plugins
|
Chris@0
|
1450 * Here are the steps to follow to perform a task that involves plugins:
|
Chris@0
|
1451 * - Locate the machine name of the plugin manager service, and instantiate the
|
Chris@0
|
1452 * service. See the @link container Services topic @endlink for more
|
Chris@0
|
1453 * information on how to do this.
|
Chris@0
|
1454 * - On the plugin manager class, use methods like getDefinition(),
|
Chris@0
|
1455 * getDefinitions(), or other methods specific to particular plugin managers
|
Chris@0
|
1456 * to retrieve information about either specific plugins or the entire list of
|
Chris@0
|
1457 * defined plugins.
|
Chris@0
|
1458 * - Call the createInstance() method on the plugin manager to instantiate
|
Chris@0
|
1459 * individual plugin objects.
|
Chris@0
|
1460 * - Call methods on the plugin objects to perform the desired tasks.
|
Chris@0
|
1461 *
|
Chris@0
|
1462 * @see annotation
|
Chris@0
|
1463 * @}
|
Chris@0
|
1464 */
|
Chris@0
|
1465
|
Chris@0
|
1466 /**
|
Chris@0
|
1467 * @defgroup oo_conventions Objected-oriented programming conventions
|
Chris@0
|
1468 * @{
|
Chris@0
|
1469 * PSR-4, namespaces, class naming, and other conventions.
|
Chris@0
|
1470 *
|
Chris@0
|
1471 * A lot of the PHP code in Drupal is object oriented (OO), making use of
|
Chris@0
|
1472 * @link http://php.net/manual/language.oop5.php PHP classes, interfaces, and traits @endlink
|
Chris@0
|
1473 * (which are loosely referred to as "classes" in the rest of this topic). The
|
Chris@0
|
1474 * following conventions and standards apply to this version of Drupal:
|
Chris@0
|
1475 * - Each class must be in its own file.
|
Chris@0
|
1476 * - Classes must be namespaced. If a module defines a class, the namespace
|
Chris@0
|
1477 * must start with \Drupal\module_name. If it is defined by Drupal Core for
|
Chris@0
|
1478 * use across many modules, the namespace should be \Drupal\Core or
|
Chris@0
|
1479 * \Drupal\Component, with the exception of the global class \Drupal. See
|
Chris@0
|
1480 * https://www.drupal.org/node/1353118 for more about namespaces.
|
Chris@0
|
1481 * - In order for the PSR-4-based class auto-loader to find the class, it must
|
Chris@0
|
1482 * be located in a directory corresponding to the namespace. For
|
Chris@0
|
1483 * module-defined classes, if the namespace is \Drupal\module_name\foo\bar,
|
Chris@0
|
1484 * then the class goes under the main module directory in directory
|
Chris@0
|
1485 * src/foo/bar. For Drupal-wide classes, if the namespace is
|
Chris@0
|
1486 * \Drupal\Core\foo\bar, then it goes in directory
|
Chris@0
|
1487 * core/lib/Drupal/Core/foo/bar. See https://www.drupal.org/node/2156625 for
|
Chris@0
|
1488 * more information about PSR-4.
|
Chris@0
|
1489 * - Some classes have annotations added to their documentation headers. See
|
Chris@0
|
1490 * the @link annotation Annotation topic @endlink for more information.
|
Chris@0
|
1491 * - Standard plugin discovery requires particular namespaces and annotation
|
Chris@0
|
1492 * for most plugin classes. See the
|
Chris@0
|
1493 * @link plugin_api Plugin API topic @endlink for more information.
|
Chris@0
|
1494 * - There are project-wide coding standards for OO code, including naming:
|
Chris@0
|
1495 * https://www.drupal.org/node/608152
|
Chris@0
|
1496 * - Documentation standards for classes are covered on:
|
Chris@0
|
1497 * https://www.drupal.org/coding-standards/docs#classes
|
Chris@0
|
1498 * @}
|
Chris@0
|
1499 */
|
Chris@0
|
1500
|
Chris@0
|
1501 /**
|
Chris@0
|
1502 * @defgroup listing_page_class Page header for Classes page
|
Chris@0
|
1503 * @{
|
Chris@0
|
1504 * Introduction to classes
|
Chris@0
|
1505 *
|
Chris@0
|
1506 * A lot of the PHP code in Drupal is object oriented (OO), making use of
|
Chris@0
|
1507 * @link http://php.net/manual/language.oop5.php PHP classes, interfaces, and traits. @endlink
|
Chris@0
|
1508 * See the
|
Chris@0
|
1509 * @link oo_conventions Objected-oriented programming conventions @endlink
|
Chris@0
|
1510 * for more information.
|
Chris@0
|
1511 *
|
Chris@0
|
1512 * @see oo_conventions
|
Chris@0
|
1513 *
|
Chris@0
|
1514 * @}
|
Chris@0
|
1515 */
|
Chris@0
|
1516
|
Chris@0
|
1517 /**
|
Chris@0
|
1518 * @defgroup listing_page_namespace Page header for Namespaces page
|
Chris@0
|
1519 * @{
|
Chris@0
|
1520 * Introduction to namespaces
|
Chris@0
|
1521 *
|
Chris@0
|
1522 * PHP classes, interfaces, and traits in Drupal are
|
Chris@14
|
1523 * @link http://php.net/manual/language.namespaces.rationale.php namespaced. @endlink
|
Chris@0
|
1524 * See the
|
Chris@0
|
1525 * @link oo_conventions Objected-oriented programming conventions @endlink
|
Chris@0
|
1526 * for more information.
|
Chris@0
|
1527 *
|
Chris@0
|
1528 * @see oo_conventions
|
Chris@0
|
1529 *
|
Chris@0
|
1530 * @}
|
Chris@0
|
1531 */
|
Chris@0
|
1532
|
Chris@0
|
1533 /**
|
Chris@0
|
1534 * @defgroup best_practices Best practices for developers
|
Chris@0
|
1535 * @{
|
Chris@0
|
1536 * Overview of standards and best practices for developers
|
Chris@0
|
1537 *
|
Chris@0
|
1538 * Ideally, all code that is included in Drupal Core and contributed modules,
|
Chris@0
|
1539 * themes, and distributions will be secure, internationalized, maintainable,
|
Chris@0
|
1540 * and efficient. In order to facilitate this, the Drupal community has
|
Chris@0
|
1541 * developed a set of guidelines and standards for developers to follow. Most of
|
Chris@0
|
1542 * these standards can be found under
|
Chris@0
|
1543 * @link https://www.drupal.org/developing/best-practices Best practices on Drupal.org @endlink
|
Chris@0
|
1544 *
|
Chris@0
|
1545 * Standards and best practices that developers should be aware of include:
|
Chris@0
|
1546 * - Security: https://www.drupal.org/writing-secure-code and the
|
Chris@0
|
1547 * @link sanitization Sanitization functions topic @endlink
|
Chris@0
|
1548 * - Coding standards: https://www.drupal.org/coding-standards
|
Chris@0
|
1549 * and https://www.drupal.org/coding-standards/docs
|
Chris@0
|
1550 * - Accessibility: https://www.drupal.org/node/1637990 (modules) and
|
Chris@0
|
1551 * https://www.drupal.org/node/464472 (themes)
|
Chris@0
|
1552 * - Usability: https://www.drupal.org/ui-standards
|
Chris@0
|
1553 * - Internationalization: @link i18n Internationalization topic @endlink
|
Chris@0
|
1554 * - Automated testing: @link testing Automated tests topic @endlink
|
Chris@0
|
1555 * @}
|
Chris@0
|
1556 */
|
Chris@0
|
1557
|
Chris@0
|
1558 /**
|
Chris@0
|
1559 * @defgroup utility Utility classes and functions
|
Chris@0
|
1560 * @{
|
Chris@0
|
1561 * Overview of utility classes and functions for developers.
|
Chris@0
|
1562 *
|
Chris@0
|
1563 * Drupal provides developers with a variety of utility functions that make it
|
Chris@0
|
1564 * easier and more efficient to perform tasks that are either really common,
|
Chris@0
|
1565 * tedious, or difficult. Utility functions help to reduce code duplication and
|
Chris@0
|
1566 * should be used in place of one-off code whenever possible.
|
Chris@0
|
1567 *
|
Chris@0
|
1568 * @see common.inc
|
Chris@0
|
1569 * @see file
|
Chris@0
|
1570 * @see format
|
Chris@0
|
1571 * @see php_wrappers
|
Chris@0
|
1572 * @see sanitization
|
Chris@0
|
1573 * @see transliteration
|
Chris@0
|
1574 * @see validation
|
Chris@0
|
1575 * @}
|
Chris@0
|
1576 */
|
Chris@0
|
1577
|
Chris@0
|
1578 /**
|
Chris@0
|
1579 * @defgroup hooks Hooks
|
Chris@0
|
1580 * @{
|
Chris@0
|
1581 * Define functions that alter the behavior of Drupal core.
|
Chris@0
|
1582 *
|
Chris@0
|
1583 * One way for modules to alter the core behavior of Drupal (or another module)
|
Chris@0
|
1584 * is to use hooks. Hooks are specially-named functions that a module defines
|
Chris@0
|
1585 * (this is known as "implementing the hook"), which are discovered and called
|
Chris@0
|
1586 * at specific times to alter or add to the base behavior or data (this is
|
Chris@0
|
1587 * known as "invoking the hook"). Each hook has a name (example:
|
Chris@0
|
1588 * hook_batch_alter()), a defined set of parameters, and a defined return value.
|
Chris@0
|
1589 * Your modules can implement hooks that are defined by Drupal core or other
|
Chris@0
|
1590 * modules that they interact with. Your modules can also define their own
|
Chris@0
|
1591 * hooks, in order to let other modules interact with them.
|
Chris@0
|
1592 *
|
Chris@0
|
1593 * To implement a hook:
|
Chris@0
|
1594 * - Locate the documentation for the hook. Hooks are documented in *.api.php
|
Chris@0
|
1595 * files, by defining functions whose name starts with "hook_" (these
|
Chris@0
|
1596 * files and their functions are never loaded by Drupal -- they exist solely
|
Chris@0
|
1597 * for documentation). The function should have a documentation header, as
|
Chris@0
|
1598 * well as a sample function body. For example, in the core file
|
Chris@0
|
1599 * system.api.php, you can find hooks such as hook_batch_alter(). Also, if
|
Chris@0
|
1600 * you are viewing this documentation on an API reference site, the Core
|
Chris@0
|
1601 * hooks will be listed in this topic.
|
Chris@0
|
1602 * - Copy the function to your module's .module file.
|
Chris@0
|
1603 * - Change the name of the function, substituting your module's short name
|
Chris@0
|
1604 * (name of the module's directory, and .info.yml file without the extension)
|
Chris@0
|
1605 * for the "hook" part of the sample function name. For instance, to implement
|
Chris@0
|
1606 * hook_batch_alter(), you would rename it to my_module_batch_alter().
|
Chris@0
|
1607 * - Edit the documentation for the function (normally, your implementation
|
Chris@0
|
1608 * should just have one line saying "Implements hook_batch_alter().").
|
Chris@0
|
1609 * - Edit the body of the function, substituting in what you need your module
|
Chris@0
|
1610 * to do.
|
Chris@0
|
1611 *
|
Chris@0
|
1612 * To define a hook:
|
Chris@0
|
1613 * - Choose a unique name for your hook. It should start with "hook_", followed
|
Chris@0
|
1614 * by your module's short name.
|
Chris@0
|
1615 * - Provide documentation in a *.api.php file in your module's main
|
Chris@0
|
1616 * directory. See the "implementing" section above for details of what this
|
Chris@0
|
1617 * should contain (parameters, return value, and sample function body).
|
Chris@0
|
1618 * - Invoke the hook in your module's code.
|
Chris@0
|
1619 *
|
Chris@0
|
1620 * To invoke a hook, use methods on
|
Chris@0
|
1621 * \Drupal\Core\Extension\ModuleHandlerInterface such as alter(), invoke(),
|
Chris@0
|
1622 * and invokeAll(). You can obtain a module handler by calling
|
Chris@0
|
1623 * \Drupal::moduleHandler(), or getting the 'module_handler' service on an
|
Chris@0
|
1624 * injected container.
|
Chris@0
|
1625 *
|
Chris@0
|
1626 * @see extending
|
Chris@0
|
1627 * @see themeable
|
Chris@0
|
1628 * @see callbacks
|
Chris@0
|
1629 * @see \Drupal\Core\Extension\ModuleHandlerInterface
|
Chris@0
|
1630 * @see \Drupal::moduleHandler()
|
Chris@0
|
1631 *
|
Chris@0
|
1632 * @}
|
Chris@0
|
1633 */
|
Chris@0
|
1634
|
Chris@0
|
1635 /**
|
Chris@0
|
1636 * @defgroup callbacks Callbacks
|
Chris@0
|
1637 * @{
|
Chris@0
|
1638 * Callback function signatures.
|
Chris@0
|
1639 *
|
Chris@0
|
1640 * Drupal's API sometimes uses callback functions to allow you to define how
|
Chris@0
|
1641 * some type of processing happens. A callback is a function with a defined
|
Chris@0
|
1642 * signature, which you define in a module. Then you pass the function name as
|
Chris@0
|
1643 * a parameter to a Drupal API function or return it as part of a hook
|
Chris@0
|
1644 * implementation return value, and your function is called at an appropriate
|
Chris@0
|
1645 * time. For instance, when setting up batch processing you might need to
|
Chris@0
|
1646 * provide a callback function for each processing step and/or a callback for
|
Chris@0
|
1647 * when processing is finished; you would do that by defining these functions
|
Chris@0
|
1648 * and passing their names into the batch setup function.
|
Chris@0
|
1649 *
|
Chris@0
|
1650 * Callback function signatures, like hook definitions, are described by
|
Chris@0
|
1651 * creating and documenting dummy functions in a *.api.php file; normally, the
|
Chris@0
|
1652 * dummy callback function's name should start with "callback_", and you should
|
Chris@0
|
1653 * document the parameters and return value and provide a sample function body.
|
Chris@0
|
1654 * Then your API documentation can refer to this callback function in its
|
Chris@0
|
1655 * documentation. A user of your API can usually name their callback function
|
Chris@0
|
1656 * anything they want, although a standard name would be to replace "callback_"
|
Chris@0
|
1657 * with the module name.
|
Chris@0
|
1658 *
|
Chris@0
|
1659 * @see hooks
|
Chris@0
|
1660 * @see themeable
|
Chris@0
|
1661 *
|
Chris@0
|
1662 * @}
|
Chris@0
|
1663 */
|
Chris@0
|
1664
|
Chris@0
|
1665 /**
|
Chris@0
|
1666 * @defgroup form_api Form generation
|
Chris@0
|
1667 * @{
|
Chris@0
|
1668 * Describes how to generate and manipulate forms and process form submissions.
|
Chris@0
|
1669 *
|
Chris@0
|
1670 * Drupal provides a Form API in order to achieve consistency in its form
|
Chris@0
|
1671 * processing and presentation, while simplifying code and reducing the amount
|
Chris@0
|
1672 * of HTML that must be explicitly generated by a module.
|
Chris@0
|
1673 *
|
Chris@0
|
1674 * @section generating_forms Creating forms
|
Chris@0
|
1675 * Forms are defined as classes that implement the
|
Chris@0
|
1676 * \Drupal\Core\Form\FormInterface and are built using the
|
Chris@0
|
1677 * \Drupal\Core\Form\FormBuilder class. Drupal provides a couple of utility
|
Chris@0
|
1678 * classes that can be extended as a starting point for most basic forms, the
|
Chris@0
|
1679 * most commonly used of which is \Drupal\Core\Form\FormBase. FormBuilder
|
Chris@0
|
1680 * handles the low level processing of forms such as rendering the necessary
|
Chris@0
|
1681 * HTML, initial processing of incoming $_POST data, and delegating to your
|
Chris@0
|
1682 * implementation of FormInterface for validation and processing of submitted
|
Chris@0
|
1683 * data.
|
Chris@0
|
1684 *
|
Chris@0
|
1685 * Here is an example of a Form class:
|
Chris@0
|
1686 * @code
|
Chris@0
|
1687 * namespace Drupal\mymodule\Form;
|
Chris@0
|
1688 *
|
Chris@0
|
1689 * use Drupal\Core\Form\FormBase;
|
Chris@0
|
1690 * use Drupal\Core\Form\FormStateInterface;
|
Chris@0
|
1691 *
|
Chris@0
|
1692 * class ExampleForm extends FormBase {
|
Chris@0
|
1693 * public function getFormId() {
|
Chris@0
|
1694 * // Unique ID of the form.
|
Chris@0
|
1695 * return 'example_form';
|
Chris@0
|
1696 * }
|
Chris@0
|
1697 *
|
Chris@0
|
1698 * public function buildForm(array $form, FormStateInterface $form_state) {
|
Chris@0
|
1699 * // Create a $form API array.
|
Chris@0
|
1700 * $form['phone_number'] = array(
|
Chris@0
|
1701 * '#type' => 'tel',
|
Chris@0
|
1702 * '#title' => $this->t('Your phone number'),
|
Chris@0
|
1703 * );
|
Chris@0
|
1704 * $form['save'] = array(
|
Chris@0
|
1705 * '#type' => 'submit',
|
Chris@0
|
1706 * '#value' => $this->t('Save'),
|
Chris@0
|
1707 * );
|
Chris@0
|
1708 * return $form;
|
Chris@0
|
1709 * }
|
Chris@0
|
1710 *
|
Chris@0
|
1711 * public function validateForm(array &$form, FormStateInterface $form_state) {
|
Chris@0
|
1712 * // Validate submitted form data.
|
Chris@0
|
1713 * }
|
Chris@0
|
1714 *
|
Chris@0
|
1715 * public function submitForm(array &$form, FormStateInterface $form_state) {
|
Chris@0
|
1716 * // Handle submitted form data.
|
Chris@0
|
1717 * }
|
Chris@0
|
1718 * }
|
Chris@0
|
1719 * @endcode
|
Chris@0
|
1720 *
|
Chris@0
|
1721 * @section retrieving_forms Retrieving and displaying forms
|
Chris@0
|
1722 * \Drupal::formBuilder()->getForm() should be used to handle retrieving,
|
Chris@0
|
1723 * processing, and displaying a rendered HTML form. Given the ExampleForm
|
Chris@0
|
1724 * defined above,
|
Chris@0
|
1725 * \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm') would
|
Chris@0
|
1726 * return the rendered HTML of the form defined by ExampleForm::buildForm(), or
|
Chris@0
|
1727 * call the validateForm() and submitForm(), methods depending on the current
|
Chris@0
|
1728 * processing state.
|
Chris@0
|
1729 *
|
Chris@0
|
1730 * The argument to \Drupal::formBuilder()->getForm() is the name of a class that
|
Chris@0
|
1731 * implements FormInterface. Any additional arguments passed to the getForm()
|
Chris@0
|
1732 * method will be passed along as additional arguments to the
|
Chris@0
|
1733 * ExampleForm::buildForm() method.
|
Chris@0
|
1734 *
|
Chris@0
|
1735 * For example:
|
Chris@0
|
1736 * @code
|
Chris@0
|
1737 * $extra = '612-123-4567';
|
Chris@0
|
1738 * $form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm', $extra);
|
Chris@0
|
1739 * ...
|
Chris@0
|
1740 * public function buildForm(array $form, FormStateInterface $form_state, $extra = NULL)
|
Chris@0
|
1741 * $form['phone_number'] = array(
|
Chris@0
|
1742 * '#type' => 'tel',
|
Chris@0
|
1743 * '#title' => $this->t('Your phone number'),
|
Chris@0
|
1744 * '#value' => $extra,
|
Chris@0
|
1745 * );
|
Chris@0
|
1746 * return $form;
|
Chris@0
|
1747 * }
|
Chris@0
|
1748 * @endcode
|
Chris@0
|
1749 *
|
Chris@0
|
1750 * Alternatively, forms can be built directly via the routing system which will
|
Chris@0
|
1751 * take care of calling \Drupal::formBuilder()->getForm(). The following example
|
Chris@0
|
1752 * demonstrates the use of a routing.yml file to display a form at the given
|
Chris@0
|
1753 * route.
|
Chris@0
|
1754 *
|
Chris@0
|
1755 * @code
|
Chris@0
|
1756 * example.form:
|
Chris@0
|
1757 * path: '/example-form'
|
Chris@0
|
1758 * defaults:
|
Chris@0
|
1759 * _title: 'Example form'
|
Chris@0
|
1760 * _form: '\Drupal\mymodule\Form\ExampleForm'
|
Chris@0
|
1761 * @endcode
|
Chris@0
|
1762 *
|
Chris@0
|
1763 * The $form argument to form-related functions is a specialized render array
|
Chris@0
|
1764 * containing the elements and properties of the form. For more about render
|
Chris@0
|
1765 * arrays, see the @link theme_render Render API topic. @endlink For more
|
Chris@0
|
1766 * detailed explanations of the Form API workflow, see the
|
Chris@0
|
1767 * @link https://www.drupal.org/node/2117411 Form API documentation section. @endlink
|
Chris@0
|
1768 * In addition, there is a set of Form API tutorials in the
|
Chris@0
|
1769 * @link https://www.drupal.org/project/examples Examples for Developers project. @endlink
|
Chris@0
|
1770 *
|
Chris@0
|
1771 * In the form builder, validation, submission, and other form methods,
|
Chris@0
|
1772 * $form_state is the primary influence on the processing of the form and is
|
Chris@0
|
1773 * passed to most methods, so they can use it to communicate with the form
|
Chris@0
|
1774 * system and each other. $form_state is an object that implements
|
Chris@0
|
1775 * \Drupal\Core\Form\FormStateInterface.
|
Chris@0
|
1776 * @}
|
Chris@0
|
1777 */
|
Chris@0
|
1778
|
Chris@0
|
1779 /**
|
Chris@0
|
1780 * @defgroup queue Queue operations
|
Chris@0
|
1781 * @{
|
Chris@0
|
1782 * Queue items to allow later processing.
|
Chris@0
|
1783 *
|
Chris@0
|
1784 * The queue system allows placing items in a queue and processing them later.
|
Chris@0
|
1785 * The system tries to ensure that only one consumer can process an item.
|
Chris@0
|
1786 *
|
Chris@0
|
1787 * Before a queue can be used it needs to be created by
|
Chris@0
|
1788 * Drupal\Core\Queue\QueueInterface::createQueue().
|
Chris@0
|
1789 *
|
Chris@0
|
1790 * Items can be added to the queue by passing an arbitrary data object to
|
Chris@0
|
1791 * Drupal\Core\Queue\QueueInterface::createItem().
|
Chris@0
|
1792 *
|
Chris@0
|
1793 * To process an item, call Drupal\Core\Queue\QueueInterface::claimItem() and
|
Chris@0
|
1794 * specify how long you want to have a lease for working on that item.
|
Chris@0
|
1795 * When finished processing, the item needs to be deleted by calling
|
Chris@0
|
1796 * Drupal\Core\Queue\QueueInterface::deleteItem(). If the consumer dies, the
|
Chris@0
|
1797 * item will be made available again by the Drupal\Core\Queue\QueueInterface
|
Chris@0
|
1798 * implementation once the lease expires. Another consumer will then be able to
|
Chris@0
|
1799 * receive it when calling Drupal\Core\Queue\QueueInterface::claimItem().
|
Chris@0
|
1800 * Due to this, the processing code should be aware that an item might be handed
|
Chris@0
|
1801 * over for processing more than once.
|
Chris@0
|
1802 *
|
Chris@0
|
1803 * The $item object used by the Drupal\Core\Queue\QueueInterface can contain
|
Chris@0
|
1804 * arbitrary metadata depending on the implementation. Systems using the
|
Chris@0
|
1805 * interface should only rely on the data property which will contain the
|
Chris@0
|
1806 * information passed to Drupal\Core\Queue\QueueInterface::createItem().
|
Chris@0
|
1807 * The full queue item returned by Drupal\Core\Queue\QueueInterface::claimItem()
|
Chris@0
|
1808 * needs to be passed to Drupal\Core\Queue\QueueInterface::deleteItem() once
|
Chris@0
|
1809 * processing is completed.
|
Chris@0
|
1810 *
|
Chris@0
|
1811 * There are two kinds of queue backends available: reliable, which preserves
|
Chris@0
|
1812 * the order of messages and guarantees that every item will be executed at
|
Chris@0
|
1813 * least once. The non-reliable kind only does a best effort to preserve order
|
Chris@0
|
1814 * in messages and to execute them at least once but there is a small chance
|
Chris@0
|
1815 * that some items get lost. For example, some distributed back-ends like
|
Chris@0
|
1816 * Amazon SQS will be managing jobs for a large set of producers and consumers
|
Chris@0
|
1817 * where a strict FIFO ordering will likely not be preserved. Another example
|
Chris@0
|
1818 * would be an in-memory queue backend which might lose items if it crashes.
|
Chris@0
|
1819 * However, such a backend would be able to deal with significantly more writes
|
Chris@0
|
1820 * than a reliable queue and for many tasks this is more important. See
|
Chris@0
|
1821 * aggregator_cron() for an example of how to effectively use a non-reliable
|
Chris@0
|
1822 * queue. Another example is doing Twitter statistics -- the small possibility
|
Chris@0
|
1823 * of losing a few items is insignificant next to power of the queue being able
|
Chris@0
|
1824 * to keep up with writes. As described in the processing section, regardless
|
Chris@0
|
1825 * of the queue being reliable or not, the processing code should be aware that
|
Chris@0
|
1826 * an item might be handed over for processing more than once (because the
|
Chris@0
|
1827 * processing code might time out before it finishes).
|
Chris@0
|
1828 * @}
|
Chris@0
|
1829 */
|
Chris@0
|
1830
|
Chris@0
|
1831 /**
|
Chris@0
|
1832 * @defgroup annotation Annotations
|
Chris@0
|
1833 * @{
|
Chris@0
|
1834 * Annotations for class discovery and metadata description.
|
Chris@0
|
1835 *
|
Chris@0
|
1836 * The Drupal plugin system has a set of reusable components that developers
|
Chris@0
|
1837 * can use, override, and extend in their modules. Most of the plugins use
|
Chris@0
|
1838 * annotations, which let classes register themselves as plugins and describe
|
Chris@0
|
1839 * their metadata. (Annotations can also be used for other purposes, though
|
Chris@0
|
1840 * at the moment, Drupal only uses them for the plugin system.)
|
Chris@0
|
1841 *
|
Chris@0
|
1842 * To annotate a class as a plugin, add code similar to the following to the
|
Chris@0
|
1843 * end of the documentation block immediately preceding the class declaration:
|
Chris@0
|
1844 * @code
|
Chris@0
|
1845 * * @ContentEntityType(
|
Chris@0
|
1846 * * id = "comment",
|
Chris@0
|
1847 * * label = @Translation("Comment"),
|
Chris@0
|
1848 * * ...
|
Chris@0
|
1849 * * base_table = "comment"
|
Chris@0
|
1850 * * )
|
Chris@0
|
1851 * @endcode
|
Chris@0
|
1852 *
|
Chris@0
|
1853 * Note that you must use double quotes; single quotes will not work in
|
Chris@0
|
1854 * annotations.
|
Chris@0
|
1855 *
|
Chris@0
|
1856 * Some annotation types, which extend the "@ PluginID" annotation class, have
|
Chris@0
|
1857 * only a single 'id' key in their annotation. For these, it is possible to use
|
Chris@0
|
1858 * a shorthand annotation. For example:
|
Chris@0
|
1859 * @code
|
Chris@0
|
1860 * * @ViewsArea("entity")
|
Chris@0
|
1861 * @endcode
|
Chris@0
|
1862 * in place of
|
Chris@0
|
1863 * @code
|
Chris@0
|
1864 * * @ViewsArea(
|
Chris@0
|
1865 * * id = "entity"
|
Chris@0
|
1866 * *)
|
Chris@0
|
1867 * @endcode
|
Chris@0
|
1868 *
|
Chris@0
|
1869 * The available annotation classes are listed in this topic, and can be
|
Chris@0
|
1870 * identified when you are looking at the Drupal source code by having
|
Chris@0
|
1871 * "@ Annotation" in their documentation blocks (without the space after @). To
|
Chris@0
|
1872 * find examples of annotation for a particular annotation class, such as
|
Chris@0
|
1873 * EntityType, look for class files that have an @ annotation section using the
|
Chris@0
|
1874 * annotation class.
|
Chris@0
|
1875 *
|
Chris@0
|
1876 * @see plugin_translatable
|
Chris@0
|
1877 * @see plugin_context
|
Chris@0
|
1878 *
|
Chris@0
|
1879 * @}
|
Chris@0
|
1880 */
|
Chris@0
|
1881
|
Chris@0
|
1882 /**
|
Chris@0
|
1883 * @addtogroup hooks
|
Chris@0
|
1884 * @{
|
Chris@0
|
1885 */
|
Chris@0
|
1886
|
Chris@0
|
1887 /**
|
Chris@0
|
1888 * Perform periodic actions.
|
Chris@0
|
1889 *
|
Chris@0
|
1890 * Modules that require some commands to be executed periodically can
|
Chris@0
|
1891 * implement hook_cron(). The engine will then call the hook whenever a cron
|
Chris@0
|
1892 * run happens, as defined by the administrator. Typical tasks managed by
|
Chris@0
|
1893 * hook_cron() are database maintenance, backups, recalculation of settings
|
Chris@0
|
1894 * or parameters, automated mailing, and retrieving remote data.
|
Chris@0
|
1895 *
|
Chris@0
|
1896 * Short-running or non-resource-intensive tasks can be executed directly in
|
Chris@0
|
1897 * the hook_cron() implementation.
|
Chris@0
|
1898 *
|
Chris@0
|
1899 * Long-running tasks and tasks that could time out, such as retrieving remote
|
Chris@0
|
1900 * data, sending email, and intensive file tasks, should use the queue API
|
Chris@0
|
1901 * instead of executing the tasks directly. To do this, first define one or
|
Chris@0
|
1902 * more queues via a \Drupal\Core\Annotation\QueueWorker plugin. Then, add items
|
Chris@0
|
1903 * that need to be processed to the defined queues.
|
Chris@0
|
1904 */
|
Chris@0
|
1905 function hook_cron() {
|
Chris@0
|
1906 // Short-running operation example, not using a queue:
|
Chris@0
|
1907 // Delete all expired records since the last cron run.
|
Chris@0
|
1908 $expires = \Drupal::state()->get('mymodule.last_check', 0);
|
Chris@0
|
1909 \Drupal::database()->delete('mymodule_table')
|
Chris@0
|
1910 ->condition('expires', $expires, '>=')
|
Chris@0
|
1911 ->execute();
|
Chris@0
|
1912 \Drupal::state()->set('mymodule.last_check', REQUEST_TIME);
|
Chris@0
|
1913
|
Chris@0
|
1914 // Long-running operation example, leveraging a queue:
|
Chris@0
|
1915 // Queue news feeds for updates once their refresh interval has elapsed.
|
Chris@0
|
1916 $queue = \Drupal::queue('aggregator_feeds');
|
Chris@0
|
1917 $ids = \Drupal::entityManager()->getStorage('aggregator_feed')->getFeedIdsToRefresh();
|
Chris@0
|
1918 foreach (Feed::loadMultiple($ids) as $feed) {
|
Chris@0
|
1919 if ($queue->createItem($feed)) {
|
Chris@0
|
1920 // Add timestamp to avoid queueing item more than once.
|
Chris@0
|
1921 $feed->setQueuedTime(REQUEST_TIME);
|
Chris@0
|
1922 $feed->save();
|
Chris@0
|
1923 }
|
Chris@0
|
1924 }
|
Chris@0
|
1925 $ids = \Drupal::entityQuery('aggregator_feed')
|
Chris@0
|
1926 ->condition('queued', REQUEST_TIME - (3600 * 6), '<')
|
Chris@0
|
1927 ->execute();
|
Chris@0
|
1928 if ($ids) {
|
Chris@0
|
1929 $feeds = Feed::loadMultiple($ids);
|
Chris@0
|
1930 foreach ($feeds as $feed) {
|
Chris@0
|
1931 $feed->setQueuedTime(0);
|
Chris@0
|
1932 $feed->save();
|
Chris@0
|
1933 }
|
Chris@0
|
1934 }
|
Chris@0
|
1935 }
|
Chris@0
|
1936
|
Chris@0
|
1937 /**
|
Chris@0
|
1938 * Alter available data types for typed data wrappers.
|
Chris@0
|
1939 *
|
Chris@0
|
1940 * @param array $data_types
|
Chris@0
|
1941 * An array of data type information.
|
Chris@0
|
1942 *
|
Chris@0
|
1943 * @see hook_data_type_info()
|
Chris@0
|
1944 */
|
Chris@0
|
1945 function hook_data_type_info_alter(&$data_types) {
|
Chris@0
|
1946 $data_types['email']['class'] = '\Drupal\mymodule\Type\Email';
|
Chris@0
|
1947 }
|
Chris@0
|
1948
|
Chris@0
|
1949 /**
|
Chris@0
|
1950 * Alter cron queue information before cron runs.
|
Chris@0
|
1951 *
|
Chris@0
|
1952 * Called by \Drupal\Core\Cron to allow modules to alter cron queue settings
|
Chris@16
|
1953 * before any jobs are processed.
|
Chris@0
|
1954 *
|
Chris@0
|
1955 * @param array $queues
|
Chris@0
|
1956 * An array of cron queue information.
|
Chris@0
|
1957 *
|
Chris@16
|
1958 * @see \Drupal\Core\Queue\QueueWorkerInterface
|
Chris@0
|
1959 * @see \Drupal\Core\Annotation\QueueWorker
|
Chris@0
|
1960 * @see \Drupal\Core\Cron
|
Chris@0
|
1961 */
|
Chris@0
|
1962 function hook_queue_info_alter(&$queues) {
|
Chris@0
|
1963 // This site has many feeds so let's spend 90 seconds on each cron run
|
Chris@0
|
1964 // updating feeds instead of the default 60.
|
Chris@0
|
1965 $queues['aggregator_feeds']['cron']['time'] = 90;
|
Chris@0
|
1966 }
|
Chris@0
|
1967
|
Chris@0
|
1968 /**
|
Chris@0
|
1969 * Alter an email message created with MailManagerInterface->mail().
|
Chris@0
|
1970 *
|
Chris@17
|
1971 * Hook hook_mail_alter() allows modification of email messages created and sent
|
Chris@0
|
1972 * with MailManagerInterface->mail(). Usage examples include adding and/or
|
Chris@0
|
1973 * changing message text, message fields, and message headers.
|
Chris@0
|
1974 *
|
Chris@0
|
1975 * Email messages sent using functions other than MailManagerInterface->mail()
|
Chris@0
|
1976 * will not invoke hook_mail_alter(). For example, a contributed module directly
|
Chris@0
|
1977 * calling the MailInterface->mail() or PHP mail() function will not invoke
|
Chris@0
|
1978 * this hook. All core modules use MailManagerInterface->mail() for messaging,
|
Chris@0
|
1979 * it is best practice but not mandatory in contributed modules.
|
Chris@0
|
1980 *
|
Chris@0
|
1981 * @param $message
|
Chris@0
|
1982 * An array containing the message data. Keys in this array include:
|
Chris@0
|
1983 * - 'id':
|
Chris@0
|
1984 * The MailManagerInterface->mail() id of the message. Look at module source
|
Chris@0
|
1985 * code or MailManagerInterface->mail() for possible id values.
|
Chris@0
|
1986 * - 'to':
|
Chris@0
|
1987 * The address or addresses the message will be sent to. The
|
Chris@0
|
1988 * formatting of this string must comply with RFC 2822.
|
Chris@0
|
1989 * - 'from':
|
Chris@0
|
1990 * The address the message will be marked as being from, which is
|
Chris@0
|
1991 * either a custom address or the site-wide default email address.
|
Chris@0
|
1992 * - 'subject':
|
Chris@0
|
1993 * Subject of the email to be sent. This must not contain any newline
|
Chris@0
|
1994 * characters, or the email may not be sent properly.
|
Chris@0
|
1995 * - 'body':
|
Chris@0
|
1996 * An array of strings or objects that implement
|
Chris@0
|
1997 * \Drupal\Component\Render\MarkupInterface containing the message text. The
|
Chris@0
|
1998 * message body is created by concatenating the individual array strings
|
Chris@0
|
1999 * into a single text string using "\n\n" as a separator.
|
Chris@0
|
2000 * - 'headers':
|
Chris@0
|
2001 * Associative array containing mail headers, such as From, Sender,
|
Chris@0
|
2002 * MIME-Version, Content-Type, etc.
|
Chris@0
|
2003 * - 'params':
|
Chris@0
|
2004 * An array of optional parameters supplied by the caller of
|
Chris@0
|
2005 * MailManagerInterface->mail() that is used to build the message before
|
Chris@0
|
2006 * hook_mail_alter() is invoked.
|
Chris@0
|
2007 * - 'language':
|
Chris@0
|
2008 * The language object used to build the message before hook_mail_alter()
|
Chris@0
|
2009 * is invoked.
|
Chris@0
|
2010 * - 'send':
|
Chris@0
|
2011 * Set to FALSE to abort sending this email message.
|
Chris@0
|
2012 *
|
Chris@0
|
2013 * @see \Drupal\Core\Mail\MailManagerInterface::mail()
|
Chris@0
|
2014 */
|
Chris@0
|
2015 function hook_mail_alter(&$message) {
|
Chris@0
|
2016 if ($message['id'] == 'modulename_messagekey') {
|
Chris@0
|
2017 if (!example_notifications_optin($message['to'], $message['id'])) {
|
Chris@0
|
2018 // If the recipient has opted to not receive such messages, cancel
|
Chris@0
|
2019 // sending.
|
Chris@0
|
2020 $message['send'] = FALSE;
|
Chris@0
|
2021 return;
|
Chris@0
|
2022 }
|
Chris@0
|
2023 $message['body'][] = "--\nMail sent out from " . \Drupal::config('system.site')->get('name');
|
Chris@0
|
2024 }
|
Chris@0
|
2025 }
|
Chris@0
|
2026
|
Chris@0
|
2027 /**
|
Chris@0
|
2028 * Prepares a message based on parameters;
|
Chris@0
|
2029 *
|
Chris@0
|
2030 * This hook is called from MailManagerInterface->mail(). Note that hook_mail(),
|
Chris@0
|
2031 * unlike hook_mail_alter(), is only called on the $module argument to
|
Chris@0
|
2032 * MailManagerInterface->mail(), not all modules.
|
Chris@0
|
2033 *
|
Chris@0
|
2034 * @param $key
|
Chris@0
|
2035 * An identifier of the mail.
|
Chris@0
|
2036 * @param $message
|
Chris@0
|
2037 * An array to be filled in. Elements in this array include:
|
Chris@0
|
2038 * - id: An ID to identify the mail sent. Look at module source code or
|
Chris@0
|
2039 * MailManagerInterface->mail() for possible id values.
|
Chris@0
|
2040 * - to: The address or addresses the message will be sent to. The
|
Chris@0
|
2041 * formatting of this string must comply with RFC 2822.
|
Chris@0
|
2042 * - subject: Subject of the email to be sent. This must not contain any
|
Chris@0
|
2043 * newline characters, or the mail may not be sent properly.
|
Chris@0
|
2044 * MailManagerInterface->mail() sets this to an empty
|
Chris@0
|
2045 * string when the hook is invoked.
|
Chris@0
|
2046 * - body: An array of lines containing the message to be sent. Drupal will
|
Chris@0
|
2047 * format the correct line endings for you. MailManagerInterface->mail()
|
Chris@0
|
2048 * sets this to an empty array when the hook is invoked. The array may
|
Chris@0
|
2049 * contain either strings or objects implementing
|
Chris@0
|
2050 * \Drupal\Component\Render\MarkupInterface.
|
Chris@0
|
2051 * - from: The address the message will be marked as being from, which is
|
Chris@0
|
2052 * set by MailManagerInterface->mail() to either a custom address or the
|
Chris@0
|
2053 * site-wide default email address when the hook is invoked.
|
Chris@0
|
2054 * - headers: Associative array containing mail headers, such as From,
|
Chris@0
|
2055 * Sender, MIME-Version, Content-Type, etc.
|
Chris@0
|
2056 * MailManagerInterface->mail() pre-fills several headers in this array.
|
Chris@0
|
2057 * @param $params
|
Chris@0
|
2058 * An array of parameters supplied by the caller of
|
Chris@0
|
2059 * MailManagerInterface->mail().
|
Chris@0
|
2060 *
|
Chris@0
|
2061 * @see \Drupal\Core\Mail\MailManagerInterface::mail()
|
Chris@0
|
2062 */
|
Chris@0
|
2063 function hook_mail($key, &$message, $params) {
|
Chris@0
|
2064 $account = $params['account'];
|
Chris@0
|
2065 $context = $params['context'];
|
Chris@0
|
2066 $variables = [
|
Chris@0
|
2067 '%site_name' => \Drupal::config('system.site')->get('name'),
|
Chris@0
|
2068 '%username' => $account->getDisplayName(),
|
Chris@0
|
2069 ];
|
Chris@0
|
2070 if ($context['hook'] == 'taxonomy') {
|
Chris@0
|
2071 $entity = $params['entity'];
|
Chris@0
|
2072 $vocabulary = Vocabulary::load($entity->id());
|
Chris@0
|
2073 $variables += [
|
Chris@0
|
2074 '%term_name' => $entity->name,
|
Chris@0
|
2075 '%term_description' => $entity->description,
|
Chris@0
|
2076 '%term_id' => $entity->id(),
|
Chris@0
|
2077 '%vocabulary_name' => $vocabulary->label(),
|
Chris@0
|
2078 '%vocabulary_description' => $vocabulary->getDescription(),
|
Chris@0
|
2079 '%vocabulary_id' => $vocabulary->id(),
|
Chris@0
|
2080 ];
|
Chris@0
|
2081 }
|
Chris@0
|
2082
|
Chris@0
|
2083 // Node-based variable translation is only available if we have a node.
|
Chris@0
|
2084 if (isset($params['node'])) {
|
Chris@0
|
2085 /** @var \Drupal\node\NodeInterface $node */
|
Chris@0
|
2086 $node = $params['node'];
|
Chris@0
|
2087 $variables += [
|
Chris@0
|
2088 '%uid' => $node->getOwnerId(),
|
Chris@18
|
2089 '%url' => $node->toUrl('canonical', ['absolute' => TRUE])->toString(),
|
Chris@0
|
2090 '%node_type' => node_get_type_label($node),
|
Chris@0
|
2091 '%title' => $node->getTitle(),
|
Chris@0
|
2092 '%teaser' => $node->teaser,
|
Chris@0
|
2093 '%body' => $node->body,
|
Chris@0
|
2094 ];
|
Chris@0
|
2095 }
|
Chris@0
|
2096 $subject = strtr($context['subject'], $variables);
|
Chris@0
|
2097 $body = strtr($context['message'], $variables);
|
Chris@0
|
2098 $message['subject'] .= str_replace(["\r", "\n"], '', $subject);
|
Chris@0
|
2099 $message['body'][] = MailFormatHelper::htmlToText($body);
|
Chris@0
|
2100 }
|
Chris@0
|
2101
|
Chris@0
|
2102 /**
|
Chris@0
|
2103 * Alter the list of mail backend plugin definitions.
|
Chris@0
|
2104 *
|
Chris@0
|
2105 * @param array $info
|
Chris@0
|
2106 * The mail backend plugin definitions to be altered.
|
Chris@0
|
2107 *
|
Chris@0
|
2108 * @see \Drupal\Core\Annotation\Mail
|
Chris@0
|
2109 * @see \Drupal\Core\Mail\MailManager
|
Chris@0
|
2110 */
|
Chris@0
|
2111 function hook_mail_backend_info_alter(&$info) {
|
Chris@0
|
2112 unset($info['test_mail_collector']);
|
Chris@0
|
2113 }
|
Chris@0
|
2114
|
Chris@0
|
2115 /**
|
Chris@0
|
2116 * Alter the default country list.
|
Chris@0
|
2117 *
|
Chris@0
|
2118 * @param $countries
|
Chris@0
|
2119 * The associative array of countries keyed by two-letter country code.
|
Chris@0
|
2120 *
|
Chris@0
|
2121 * @see \Drupal\Core\Locale\CountryManager::getList()
|
Chris@0
|
2122 */
|
Chris@0
|
2123 function hook_countries_alter(&$countries) {
|
Chris@0
|
2124 // Elbonia is now independent, so add it to the country list.
|
Chris@0
|
2125 $countries['EB'] = 'Elbonia';
|
Chris@0
|
2126 }
|
Chris@0
|
2127
|
Chris@0
|
2128 /**
|
Chris@0
|
2129 * Alter display variant plugin definitions.
|
Chris@0
|
2130 *
|
Chris@0
|
2131 * @param array $definitions
|
Chris@0
|
2132 * The array of display variant definitions, keyed by plugin ID.
|
Chris@0
|
2133 *
|
Chris@0
|
2134 * @see \Drupal\Core\Display\VariantManager
|
Chris@0
|
2135 * @see \Drupal\Core\Display\Annotation\DisplayVariant
|
Chris@0
|
2136 */
|
Chris@0
|
2137 function hook_display_variant_plugin_alter(array &$definitions) {
|
Chris@0
|
2138 $definitions['full_page']['admin_label'] = t('Block layout');
|
Chris@0
|
2139 }
|
Chris@0
|
2140
|
Chris@0
|
2141 /**
|
Chris@0
|
2142 * Allow modules to alter layout plugin definitions.
|
Chris@0
|
2143 *
|
Chris@0
|
2144 * @param \Drupal\Core\Layout\LayoutDefinition[] $definitions
|
Chris@0
|
2145 * The array of layout definitions, keyed by plugin ID.
|
Chris@0
|
2146 */
|
Chris@0
|
2147 function hook_layout_alter(&$definitions) {
|
Chris@0
|
2148 // Remove a layout.
|
Chris@0
|
2149 unset($definitions['twocol']);
|
Chris@0
|
2150 }
|
Chris@0
|
2151
|
Chris@0
|
2152 /**
|
Chris@0
|
2153 * Flush all persistent and static caches.
|
Chris@0
|
2154 *
|
Chris@0
|
2155 * This hook asks your module to clear all of its static caches,
|
Chris@0
|
2156 * in order to ensure a clean environment for subsequently
|
Chris@0
|
2157 * invoked data rebuilds.
|
Chris@0
|
2158 *
|
Chris@0
|
2159 * Do NOT use this hook for rebuilding information. Only use it to flush custom
|
Chris@0
|
2160 * caches.
|
Chris@0
|
2161 *
|
Chris@0
|
2162 * Static caches using drupal_static() do not need to be reset manually.
|
Chris@0
|
2163 * However, all other static variables that do not use drupal_static() must be
|
Chris@0
|
2164 * manually reset.
|
Chris@0
|
2165 *
|
Chris@0
|
2166 * This hook is invoked by drupal_flush_all_caches(). It runs before module data
|
Chris@0
|
2167 * is updated and before hook_rebuild().
|
Chris@0
|
2168 *
|
Chris@0
|
2169 * @see drupal_flush_all_caches()
|
Chris@0
|
2170 * @see hook_rebuild()
|
Chris@0
|
2171 */
|
Chris@0
|
2172 function hook_cache_flush() {
|
Chris@0
|
2173 if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') {
|
Chris@0
|
2174 _update_cache_clear();
|
Chris@0
|
2175 }
|
Chris@0
|
2176 }
|
Chris@0
|
2177
|
Chris@0
|
2178 /**
|
Chris@0
|
2179 * Rebuild data based upon refreshed caches.
|
Chris@0
|
2180 *
|
Chris@0
|
2181 * This hook allows your module to rebuild its data based on the latest/current
|
Chris@0
|
2182 * module data. It runs after hook_cache_flush() and after all module data has
|
Chris@0
|
2183 * been updated.
|
Chris@0
|
2184 *
|
Chris@0
|
2185 * This hook is only invoked after the system has been completely cleared;
|
Chris@0
|
2186 * i.e., all previously cached data is known to be gone and every API in the
|
Chris@0
|
2187 * system is known to return current information, so your module can safely rely
|
Chris@0
|
2188 * on all available data to rebuild its own.
|
Chris@0
|
2189 *
|
Chris@0
|
2190 * @see hook_cache_flush()
|
Chris@0
|
2191 * @see drupal_flush_all_caches()
|
Chris@0
|
2192 */
|
Chris@0
|
2193 function hook_rebuild() {
|
Chris@0
|
2194 $themes = \Drupal::service('theme_handler')->listInfo();
|
Chris@0
|
2195 foreach ($themes as $theme) {
|
Chris@0
|
2196 _block_rehash($theme->getName());
|
Chris@0
|
2197 }
|
Chris@0
|
2198 }
|
Chris@0
|
2199
|
Chris@0
|
2200 /**
|
Chris@0
|
2201 * Alter the configuration synchronization steps.
|
Chris@0
|
2202 *
|
Chris@0
|
2203 * @param array $sync_steps
|
Chris@0
|
2204 * A one-dimensional array of \Drupal\Core\Config\ConfigImporter method names
|
Chris@0
|
2205 * or callables that are invoked to complete the import, in the order that
|
Chris@0
|
2206 * they will be processed. Each callable item defined in $sync_steps should
|
Chris@0
|
2207 * either be a global function or a public static method. The callable should
|
Chris@0
|
2208 * accept a $context array by reference. For example:
|
Chris@16
|
2209 * @code
|
Chris@0
|
2210 * function _additional_configuration_step(&$context) {
|
Chris@0
|
2211 * // Do stuff.
|
Chris@0
|
2212 * // If finished set $context['finished'] = 1.
|
Chris@0
|
2213 * }
|
Chris@16
|
2214 * @endcode
|
Chris@0
|
2215 * For more information on creating batches, see the
|
Chris@0
|
2216 * @link batch Batch operations @endlink documentation.
|
Chris@0
|
2217 *
|
Chris@0
|
2218 * @see callback_batch_operation()
|
Chris@0
|
2219 * @see \Drupal\Core\Config\ConfigImporter::initialize()
|
Chris@0
|
2220 */
|
Chris@0
|
2221 function hook_config_import_steps_alter(&$sync_steps, \Drupal\Core\Config\ConfigImporter $config_importer) {
|
Chris@0
|
2222 $deletes = $config_importer->getUnprocessedConfiguration('delete');
|
Chris@0
|
2223 if (isset($deletes['field.storage.node.body'])) {
|
Chris@0
|
2224 $sync_steps[] = '_additional_configuration_step';
|
Chris@0
|
2225 }
|
Chris@0
|
2226 }
|
Chris@0
|
2227
|
Chris@0
|
2228 /**
|
Chris@0
|
2229 * Alter config typed data definitions.
|
Chris@0
|
2230 *
|
Chris@0
|
2231 * For example you can alter the typed data types representing each
|
Chris@0
|
2232 * configuration schema type to change default labels or form element renderers
|
Chris@0
|
2233 * used for configuration translation.
|
Chris@0
|
2234 *
|
Chris@0
|
2235 * If implementations of this hook add or remove configuration schema a
|
Chris@0
|
2236 * ConfigSchemaAlterException will be thrown. Keep in mind that there are tools
|
Chris@0
|
2237 * that may use the configuration schema for static analysis of configuration
|
Chris@0
|
2238 * files, like the string extractor for the localization system. Such systems
|
Chris@0
|
2239 * won't work with dynamically defined configuration schemas.
|
Chris@0
|
2240 *
|
Chris@0
|
2241 * For adding new data types use configuration schema YAML files instead.
|
Chris@0
|
2242 *
|
Chris@0
|
2243 * @param $definitions
|
Chris@0
|
2244 * Associative array of configuration type definitions keyed by schema type
|
Chris@0
|
2245 * names. The elements are themselves array with information about the type.
|
Chris@0
|
2246 *
|
Chris@0
|
2247 * @see \Drupal\Core\Config\TypedConfigManager
|
Chris@0
|
2248 * @see \Drupal\Core\Config\Schema\ConfigSchemaAlterException
|
Chris@0
|
2249 */
|
Chris@0
|
2250 function hook_config_schema_info_alter(&$definitions) {
|
Chris@0
|
2251 // Enhance the text and date type definitions with classes to generate proper
|
Chris@0
|
2252 // form elements in ConfigTranslationFormBase. Other translatable types will
|
Chris@0
|
2253 // appear as a one line textfield.
|
Chris@0
|
2254 $definitions['text']['form_element_class'] = '\Drupal\config_translation\FormElement\Textarea';
|
Chris@0
|
2255 $definitions['date_format']['form_element_class'] = '\Drupal\config_translation\FormElement\DateFormat';
|
Chris@0
|
2256 }
|
Chris@0
|
2257
|
Chris@0
|
2258 /**
|
Chris@0
|
2259 * Alter validation constraint plugin definitions.
|
Chris@0
|
2260 *
|
Chris@0
|
2261 * @param array[] $definitions
|
Chris@0
|
2262 * The array of validation constraint definitions, keyed by plugin ID.
|
Chris@0
|
2263 *
|
Chris@0
|
2264 * @see \Drupal\Core\Validation\ConstraintManager
|
Chris@0
|
2265 * @see \Drupal\Core\Validation\Annotation\Constraint
|
Chris@0
|
2266 */
|
Chris@0
|
2267 function hook_validation_constraint_alter(array &$definitions) {
|
Chris@0
|
2268 $definitions['Null']['class'] = '\Drupal\mymodule\Validator\Constraints\MyClass';
|
Chris@0
|
2269 }
|
Chris@0
|
2270
|
Chris@0
|
2271 /**
|
Chris@0
|
2272 * @} End of "addtogroup hooks".
|
Chris@0
|
2273 */
|
Chris@0
|
2274
|
Chris@0
|
2275 /**
|
Chris@0
|
2276 * @defgroup ajax Ajax API
|
Chris@0
|
2277 * @{
|
Chris@0
|
2278 * Overview for Drupal's Ajax API.
|
Chris@0
|
2279 *
|
Chris@0
|
2280 * @section sec_overview Overview of Ajax
|
Chris@0
|
2281 * Ajax is the process of dynamically updating parts of a page's HTML based on
|
Chris@0
|
2282 * data from the server. When a specified event takes place, a PHP callback is
|
Chris@0
|
2283 * triggered, which performs server-side logic and may return updated markup or
|
Chris@0
|
2284 * JavaScript commands to run. After the return, the browser runs the JavaScript
|
Chris@0
|
2285 * or updates the markup on the fly, with no full page refresh necessary.
|
Chris@0
|
2286 *
|
Chris@0
|
2287 * Many different events can trigger Ajax responses, including:
|
Chris@0
|
2288 * - Clicking a button
|
Chris@0
|
2289 * - Pressing a key
|
Chris@0
|
2290 * - Moving the mouse
|
Chris@0
|
2291 *
|
Chris@0
|
2292 * @section sec_framework Ajax responses in forms
|
Chris@0
|
2293 * Forms that use the Drupal Form API (see the
|
Chris@0
|
2294 * @link form_api Form API topic @endlink for more information about forms) can
|
Chris@0
|
2295 * trigger AJAX responses. Here is an outline of the steps:
|
Chris@0
|
2296 * - Add property '#ajax' to a form element in your form array, to trigger an
|
Chris@0
|
2297 * Ajax response.
|
Chris@0
|
2298 * - Write an Ajax callback to process the input and respond.
|
Chris@0
|
2299 * See sections below for details on these two steps.
|
Chris@0
|
2300 *
|
Chris@0
|
2301 * @subsection sub_form Adding Ajax triggers to a form
|
Chris@0
|
2302 * As an example of adding Ajax triggers to a form, consider editing a date
|
Chris@0
|
2303 * format, where the user is provided with a sample of the generated date output
|
Chris@0
|
2304 * as they type. To accomplish this, typing in the text field should trigger an
|
Chris@0
|
2305 * Ajax response. This is done in the text field form array element
|
Chris@0
|
2306 * in \Drupal\config_translation\FormElement\DateFormat::getFormElement():
|
Chris@0
|
2307 * @code
|
Chris@0
|
2308 * '#ajax' => array(
|
Chris@0
|
2309 * 'callback' => 'Drupal\config_translation\FormElement\DateFormat::ajaxSample',
|
Chris@0
|
2310 * 'event' => 'keyup',
|
Chris@0
|
2311 * 'progress' => array(
|
Chris@0
|
2312 * 'type' => 'throbber',
|
Chris@0
|
2313 * 'message' => NULL,
|
Chris@0
|
2314 * ),
|
Chris@0
|
2315 * ),
|
Chris@0
|
2316 * @endcode
|
Chris@0
|
2317 *
|
Chris@0
|
2318 * As you can see from this example, the #ajax property for a form element is
|
Chris@0
|
2319 * an array. Here are the details of its elements, all of which are optional:
|
Chris@0
|
2320 * - callback: The callback to invoke to handle the server side of the
|
Chris@0
|
2321 * Ajax event. More information on callbacks is below in @ref sub_callback.
|
Chris@0
|
2322 * - wrapper: The HTML 'id' attribute of the area where the content returned by
|
Chris@0
|
2323 * the callback should be placed. Note that callbacks have a choice of
|
Chris@0
|
2324 * returning content or JavaScript commands; 'wrapper' is used for content
|
Chris@0
|
2325 * returns.
|
Chris@0
|
2326 * - method: The jQuery method for placing the new content (used with
|
Chris@0
|
2327 * 'wrapper'). Valid options are 'replaceWith' (default), 'append', 'prepend',
|
Chris@0
|
2328 * 'before', 'after', or 'html'. See
|
Chris@0
|
2329 * http://api.jquery.com/category/manipulation/ for more information on these
|
Chris@0
|
2330 * methods.
|
Chris@0
|
2331 * - effect: The jQuery effect to use when placing the new HTML (used with
|
Chris@0
|
2332 * 'wrapper'). Valid options are 'none' (default), 'slide', or 'fade'.
|
Chris@0
|
2333 * - speed: The effect speed to use (used with 'effect' and 'wrapper'). Valid
|
Chris@0
|
2334 * options are 'slow' (default), 'fast', or the number of milliseconds the
|
Chris@0
|
2335 * effect should run.
|
Chris@0
|
2336 * - event: The JavaScript event to respond to. This is selected automatically
|
Chris@0
|
2337 * for the type of form element; provide a value to override the default.
|
Chris@0
|
2338 * - prevent: A JavaScript event to prevent when the event is triggered. For
|
Chris@0
|
2339 * example, if you use event 'mousedown' on a button, you might want to
|
Chris@0
|
2340 * prevent 'click' events from also being triggered.
|
Chris@0
|
2341 * - progress: An array indicating how to show Ajax processing progress. Can
|
Chris@0
|
2342 * contain one or more of these elements:
|
Chris@0
|
2343 * - type: Type of indicator: 'throbber' (default) or 'bar'.
|
Chris@0
|
2344 * - message: Translated message to display.
|
Chris@0
|
2345 * - url: For a bar progress indicator, URL path for determining progress.
|
Chris@0
|
2346 * - interval: For a bar progress indicator, how often to update it.
|
Chris@0
|
2347 * - url: A \Drupal\Core\Url to which to submit the Ajax request. If omitted,
|
Chris@0
|
2348 * defaults to either the same URL as the form or link destination is for
|
Chris@0
|
2349 * someone with JavaScript disabled, or a slightly modified version (e.g.,
|
Chris@0
|
2350 * with a query parameter added, removed, or changed) of that URL if
|
Chris@0
|
2351 * necessary to support Drupal's content negotiation. It is recommended to
|
Chris@0
|
2352 * omit this key and use Drupal's content negotiation rather than using
|
Chris@0
|
2353 * substantially different URLs between Ajax and non-Ajax.
|
Chris@0
|
2354 *
|
Chris@0
|
2355 * @subsection sub_callback Setting up a callback to process Ajax
|
Chris@0
|
2356 * Once you have set up your form to trigger an Ajax response (see @ref sub_form
|
Chris@0
|
2357 * above), you need to write some PHP code to process the response. If you use
|
Chris@0
|
2358 * 'path' in your Ajax set-up, your route controller will be triggered with only
|
Chris@0
|
2359 * the information you provide in the URL. If you use 'callback', your callback
|
Chris@0
|
2360 * method is a function, which will receive the $form and $form_state from the
|
Chris@0
|
2361 * triggering form. You can use $form_state to get information about the
|
Chris@0
|
2362 * data the user has entered into the form. For instance, in the above example
|
Chris@0
|
2363 * for the date format preview,
|
Chris@0
|
2364 * \Drupal\config_translation\FormElement\DateFormat\ajaxSample() does this to
|
Chris@0
|
2365 * get the format string entered by the user:
|
Chris@0
|
2366 * @code
|
Chris@0
|
2367 * $format_value = \Drupal\Component\Utility\NestedArray::getValue(
|
Chris@0
|
2368 * $form_state->getValues(),
|
Chris@0
|
2369 * $form_state->getTriggeringElement()['#array_parents']);
|
Chris@0
|
2370 * @endcode
|
Chris@0
|
2371 *
|
Chris@0
|
2372 * Once you have processed the input, you have your choice of returning HTML
|
Chris@0
|
2373 * markup or a set of Ajax commands. If you choose to return HTML markup, you
|
Chris@0
|
2374 * can return it as a string or a renderable array, and it will be placed in
|
Chris@0
|
2375 * the defined 'wrapper' element (see documentation above in @ref sub_form).
|
Chris@17
|
2376 * In addition, any messages returned by
|
Chris@17
|
2377 * \Drupal\Core\Messenger\Messenger::all(), themed as in
|
Chris@0
|
2378 * status-messages.html.twig, will be prepended.
|
Chris@0
|
2379 *
|
Chris@0
|
2380 * To return commands, you need to set up an object of class
|
Chris@0
|
2381 * \Drupal\Core\Ajax\AjaxResponse, and then use its addCommand() method to add
|
Chris@0
|
2382 * individual commands to it. In the date format preview example, the format
|
Chris@0
|
2383 * output is calculated, and then it is returned as replacement markup for a div
|
Chris@0
|
2384 * like this:
|
Chris@0
|
2385 * @code
|
Chris@0
|
2386 * $response = new AjaxResponse();
|
Chris@0
|
2387 * $response->addCommand(new ReplaceCommand(
|
Chris@0
|
2388 * '#edit-date-format-suffix',
|
Chris@0
|
2389 * '<small id="edit-date-format-suffix">' . $format . '</small>'));
|
Chris@0
|
2390 * return $response;
|
Chris@0
|
2391 * @endcode
|
Chris@0
|
2392 *
|
Chris@0
|
2393 * The individual commands that you can return implement interface
|
Chris@0
|
2394 * \Drupal\Core\Ajax\CommandInterface. Available commands provide the ability
|
Chris@0
|
2395 * to pop up alerts, manipulate text and markup in various ways, redirect
|
Chris@0
|
2396 * to a new URL, and the generic \Drupal\Core\Ajax\InvokeCommand, which
|
Chris@0
|
2397 * invokes an arbitrary jQuery command.
|
Chris@0
|
2398 *
|
Chris@0
|
2399 * As noted above, status messages are prepended automatically if you use the
|
Chris@0
|
2400 * 'wrapper' method and return HTML markup. This is not the case if you return
|
Chris@0
|
2401 * commands, but if you would like to show status messages, you can add
|
Chris@0
|
2402 * @code
|
Chris@0
|
2403 * array('#type' => 'status_messages')
|
Chris@0
|
2404 * @endcode
|
Chris@0
|
2405 * to a render array, use drupal_render() to render it, and add a command to
|
Chris@0
|
2406 * place the messages in an appropriate location.
|
Chris@0
|
2407 *
|
Chris@0
|
2408 * @section sec_other Other methods for triggering Ajax
|
Chris@0
|
2409 * Here are some additional methods you can use to trigger Ajax responses in
|
Chris@0
|
2410 * Drupal:
|
Chris@0
|
2411 * - Add class 'use-ajax' to a link. The link will be loaded using an Ajax
|
Chris@0
|
2412 * call. When using this method, the href of the link can contain '/nojs/' as
|
Chris@0
|
2413 * part of the path. When the Ajax JavaScript processes the page, it will
|
Chris@0
|
2414 * convert this to '/ajax/'. The server is then able to easily tell if this
|
Chris@0
|
2415 * request was made through an actual Ajax request or in a degraded state, and
|
Chris@0
|
2416 * respond appropriately.
|
Chris@0
|
2417 * - Add class 'use-ajax-submit' to a submit button in a form. The form will
|
Chris@0
|
2418 * then be submitted via Ajax to the path specified in the #action. Like the
|
Chris@0
|
2419 * ajax-submit class on links, this path will have '/nojs/' replaced with
|
Chris@0
|
2420 * '/ajax/' so that the submit handler can tell if the form was submitted in a
|
Chris@0
|
2421 * degraded state or not.
|
Chris@0
|
2422 * - Add property '#autocomplete_route_name' to a text field in a form. The
|
Chris@0
|
2423 * route controller for this route must return an array of options for
|
Chris@0
|
2424 * autocomplete, as a \Symfony\Component\HttpFoundation\JsonResponse object.
|
Chris@0
|
2425 * See the @link menu Routing topic @endlink for more information about
|
Chris@0
|
2426 * routing.
|
Chris@0
|
2427 */
|
Chris@0
|
2428
|
Chris@0
|
2429 /**
|
Chris@0
|
2430 * @} End of "defgroup ajax".
|
Chris@0
|
2431 */
|
Chris@0
|
2432
|
Chris@0
|
2433 /**
|
Chris@0
|
2434 * @defgroup service_tag Service Tags
|
Chris@0
|
2435 * @{
|
Chris@0
|
2436 * Service tags overview
|
Chris@0
|
2437 *
|
Chris@0
|
2438 * Some services have tags, which are defined in the service definition. Tags
|
Chris@0
|
2439 * are used to define a group of related services, or to specify some aspect of
|
Chris@0
|
2440 * how the service behaves. Typically, if you tag a service, your service class
|
Chris@0
|
2441 * must also implement a corresponding interface. Some common examples:
|
Chris@0
|
2442 * - access_check: Indicates a route access checking service; see the
|
Chris@0
|
2443 * @link menu Menu and routing system topic @endlink for more information.
|
Chris@0
|
2444 * - cache.bin: Indicates a cache bin service; see the
|
Chris@0
|
2445 * @link cache Cache topic @endlink for more information.
|
Chris@0
|
2446 * - event_subscriber: Indicates an event subscriber service. Event subscribers
|
Chris@0
|
2447 * can be used for dynamic routing and route altering; see the
|
Chris@0
|
2448 * @link menu Menu and routing system topic @endlink for more information.
|
Chris@0
|
2449 * They can also be used for other purposes; see
|
Chris@0
|
2450 * http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html
|
Chris@0
|
2451 * for more information.
|
Chris@0
|
2452 * - needs_destruction: Indicates that a destruct() method needs to be called
|
Chris@0
|
2453 * at the end of a request to finalize operations, if this service was
|
Chris@0
|
2454 * instantiated. Services should implement \Drupal\Core\DestructableInterface
|
Chris@0
|
2455 * in this case.
|
Chris@0
|
2456 * - context_provider: Indicates a block context provider, used for example
|
Chris@0
|
2457 * by block conditions. It has to implement
|
Chris@0
|
2458 * \Drupal\Core\Plugin\Context\ContextProviderInterface.
|
Chris@0
|
2459 * - http_client_middleware: Indicates that the service provides a guzzle
|
Chris@0
|
2460 * middleware, see
|
Chris@0
|
2461 * https://guzzle.readthedocs.org/en/latest/handlers-and-middleware.html for
|
Chris@0
|
2462 * more information.
|
Chris@0
|
2463 *
|
Chris@0
|
2464 * Creating a tag for a service does not do anything on its own, but tags
|
Chris@0
|
2465 * can be discovered or queried in a compiler pass when the container is built,
|
Chris@0
|
2466 * and a corresponding action can be taken. See
|
Chris@0
|
2467 * \Drupal\Core\Render\MainContent\MainContentRenderersPass for an example of
|
Chris@0
|
2468 * finding tagged services.
|
Chris@0
|
2469 *
|
Chris@0
|
2470 * See @link container Services and Dependency Injection Container @endlink for
|
Chris@0
|
2471 * information on services and the dependency injection container.
|
Chris@0
|
2472 *
|
Chris@0
|
2473 * @}
|
Chris@0
|
2474 */
|
Chris@0
|
2475
|
Chris@0
|
2476 /**
|
Chris@0
|
2477 * @defgroup events Events
|
Chris@0
|
2478 * @{
|
Chris@0
|
2479 * Overview of event dispatch and subscribing
|
Chris@0
|
2480 *
|
Chris@0
|
2481 * @section sec_intro Introduction and terminology
|
Chris@0
|
2482 * Events are part of the Symfony framework: they allow for different components
|
Chris@0
|
2483 * of the system to interact and communicate with each other. Each event has a
|
Chris@0
|
2484 * unique string name. One system component dispatches the event at an
|
Chris@0
|
2485 * appropriate time; many events are dispatched by Drupal core and the Symfony
|
Chris@0
|
2486 * framework in every request. Other system components can register as event
|
Chris@0
|
2487 * subscribers; when an event is dispatched, a method is called on each
|
Chris@0
|
2488 * registered subscriber, allowing each one to react. For more on the general
|
Chris@0
|
2489 * concept of events, see
|
Chris@0
|
2490 * http://symfony.com/doc/current/components/event_dispatcher/introduction.html
|
Chris@0
|
2491 *
|
Chris@0
|
2492 * @section sec_dispatch Dispatching events
|
Chris@0
|
2493 * To dispatch an event, call the
|
Chris@0
|
2494 * \Symfony\Component\EventDispatcher\EventDispatcherInterface::dispatch()
|
Chris@0
|
2495 * method on the 'event_dispatcher' service (see the
|
Chris@0
|
2496 * @link container Services topic @endlink for more information about how to
|
Chris@0
|
2497 * interact with services). The first argument is the unique event name, which
|
Chris@0
|
2498 * you should normally define as a constant in a separate static class (see
|
Chris@0
|
2499 * \Symfony\Component\HttpKernel\KernelEvents and
|
Chris@0
|
2500 * \Drupal\Core\Config\ConfigEvents for examples). The second argument is a
|
Chris@0
|
2501 * \Symfony\Component\EventDispatcher\Event object; normally you will need to
|
Chris@0
|
2502 * extend this class, so that your event class can provide data to the event
|
Chris@0
|
2503 * subscribers.
|
Chris@0
|
2504 *
|
Chris@0
|
2505 * @section sec_subscribe Registering event subscribers
|
Chris@0
|
2506 * Here are the steps to register an event subscriber:
|
Chris@0
|
2507 * - Define a service in your module, tagged with 'event_subscriber' (see the
|
Chris@0
|
2508 * @link container Services topic @endlink for instructions).
|
Chris@0
|
2509 * - Define a class for your subscriber service that implements
|
Chris@0
|
2510 * \Symfony\Component\EventDispatcher\EventSubscriberInterface
|
Chris@0
|
2511 * - In your class, the getSubscribedEvents method returns a list of the events
|
Chris@0
|
2512 * this class is subscribed to, and which methods on the class should be
|
Chris@0
|
2513 * called for each one. Example:
|
Chris@0
|
2514 * @code
|
Chris@0
|
2515 * public static function getSubscribedEvents() {
|
Chris@0
|
2516 * // Subscribe to kernel terminate with priority 100.
|
Chris@0
|
2517 * $events[KernelEvents::TERMINATE][] = array('onTerminate', 100);
|
Chris@0
|
2518 * // Subscribe to kernel request with default priority of 0.
|
Chris@0
|
2519 * $events[KernelEvents::REQUEST][] = array('onRequest');
|
Chris@0
|
2520 * return $events;
|
Chris@0
|
2521 * }
|
Chris@0
|
2522 * @endcode
|
Chris@0
|
2523 * - Write the methods that respond to the events; each one receives the
|
Chris@0
|
2524 * event object provided in the dispatch as its one argument. In the above
|
Chris@0
|
2525 * example, you would need to write onTerminate() and onRequest() methods.
|
Chris@0
|
2526 *
|
Chris@0
|
2527 * Note that in your getSubscribedEvents() method, you can optionally set the
|
Chris@0
|
2528 * priority of your event subscriber (see terminate example above). Event
|
Chris@0
|
2529 * subscribers with higher priority numbers get executed first; the default
|
Chris@0
|
2530 * priority is zero. If two event subscribers for the same event have the same
|
Chris@0
|
2531 * priority, the one defined in a module with a lower module weight will fire
|
Chris@0
|
2532 * first. Subscribers defined in the same services file are fired in
|
Chris@0
|
2533 * definition order. If order matters defining a priority is strongly advised
|
Chris@0
|
2534 * instead of relying on these two tie breaker rules as they might change in a
|
Chris@0
|
2535 * minor release.
|
Chris@0
|
2536 * @}
|
Chris@0
|
2537 */
|
Chris@16
|
2538
|
Chris@16
|
2539 /**
|
Chris@16
|
2540 * @defgroup session Sessions
|
Chris@16
|
2541 * @{
|
Chris@16
|
2542 * Store and retrieve data associated with a user's browsing session.
|
Chris@16
|
2543 *
|
Chris@16
|
2544 * @section sec_intro Overview
|
Chris@16
|
2545 * The Drupal session management subsystem is built on top of the Symfony
|
Chris@16
|
2546 * session component. It is optimized in order to minimize the impact of
|
Chris@16
|
2547 * anonymous sessions on caching proxies. A session is only started if necessary
|
Chris@16
|
2548 * and the session cookie is removed from the browser as soon as the session
|
Chris@16
|
2549 * has no data. For this reason it is important for contributed and custom
|
Chris@16
|
2550 * code to remove session data if it is not used anymore.
|
Chris@16
|
2551 *
|
Chris@16
|
2552 * @section sec_usage Usage
|
Chris@16
|
2553 * Session data is accessed via the
|
Chris@16
|
2554 * \Symfony\Component\HttpFoundation\Request::getSession()
|
Chris@16
|
2555 * method, which returns an instance of
|
Chris@16
|
2556 * \Symfony\Component\HttpFoundation\Session\SessionInterface. The most
|
Chris@16
|
2557 * important methods on SessionInterface are set(), get(), and remove().
|
Chris@16
|
2558 *
|
Chris@16
|
2559 * The following code fragment shows the implementation of a counter controller
|
Chris@16
|
2560 * relying on the session:
|
Chris@16
|
2561 * @code
|
Chris@16
|
2562 * public function counter(Request $request) {
|
Chris@16
|
2563 * $session = $request->getSession();
|
Chris@16
|
2564 * $count = $session->get('mymodule.counter', 0) + 1;
|
Chris@16
|
2565 * $session->set('mymodule.counter', $count);
|
Chris@16
|
2566 *
|
Chris@16
|
2567 * return [
|
Chris@16
|
2568 * '#markup' => $this->t('Page Views: @count', ['@count' => $count]),
|
Chris@16
|
2569 * '#cache' => [
|
Chris@16
|
2570 * 'max-age' => 0,
|
Chris@16
|
2571 * ],
|
Chris@16
|
2572 * ];
|
Chris@16
|
2573 * }
|
Chris@16
|
2574 *
|
Chris@16
|
2575 * public function reset(Request $request) {
|
Chris@16
|
2576 * $session = $request->getSession();
|
Chris@16
|
2577 * $session->remove('mymodule.counter');
|
Chris@16
|
2578 * }
|
Chris@16
|
2579 * @endcode
|
Chris@16
|
2580 *
|
Chris@16
|
2581 * It is important to keep the amount of data stored inside the session to a
|
Chris@16
|
2582 * minimum, as the complete session is loaded on every request. Also third
|
Chris@16
|
2583 * party session storage backends do not necessarily allow objects of unlimited
|
Chris@16
|
2584 * size. If it is necessary to collect a non-trivial amount of data specific to
|
Chris@16
|
2585 * a user's session, use the Key/Value store to save the serialized data and
|
Chris@16
|
2586 * only store the key to the entry in the session.
|
Chris@16
|
2587 *
|
Chris@16
|
2588 * @section sec_reserved Reserved attributes and namespacing
|
Chris@16
|
2589 * Contributed modules relying on the session are encouraged to namespace
|
Chris@16
|
2590 * session attributes by prefixing them with their project name or an
|
Chris@16
|
2591 * abbreviation thereof.
|
Chris@16
|
2592 *
|
Chris@16
|
2593 * Some attributes are reserved for Drupal core and must not be accessed from
|
Chris@16
|
2594 * within contributed and custom code. Reserved attributes include:
|
Chris@16
|
2595 * - uid: The user ID for an authenticated user. The value of this attribute
|
Chris@16
|
2596 * cannot be modified.
|
Chris@16
|
2597 * @}
|
Chris@16
|
2598 */
|