annotate core/lib/Drupal/Core/Utility/ProjectInfo.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core\Utility;
Chris@0 4
Chris@0 5 use Drupal\Core\Extension\Extension;
Chris@0 6
Chris@0 7 /**
Chris@0 8 * Performs operations on drupal.org project data.
Chris@0 9 */
Chris@0 10 class ProjectInfo {
Chris@0 11
Chris@0 12 /**
Chris@0 13 * Populates an array of project data.
Chris@0 14 *
Chris@0 15 * This iterates over a list of the installed modules or themes and groups
Chris@0 16 * them by project and status. A few parts of this function assume that
Chris@0 17 * enabled modules and themes are always processed first, and if uninstalled
Chris@0 18 * modules or themes are being processed (there is a setting to control if
Chris@0 19 * uninstalled code should be included in the Available updates report or
Chris@0 20 * not),those are only processed after $projects has been populated with
Chris@0 21 * information about the enabled code. 'Hidden' modules and themes are
Chris@0 22 * ignored if they are not installed. 'Hidden' Modules and themes in the
Chris@0 23 * "Testing" package are ignored regardless of installation status.
Chris@0 24 *
Chris@0 25 * This function also records the latest change time on the .info.yml files
Chris@0 26 * for each module or theme, which is important data which is used when
Chris@0 27 * deciding if the available update data should be invalidated.
Chris@0 28 *
Chris@0 29 * @param array $projects
Chris@0 30 * Reference to the array of project data of what's installed on this site.
Chris@0 31 * @param \Drupal\Core\Extension\Extension[] $list
Chris@0 32 * Array of data to process to add the relevant info to the $projects array.
Chris@0 33 * @param string $project_type
Chris@0 34 * The kind of data in the list. Can be 'module' or 'theme'.
Chris@0 35 * @param bool $status
Chris@0 36 * Boolean that controls what status (enabled or uninstalled) to process out
Chris@0 37 * of the $list and add to the $projects array.
Chris@0 38 * @param array $additional_whitelist
Chris@0 39 * (optional) Array of additional elements to be collected from the .info.yml
Chris@0 40 * file. Defaults to array().
Chris@0 41 */
Chris@0 42 public function processInfoList(array &$projects, array $list, $project_type, $status, array $additional_whitelist = []) {
Chris@0 43 foreach ($list as $file) {
Chris@0 44 // Just projects with a matching status should be listed.
Chris@0 45 if ($file->status != $status) {
Chris@0 46 continue;
Chris@0 47 }
Chris@0 48
Chris@0 49 // Skip if the .info.yml file is broken.
Chris@0 50 if (empty($file->info)) {
Chris@0 51 continue;
Chris@0 52 }
Chris@0 53
Chris@0 54 // Skip if it's a hidden project and the project is not installed.
Chris@0 55 if (!empty($file->info['hidden']) && empty($status)) {
Chris@0 56 continue;
Chris@0 57 }
Chris@0 58
Chris@0 59 // Skip if it's a hidden project and the project is a test project. Tests
Chris@0 60 // should use hook_system_info_alter() to test ProjectInfo's
Chris@0 61 // functionality.
Chris@0 62 if (!empty($file->info['hidden']) && isset($file->info['package']) && $file->info['package'] == 'Testing') {
Chris@0 63 continue;
Chris@0 64 }
Chris@0 65
Chris@0 66 // If the .info.yml doesn't define the 'project', try to figure it out.
Chris@0 67 if (!isset($file->info['project'])) {
Chris@0 68 $file->info['project'] = $this->getProjectName($file);
Chris@0 69 }
Chris@0 70
Chris@0 71 // If we still don't know the 'project', give up.
Chris@0 72 if (empty($file->info['project'])) {
Chris@0 73 continue;
Chris@0 74 }
Chris@0 75
Chris@0 76 // If we don't already know it, grab the change time on the .info.yml file
Chris@0 77 // itself. Note: we need to use the ctime, not the mtime (modification
Chris@0 78 // time) since many (all?) tar implementations will go out of their way to
Chris@0 79 // set the mtime on the files it creates to the timestamps recorded in the
Chris@0 80 // tarball. We want to see the last time the file was changed on disk,
Chris@0 81 // which is left alone by tar and correctly set to the time the .info.yml
Chris@0 82 // file was unpacked.
Chris@0 83 if (!isset($file->info['_info_file_ctime'])) {
Chris@0 84 $file->info['_info_file_ctime'] = $file->getCTime();
Chris@0 85 }
Chris@0 86
Chris@0 87 if (!isset($file->info['datestamp'])) {
Chris@0 88 $file->info['datestamp'] = 0;
Chris@0 89 }
Chris@0 90
Chris@0 91 $project_name = $file->info['project'];
Chris@0 92
Chris@0 93 // Figure out what project type we're going to use to display this module
Chris@0 94 // or theme. If the project name is 'drupal', we don't want it to show up
Chris@0 95 // under the usual "Modules" section, we put it at a special "Drupal Core"
Chris@0 96 // section at the top of the report.
Chris@0 97 if ($project_name == 'drupal') {
Chris@0 98 $project_display_type = 'core';
Chris@0 99 }
Chris@0 100 else {
Chris@0 101 $project_display_type = $project_type;
Chris@0 102 }
Chris@0 103 if (empty($status)) {
Chris@0 104 // If we're processing uninstalled modules or themes, append a suffix.
Chris@0 105 $project_display_type .= '-disabled';
Chris@0 106 }
Chris@0 107 if (!isset($projects[$project_name])) {
Chris@0 108 // Only process this if we haven't done this project, since a single
Chris@0 109 // project can have multiple modules or themes.
Chris@0 110 $projects[$project_name] = [
Chris@0 111 'name' => $project_name,
Chris@0 112 // Only save attributes from the .info.yml file we care about so we do
Chris@0 113 // not bloat our RAM usage needlessly.
Chris@0 114 'info' => $this->filterProjectInfo($file->info, $additional_whitelist),
Chris@0 115 'datestamp' => $file->info['datestamp'],
Chris@0 116 'includes' => [$file->getName() => $file->info['name']],
Chris@0 117 'project_type' => $project_display_type,
Chris@0 118 'project_status' => $status,
Chris@0 119 ];
Chris@0 120 }
Chris@0 121 elseif ($projects[$project_name]['project_type'] == $project_display_type) {
Chris@0 122 // Only add the file we're processing to the 'includes' array for this
Chris@0 123 // project if it is of the same type and status (which is encoded in the
Chris@0 124 // $project_display_type). This prevents listing all the uninstalled
Chris@0 125 // modules included with an enabled project if we happen to be checking
Chris@0 126 // for uninstalled modules, too.
Chris@0 127 $projects[$project_name]['includes'][$file->getName()] = $file->info['name'];
Chris@0 128 $projects[$project_name]['info']['_info_file_ctime'] = max($projects[$project_name]['info']['_info_file_ctime'], $file->info['_info_file_ctime']);
Chris@0 129 $projects[$project_name]['datestamp'] = max($projects[$project_name]['datestamp'], $file->info['datestamp']);
Chris@0 130 }
Chris@0 131 elseif (empty($status)) {
Chris@0 132 // If we have a project_name that matches, but the project_display_type
Chris@0 133 // does not, it means we're processing a uninstalled module or theme
Chris@0 134 // that belongs to a project that has some enabled code. In this case,
Chris@0 135 // we add the uninstalled thing into a separate array for separate
Chris@0 136 // display.
Chris@0 137 $projects[$project_name]['disabled'][$file->getName()] = $file->info['name'];
Chris@0 138 }
Chris@0 139 }
Chris@0 140 }
Chris@0 141
Chris@0 142 /**
Chris@0 143 * Determines what project a given file object belongs to.
Chris@0 144 *
Chris@0 145 * @param \Drupal\Core\Extension\Extension $file
Chris@0 146 * An extension object.
Chris@0 147 *
Chris@0 148 * @return string
Chris@0 149 * The canonical project short name.
Chris@0 150 */
Chris@0 151 public function getProjectName(Extension $file) {
Chris@0 152 $project_name = '';
Chris@0 153 if (isset($file->info['project'])) {
Chris@0 154 $project_name = $file->info['project'];
Chris@0 155 }
Chris@0 156 elseif (strpos($file->getPath(), 'core/modules') === 0) {
Chris@0 157 $project_name = 'drupal';
Chris@0 158 }
Chris@0 159 return $project_name;
Chris@0 160 }
Chris@0 161
Chris@0 162 /**
Chris@0 163 * Filters the project .info.yml data to only save attributes we need.
Chris@0 164 *
Chris@0 165 * @param array $info
Chris@0 166 * Array of .info.yml file data as returned by
Chris@0 167 * \Drupal\Core\Extension\InfoParser.
Chris@0 168 * @param $additional_whitelist
Chris@0 169 * (optional) Array of additional elements to be collected from the .info.yml
Chris@0 170 * file. Defaults to array().
Chris@0 171 *
Chris@0 172 * @return
Chris@0 173 * Array of .info.yml file data we need for the update manager.
Chris@0 174 *
Chris@0 175 * @see \Drupal\Core\Utility\ProjectInfo::processInfoList()
Chris@0 176 */
Chris@0 177 public function filterProjectInfo($info, $additional_whitelist = []) {
Chris@0 178 $whitelist = [
Chris@0 179 '_info_file_ctime',
Chris@0 180 'datestamp',
Chris@0 181 'major',
Chris@0 182 'name',
Chris@0 183 'package',
Chris@0 184 'project',
Chris@0 185 'project status url',
Chris@0 186 'version',
Chris@0 187 ];
Chris@0 188 $whitelist = array_merge($whitelist, $additional_whitelist);
Chris@0 189 return array_intersect_key($info, array_combine($whitelist, $whitelist));
Chris@0 190 }
Chris@0 191
Chris@0 192 }