Issue #2216071 by dawehner, damiankloip, Gaelan: Views plugins need a way to store...
[project/drupal.git] / core / modules / update / update.compare.inc
CommitLineData
6ec2ff7e 1<?php
6ec2ff7e
DB
2
3/**
4 * @file
5 * Code required only when comparing available updates to existing data.
6 */
7
8/**
c0f384fa 9 * Fetches an array of installed and enabled projects.
6ec2ff7e
DB
10 *
11 * This is only responsible for generating an array of projects (taking into
12 * account projects that include more than one module or theme). Other
13 * information like the specific version and install type (official release,
14 * dev snapshot, etc) is handled later in update_process_project_info() since
15 * that logic is only required when preparing the status report, not for
16 * fetching the available release data.
17 *
c0f384fa 18 * This array is fairly expensive to construct, since it involves a lot of disk
fea7acc2 19 * I/O, so we store the results. However, since this is not the data about
c0f384fa
JH
20 * available updates fetched from the network, it is acceptable to invalidate it
21 * somewhat quickly. If we keep this data for very long, site administrators are
22 * more likely to see incorrect results if they upgrade to a newer version of a
23 * module or theme but do not visit certain pages that automatically clear this
fea7acc2 24 * data.
c0f384fa
JH
25 *
26 * @return
27 * An associative array of currently enabled projects keyed by the
28 * machine-readable project short name. Each project contains:
29 * - name: The machine-readable project short name.
6bf13bd8 30 * - info: An array with values from the main .info.yml file for this project.
c0f384fa
JH
31 * - name: The human-readable name of the project.
32 * - package: The package that the project is grouped under.
33 * - version: The version of the project.
34 * - project: The Drupal.org project name.
6bf13bd8
D
35 * - datestamp: The date stamp of the project's main .info.yml file.
36 * - _info_file_ctime: The maximum file change time for all of the .info.yml
c0f384fa
JH
37 * files included in this project.
38 * - datestamp: The date stamp when the project was released, if known.
39 * - includes: An associative array containing all projects included with this
40 * project, keyed by the machine-readable short name with the human-readable
41 * name as value.
42 * - project_type: The type of project. Allowed values are 'module' and
43 * 'theme'.
44 * - project_status: This indicates if the project is enabled and will always
45 * be TRUE, as the function only returns enabled projects.
46 * - sub_themes: If the project is a theme it contains an associative array of
47 * all sub-themes.
48 * - base_themes: If the project is a theme it contains an associative array
49 * of all base-themes.
cac7e7c4 50 *
cc1892d4 51 * @see \Drupal\update\UpdateManager::getProjects()
eb53ecc5
AP
52 *
53 * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
54 * Use \Drupal::service('update.manager')->getProjects().
6ec2ff7e
DB
55 */
56function update_get_projects() {
cc1892d4 57 return \Drupal::service('update.manager')->getProjects();
6ec2ff7e
DB
58}
59
60/**
c0f384fa
JH
61 * Determines version and type information for currently installed projects.
62 *
63 * Processes the list of projects on the system to figure out the currently
6ec2ff7e
DB
64 * installed versions, and other information that is required before we can
65 * compare against the available releases to produce the status report.
66 *
67 * @param $projects
68 * Array of project information from update_get_projects().
69 */
70function update_process_project_info(&$projects) {
71 foreach ($projects as $key => $project) {
72 // Assume an official release until we see otherwise.
73 $install_type = 'official';
74
75 $info = $project['info'];
76
77 if (isset($info['version'])) {
78 // Check for development snapshots
79 if (preg_match('@(dev|HEAD)@', $info['version'])) {
80 $install_type = 'dev';
81 }
82
83 // Figure out what the currently installed major version is. We need
84 // to handle both contribution (e.g. "5.x-1.3", major = 1) and core
85 // (e.g. "5.1", major = 5) version strings.
86 $matches = array();
87 if (preg_match('/^(\d+\.x-)?(\d+)\..*$/', $info['version'], $matches)) {
88 $info['major'] = $matches[2];
89 }
90 elseif (!isset($info['major'])) {
91 // This would only happen for version strings that don't follow the
92 // drupal.org convention. We let contribs define "major" in their
6bf13bd8 93 // .info.yml in this case, and only if that's missing would we hit this.
6ec2ff7e
DB
94 $info['major'] = -1;
95 }
96 }
97 else {
98 // No version info available at all.
99 $install_type = 'unknown';
100 $info['version'] = t('Unknown');
101 $info['major'] = -1;
102 }
103
104 // Finally, save the results we care about into the $projects array.
105 $projects[$key]['existing_version'] = $info['version'];
106 $projects[$key]['existing_major'] = $info['major'];
107 $projects[$key]['install_type'] = $install_type;
6ec2ff7e
DB
108 }
109}
110
111/**
c0f384fa 112 * Calculates the current update status of all projects on the site.
6ec2ff7e 113 *
0c2dbb72
AB
114 * The results of this function are expensive to compute, especially on sites
115 * with lots of modules or themes, since it involves a lot of comparisons and
fea7acc2 116 * other operations. Therefore, we store the results. However, since this is not
0c2dbb72
AB
117 * the data about available updates fetched from the network, it is ok to
118 * invalidate it somewhat quickly. If we keep this data for very long, site
c0f384fa
JH
119 * administrators are more likely to see incorrect results if they upgrade to a
120 * newer version of a module or theme but do not visit certain pages that
fea7acc2 121 * automatically clear this.
0c2dbb72
AB
122 *
123 * @param array $available
124 * Data about available project releases.
125 *
c0f384fa
JH
126 * @return
127 * An array of installed projects with current update status information.
128 *
0c2dbb72
AB
129 * @see update_get_available()
130 * @see update_get_projects()
131 * @see update_process_project_info()
fea7acc2 132 * @see update_project_storage()
0c2dbb72
AB
133 */
134function update_calculate_project_data($available) {
fea7acc2 135 // Retrieve the projects from storage, if present.
136 $projects = update_project_storage('update_project_data');
137 // If $projects is empty, then the data must be rebuilt.
138 // Otherwise, return the data and skip the rest of the function.
0c2dbb72
AB
139 if (!empty($projects)) {
140 return $projects;
141 }
142 $projects = update_get_projects();
143 update_process_project_info($projects);
144 foreach ($projects as $project => $project_info) {
145 if (isset($available[$project])) {
526fc9bc 146 update_calculate_project_update_status($projects[$project], $available[$project]);
0c2dbb72
AB
147 }
148 else {
149 $projects[$project]['status'] = UPDATE_UNKNOWN;
150 $projects[$project]['reason'] = t('No available releases found');
151 }
152 }
153 // Give other modules a chance to alter the status (for example, to allow a
154 // contrib module to provide fine-grained settings to ignore specific
155 // projects or releases).
78e5e9bf 156 \Drupal::moduleHandler()->alter('update_status', $projects);
0c2dbb72 157
fea7acc2 158 // Store the site's update status for at most 1 hour.
5bb0d5e6 159 \Drupal::keyValueExpirable('update')->setWithExpire('update_project_data', $projects, 3600);
0c2dbb72
AB
160 return $projects;
161}
162
163/**
c0f384fa 164 * Calculates the current update status of a specific project.
0c2dbb72 165 *
c0f384fa
JH
166 * This function is the heart of the update status feature. For each project it
167 * is invoked with, it first checks if the project has been flagged with a
0c2dbb72
AB
168 * special status like "unsupported" or "insecure", or if the project node
169 * itself has been unpublished. In any of those cases, the project is marked
170 * with an error and the next project is considered.
550b9255
H
171 *
172 * If the project itself is valid, the function decides what major release
173 * series to consider. The project defines what the currently supported major
c0f384fa
JH
174 * versions are for each version of core, so the first step is to make sure the
175 * current version is still supported. If so, that's the target version. If the
176 * current version is unsupported, the project maintainer's recommended major
177 * version is used. There's also a check to make sure that this function never
178 * recommends an earlier release than the currently installed major version.
179 *
180 * Given a target major version, the available releases are scanned looking for
6ec2ff7e 181 * the specific release to recommend (avoiding beta releases and development
c0f384fa
JH
182 * snapshots if possible). For the target major version, the highest patch level
183 * is found. If there is a release at that patch level with no extra ("beta",
184 * etc.), then the release at that patch level with the most recent release date
185 * is recommended. If every release at that patch level has extra (only betas),
186 * then the latest release from the previous patch level is recommended. For
187 * example:
6ec2ff7e 188 *
c0f384fa
JH
189 * - 1.6-bugfix <-- recommended version because 1.6 already exists.
190 * - 1.6
6ec2ff7e
DB
191 *
192 * or
193 *
c0f384fa
JH
194 * - 1.6-beta
195 * - 1.5 <-- recommended version because no 1.6 exists.
196 * - 1.4
6ec2ff7e 197 *
c0f384fa
JH
198 * Also, the latest release from the same major version is looked for, even beta
199 * releases, to display to the user as the "Latest version" option.
200 * Additionally, the latest official release from any higher major versions that
201 * have been released is searched for to provide a set of "Also available"
6ec2ff7e
DB
202 * options.
203 *
c0f384fa
JH
204 * Finally, and most importantly, the release history continues to be scanned
205 * until the currently installed release is reached, searching for anything
206 * marked as a security update. If any security updates have been found between
207 * the recommended release and the installed version, all of the releases that
6ec2ff7e
DB
208 * included a security fix are recorded so that the site administrator can be
209 * warned their site is insecure, and links pointing to the release notes for
210 * each security update can be included (which, in turn, will link to the
211 * official security announcements for each vulnerability).
212 *
213 * This function relies on the fact that the .xml release history data comes
214 * sorted based on major version and patch level, then finally by release date
215 * if there are multiple releases such as betas from the same major.patch
c0f384fa 216 * version (e.g., 5.x-1.5-beta1, 5.x-1.5-beta2, and 5.x-1.5). Development
6ec2ff7e
DB
217 * snapshots for a given major version are always listed last.
218 *
c0f384fa
JH
219 * @param $project_data
220 * An array containing information about a specific project.
221 * @param $available
222 * Data about available project releases of a specific project.
6ec2ff7e 223 */
526fc9bc 224function update_calculate_project_update_status(&$project_data, $available) {
0c2dbb72
AB
225 foreach (array('title', 'link') as $attribute) {
226 if (!isset($project_data[$attribute]) && isset($available[$attribute])) {
227 $project_data[$attribute] = $available[$attribute];
228 }
e130f208 229 }
550b9255 230
0c2dbb72
AB
231 // If the project status is marked as something bad, there's nothing else
232 // to consider.
233 if (isset($available['project_status'])) {
234 switch ($available['project_status']) {
235 case 'insecure':
236 $project_data['status'] = UPDATE_NOT_SECURE;
237 if (empty($project_data['extra'])) {
238 $project_data['extra'] = array();
550b9255 239 }
0c2dbb72
AB
240 $project_data['extra'][] = array(
241 'class' => array('project-not-secure'),
242 'label' => t('Project not secure'),
243 'data' => t('This project has been labeled insecure by the Drupal security team, and is no longer available for download. Immediately disabling everything included by this project is strongly recommended!'),
244 );
245 break;
246 case 'unpublished':
247 case 'revoked':
248 $project_data['status'] = UPDATE_REVOKED;
249 if (empty($project_data['extra'])) {
250 $project_data['extra'] = array();
251 }
252 $project_data['extra'][] = array(
253 'class' => array('project-revoked'),
254 'label' => t('Project revoked'),
255 'data' => t('This project has been revoked, and is no longer available for download. Disabling everything included by this project is strongly recommended!'),
256 );
257 break;
258 case 'unsupported':
259 $project_data['status'] = UPDATE_NOT_SUPPORTED;
260 if (empty($project_data['extra'])) {
261 $project_data['extra'] = array();
262 }
263 $project_data['extra'][] = array(
264 'class' => array('project-not-supported'),
265 'label' => t('Project not supported'),
266 'data' => t('This project is no longer supported, and is no longer available for download. Disabling everything included by this project is strongly recommended!'),
267 );
268 break;
269 case 'not-fetched':
270 $project_data['status'] = UPDATE_NOT_FETCHED;
271 $project_data['reason'] = t('Failed to get available update data.');
272 break;
273
274 default:
275 // Assume anything else (e.g. 'published') is valid and we should
276 // perform the rest of the logic in this function.
277 break;
278 }
279 }
550b9255 280
0c2dbb72
AB
281 if (!empty($project_data['status'])) {
282 // We already know the status for this project, so there's nothing else to
283 // compute. Record the project status into $project_data and we're done.
284 $project_data['project_status'] = $available['project_status'];
285 return;
286 }
550b9255 287
0c2dbb72
AB
288 // Figure out the target major version.
289 $existing_major = $project_data['existing_major'];
290 $supported_majors = array();
291 if (isset($available['supported_majors'])) {
292 $supported_majors = explode(',', $available['supported_majors']);
293 }
294 elseif (isset($available['default_major'])) {
295 // Older release history XML file without supported or recommended.
296 $supported_majors[] = $available['default_major'];
297 }
550b9255 298
0c2dbb72
AB
299 if (in_array($existing_major, $supported_majors)) {
300 // Still supported, stay at the current major version.
301 $target_major = $existing_major;
302 }
303 elseif (isset($available['recommended_major'])) {
304 // Since 'recommended_major' is defined, we know this is the new XML
305 // format. Therefore, we know the current release is unsupported since
306 // its major version was not in the 'supported_majors' list. We should
307 // find the best release from the recommended major version.
308 $target_major = $available['recommended_major'];
309 $project_data['status'] = UPDATE_NOT_SUPPORTED;
310 }
311 elseif (isset($available['default_major'])) {
312 // Older release history XML file without recommended, so recommend
313 // the currently defined "default_major" version.
314 $target_major = $available['default_major'];
315 }
316 else {
317 // Malformed XML file? Stick with the current version.
318 $target_major = $existing_major;
319 }
6ec2ff7e 320
0c2dbb72
AB
321 // Make sure we never tell the admin to downgrade. If we recommended an
322 // earlier version than the one they're running, they'd face an
323 // impossible data migration problem, since Drupal never supports a DB
324 // downgrade path. In the unfortunate case that what they're running is
325 // unsupported, and there's nothing newer for them to upgrade to, we
326 // can't print out a "Recommended version", but just have to tell them
327 // what they have is unsupported and let them figure it out.
328 $target_major = max($existing_major, $target_major);
329
330 $release_patch_changed = '';
331 $patch = '';
332
333 // If the project is marked as UPDATE_FETCH_PENDING, it means that the
334 // data we currently have (if any) is stale, and we've got a task queued
335 // up to (re)fetch the data. In that case, we mark it as such, merge in
336 // whatever data we have (e.g. project title and link), and move on.
337 if (!empty($available['fetch_status']) && $available['fetch_status'] == UPDATE_FETCH_PENDING) {
338 $project_data['status'] = UPDATE_FETCH_PENDING;
339 $project_data['reason'] = t('No available update data');
340 $project_data['fetch_status'] = $available['fetch_status'];
341 return;
342 }
9058a898 343
0c2dbb72
AB
344 // Defend ourselves from XML history files that contain no releases.
345 if (empty($available['releases'])) {
346 $project_data['status'] = UPDATE_UNKNOWN;
347 $project_data['reason'] = t('No available releases found');
348 return;
349 }
350 foreach ($available['releases'] as $version => $release) {
351 // First, if this is the existing release, check a few conditions.
352 if ($project_data['existing_version'] === $version) {
353 if (isset($release['terms']['Release type']) &&
354 in_array('Insecure', $release['terms']['Release type'])) {
355 $project_data['status'] = UPDATE_NOT_SECURE;
550b9255 356 }
0c2dbb72
AB
357 elseif ($release['status'] == 'unpublished') {
358 $project_data['status'] = UPDATE_REVOKED;
359 if (empty($project_data['extra'])) {
360 $project_data['extra'] = array();
6ec2ff7e 361 }
0c2dbb72
AB
362 $project_data['extra'][] = array(
363 'class' => array('release-revoked'),
364 'label' => t('Release revoked'),
365 'data' => t('Your currently installed release has been revoked, and is no longer available for download. Disabling everything included in this release or upgrading is strongly recommended!'),
366 );
367 }
368 elseif (isset($release['terms']['Release type']) &&
369 in_array('Unsupported', $release['terms']['Release type'])) {
370 $project_data['status'] = UPDATE_NOT_SUPPORTED;
371 if (empty($project_data['extra'])) {
372 $project_data['extra'] = array();
6ec2ff7e 373 }
0c2dbb72
AB
374 $project_data['extra'][] = array(
375 'class' => array('release-not-supported'),
376 'label' => t('Release not supported'),
377 'data' => t('Your currently installed release is now unsupported, and is no longer available for download. Disabling everything included in this release or upgrading is strongly recommended!'),
378 );
379 }
380 }
6ec2ff7e 381
0c2dbb72
AB
382 // Otherwise, ignore unpublished, insecure, or unsupported releases.
383 if ($release['status'] == 'unpublished' ||
384 (isset($release['terms']['Release type']) &&
385 (in_array('Insecure', $release['terms']['Release type']) ||
386 in_array('Unsupported', $release['terms']['Release type'])))) {
387 continue;
388 }
6ec2ff7e 389
0c2dbb72
AB
390 // See if this is a higher major version than our target and yet still
391 // supported. If so, record it as an "Also available" release.
a3ee7d2a 392 // Note: Some projects have a HEAD release from CVS days, which could
393 // be one of those being compared. They would not have version_major
394 // set, so we must call isset first.
395 if (isset($release['version_major']) && $release['version_major'] > $target_major) {
0c2dbb72
AB
396 if (in_array($release['version_major'], $supported_majors)) {
397 if (!isset($project_data['also'])) {
398 $project_data['also'] = array();
6ec2ff7e 399 }
0c2dbb72
AB
400 if (!isset($project_data['also'][$release['version_major']])) {
401 $project_data['also'][$release['version_major']] = $version;
402 $project_data['releases'][$version] = $release;
6ec2ff7e 403 }
0c2dbb72
AB
404 }
405 // Otherwise, this release can't matter to us, since it's neither
406 // from the release series we're currently using nor the recommended
407 // release. We don't even care about security updates for this
408 // branch, since if a project maintainer puts out a security release
409 // at a higher major version and not at the lower major version,
410 // they must remove the lower version from the supported major
411 // versions at the same time, in which case we won't hit this code.
412 continue;
413 }
6ec2ff7e 414
0c2dbb72
AB
415 // Look for the 'latest version' if we haven't found it yet. Latest is
416 // defined as the most recent version for the target major version.
417 if (!isset($project_data['latest_version'])
418 && $release['version_major'] == $target_major) {
419 $project_data['latest_version'] = $version;
420 $project_data['releases'][$version] = $release;
421 }
6ec2ff7e 422
0c2dbb72
AB
423 // Look for the development snapshot release for this branch.
424 if (!isset($project_data['dev_version'])
425 && $release['version_major'] == $target_major
426 && isset($release['version_extra'])
427 && $release['version_extra'] == 'dev') {
428 $project_data['dev_version'] = $version;
429 $project_data['releases'][$version] = $release;
430 }
6ec2ff7e 431
0c2dbb72
AB
432 // Look for the 'recommended' version if we haven't found it yet (see
433 // phpdoc at the top of this function for the definition).
434 if (!isset($project_data['recommended'])
435 && $release['version_major'] == $target_major
436 && isset($release['version_patch'])) {
437 if ($patch != $release['version_patch']) {
438 $patch = $release['version_patch'];
439 $release_patch_changed = $release;
6ec2ff7e 440 }
0c2dbb72
AB
441 if (empty($release['version_extra']) && $patch == $release['version_patch']) {
442 $project_data['recommended'] = $release_patch_changed['version'];
443 $project_data['releases'][$release_patch_changed['version']] = $release_patch_changed;
6ec2ff7e 444 }
0c2dbb72 445 }
6ec2ff7e 446
0c2dbb72
AB
447 // Stop searching once we hit the currently installed version.
448 if ($project_data['existing_version'] === $version) {
449 break;
450 }
550b9255 451
0c2dbb72
AB
452 // If we're running a dev snapshot and have a timestamp, stop
453 // searching for security updates once we hit an official release
454 // older than what we've got. Allow 100 seconds of leeway to handle
6bf13bd8 455 // differences between the datestamp in the .info.yml file and the
0c2dbb72
AB
456 // timestamp of the tarball itself (which are usually off by 1 or 2
457 // seconds) so that we don't flag that as a new release.
458 if ($project_data['install_type'] == 'dev') {
459 if (empty($project_data['datestamp'])) {
460 // We don't have current timestamp info, so we can't know.
550b9255
H
461 continue;
462 }
0c2dbb72
AB
463 elseif (isset($release['date']) && ($project_data['datestamp'] + 100 > $release['date'])) {
464 // We're newer than this, so we can skip it.
6ec2ff7e
DB
465 continue;
466 }
0c2dbb72 467 }
6ec2ff7e 468
0c2dbb72
AB
469 // See if this release is a security update.
470 if (isset($release['terms']['Release type'])
471 && in_array('Security update', $release['terms']['Release type'])) {
472 $project_data['security updates'][] = $release;
473 }
474 }
6ec2ff7e 475
0c2dbb72
AB
476 // If we were unable to find a recommended version, then make the latest
477 // version the recommended version if possible.
478 if (!isset($project_data['recommended']) && isset($project_data['latest_version'])) {
479 $project_data['recommended'] = $project_data['latest_version'];
480 }
481
482 //
483 // Check to see if we need an update or not.
484 //
485
486 if (!empty($project_data['security updates'])) {
487 // If we found security updates, that always trumps any other status.
488 $project_data['status'] = UPDATE_NOT_SECURE;
489 }
490
491 if (isset($project_data['status'])) {
492 // If we already know the status, we're done.
493 return;
494 }
495
496 // If we don't know what to recommend, there's nothing we can report.
497 // Bail out early.
498 if (!isset($project_data['recommended'])) {
499 $project_data['status'] = UPDATE_UNKNOWN;
500 $project_data['reason'] = t('No available releases found');
501 return;
502 }
503
504 // If we're running a dev snapshot, compare the date of the dev snapshot
505 // with the latest official version, and record the absolute latest in
506 // 'latest_dev' so we can correctly decide if there's a newer release
507 // than our current snapshot.
508 if ($project_data['install_type'] == 'dev') {
509 if (isset($project_data['dev_version']) && $available['releases'][$project_data['dev_version']]['date'] > $available['releases'][$project_data['latest_version']]['date']) {
510 $project_data['latest_dev'] = $project_data['dev_version'];
6ec2ff7e
DB
511 }
512 else {
0c2dbb72 513 $project_data['latest_dev'] = $project_data['latest_version'];
6ec2ff7e
DB
514 }
515 }
e130f208 516
0c2dbb72
AB
517 // Figure out the status, based on what we've seen and the install type.
518 switch ($project_data['install_type']) {
519 case 'official':
520 if ($project_data['existing_version'] === $project_data['recommended'] || $project_data['existing_version'] === $project_data['latest_version']) {
521 $project_data['status'] = UPDATE_CURRENT;
522 }
523 else {
524 $project_data['status'] = UPDATE_NOT_CURRENT;
525 }
526 break;
527
528 case 'dev':
529 $latest = $available['releases'][$project_data['latest_dev']];
530 if (empty($project_data['datestamp'])) {
531 $project_data['status'] = UPDATE_NOT_CHECKED;
532 $project_data['reason'] = t('Unknown release date');
533 }
534 elseif (($project_data['datestamp'] + 100 > $latest['date'])) {
535 $project_data['status'] = UPDATE_CURRENT;
536 }
537 else {
538 $project_data['status'] = UPDATE_NOT_CURRENT;
539 }
540 break;
541
542 default:
543 $project_data['status'] = UPDATE_UNKNOWN;
544 $project_data['reason'] = t('Invalid info');
545 }
e130f208
H
546}
547
548/**
fea7acc2 549 * Retrieves update storage data or empties it.
e130f208
H
550 *
551 * Two very expensive arrays computed by this module are the list of all
fea7acc2 552 * installed modules and themes (and .info.yml data, project associations, etc), and
553 * the current status of the site relative to the currently available releases.
554 * These two arrays are stored and used whenever possible. The data is cleared
555 * whenever the administrator visits the status report, available updates
556 * report, or the module or theme administration pages, since we should always
557 * recompute the most current values on any of those pages.
e130f208 558 *
cac7e7c4
AB
559 * Note: while both of these arrays are expensive to compute (in terms of disk
560 * I/O and some fairly heavy CPU processing), neither of these is the actual
561 * data about available updates that we have to fetch over the network from
fea7acc2 562 * updates.drupal.org. That information is stored in the
563 * 'update_available_releases' collection -- it needs to persist longer than 1
cac7e7c4
AB
564 * hour and never get invalidated just by visiting a page on the site.
565 *
fea7acc2 566 * @param $key
567 * The key of data to return. Valid options are 'update_project_data' and
568 * 'update_project_projects'.
e130f208
H
569 *
570 * @return
fea7acc2 571 * The stored value of the $projects array generated by
c0f384fa 572 * update_calculate_project_data() or update_get_projects(), or an empty array
fea7acc2 573 * when the storage is cleared.
cc1892d4 574 *
cc1892d4 575 * @see \Drupal\update\UpdateManager::projectStorage()
eb53ecc5
AP
576 *
577 * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
578 * Use \Drupal::service('update.manager')->projectStorage().
e130f208 579 */
fea7acc2 580function update_project_storage($key) {
cc1892d4 581 return \Drupal::service('update.manager')->projectStorage($key);
6ec2ff7e 582}