annotate modules/contrib/migrate_plus/src/Plugin/migrate/process/FileBlob.php @ 5:12f9dff5fda9 tip

Update to Drupal core 8.7.1
author Chris Cannam
date Thu, 09 May 2019 15:34:47 +0100
parents a9cd425dd02b
children
rev   line source
Chris@4 1 <?php
Chris@4 2
Chris@4 3 namespace Drupal\migrate_plus\Plugin\migrate\process;
Chris@4 4
Chris@4 5 use Drupal\Core\File\FileSystemInterface;
Chris@4 6 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
Chris@4 7 use Drupal\migrate\MigrateExecutableInterface;
Chris@4 8 use Drupal\migrate\MigrateSkipProcessException;
Chris@4 9 use Drupal\migrate\ProcessPluginBase;
Chris@4 10 use Drupal\migrate\Row;
Chris@4 11 use Symfony\Component\DependencyInjection\ContainerInterface;
Chris@4 12
Chris@4 13 /**
Chris@4 14 * Copy a file from a blob into a file.
Chris@4 15 *
Chris@5 16 * The source value is an indexed array of two values:
Chris@5 17 * - The destination URI, e.g. 'public://example.txt'.
Chris@5 18 * - The binary blob data.
Chris@5 19 *
Chris@5 20 * Available configuration keys:
Chris@5 21 * - reuse: true
Chris@5 22 *
Chris@5 23 * @codingStandardsIgnoreStart
Chris@5 24 *
Chris@5 25 * Examples:
Chris@5 26 * @code
Chris@5 27 * uri:
Chris@5 28 * plugin: file_blob
Chris@5 29 * source:
Chris@5 30 * - 'public://example.txt'
Chris@5 31 * - blob
Chris@5 32 * @endcode
Chris@5 33 * Above, a basic configuration.
Chris@5 34 *
Chris@5 35 * @code
Chris@5 36 * source:
Chris@5 37 * constants:
Chris@5 38 * destination: public://images
Chris@5 39 * process:
Chris@5 40 * destination_blob:
Chris@5 41 * plugin: callback
Chris@5 42 * callable: base64_decode
Chris@5 43 * source:
Chris@5 44 * - blob
Chris@5 45 * destination_basename:
Chris@5 46 * plugin: callback
Chris@5 47 * callable: basename
Chris@5 48 * source: file_name
Chris@5 49 * destination_path:
Chris@5 50 * plugin: concat
Chris@5 51 * source:
Chris@5 52 * - constants/destination
Chris@5 53 * - @destination_basename
Chris@5 54 * uri:
Chris@5 55 * plugin: file_blob
Chris@5 56 * source:
Chris@5 57 * - @destination_path
Chris@5 58 * - @destination_blob
Chris@5 59 * @endcode
Chris@5 60 In the example above, it is necessary to manipulate the values before they
Chris@5 61 * are processed by this plugin. This is because this plugin takes a binary blob
Chris@5 62 * and saves it as a file. In many cases, as in this example, the data is base64
Chris@5 63 * encoded and should be decoded first. In destination_blob, the incoming data
Chris@5 64 * is decoded from base64 to binary. The destination_path element is
Chris@5 65 * concatenating the base filename with the destination directory set in the
Chris@5 66 * constants to create the final path. The resulting values are then referenced
Chris@5 67 * as the source of the file_blob plugin.
Chris@5 68 *
Chris@5 69 * @codingStandardsIgnoreEnd
Chris@5 70 *
Chris@4 71 * @MigrateProcessPlugin(
Chris@4 72 * id = "file_blob"
Chris@4 73 * )
Chris@4 74 */
Chris@4 75 class FileBlob extends ProcessPluginBase implements ContainerFactoryPluginInterface {
Chris@4 76
Chris@4 77 /**
Chris@4 78 * The file system service.
Chris@4 79 *
Chris@4 80 * @var \Drupal\Core\File\FileSystemInterface
Chris@4 81 */
Chris@4 82 protected $fileSystem;
Chris@4 83
Chris@4 84 /**
Chris@4 85 * Constructs a file_blob process plugin.
Chris@4 86 *
Chris@4 87 * @param array $configuration
Chris@4 88 * The plugin configuration.
Chris@4 89 * @param string $plugin_id
Chris@4 90 * The plugin ID.
Chris@4 91 * @param mixed $plugin_definition
Chris@4 92 * The plugin definition.
Chris@4 93 * @param \Drupal\Core\File\FileSystemInterface $file_system
Chris@4 94 * The file system service.
Chris@4 95 */
Chris@4 96 public function __construct(array $configuration, $plugin_id, $plugin_definition, FileSystemInterface $file_system) {
Chris@4 97 $configuration += [
Chris@4 98 'reuse' => FALSE,
Chris@4 99 ];
Chris@4 100 parent::__construct($configuration, $plugin_id, $plugin_definition);
Chris@4 101 $this->fileSystem = $file_system;
Chris@4 102 }
Chris@4 103
Chris@4 104 /**
Chris@4 105 * {@inheritdoc}
Chris@4 106 */
Chris@4 107 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
Chris@4 108 return new static(
Chris@4 109 $configuration,
Chris@4 110 $plugin_id,
Chris@4 111 $plugin_definition,
Chris@4 112 $container->get('file_system')
Chris@4 113 );
Chris@4 114 }
Chris@4 115
Chris@4 116 /**
Chris@4 117 * {@inheritdoc}
Chris@4 118 */
Chris@4 119 public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
Chris@4 120 // If we're stubbing a file entity, return a URI of NULL so it will get
Chris@4 121 // stubbed by the general process.
Chris@4 122 if ($row->isStub()) {
Chris@4 123 return NULL;
Chris@4 124 }
Chris@4 125 list($destination, $blob) = $value;
Chris@4 126
Chris@4 127 // Determine if we going to overwrite existing files or not touch them.
Chris@4 128 $replace = $this->getOverwriteMode();
Chris@4 129
Chris@4 130 // Attempt to save the file to avoid calling file_prepare_directory() any
Chris@4 131 // more than absolutely necessary.
Chris@4 132 if ($this->putFile($destination, $blob, $replace)) {
Chris@4 133 return $destination;
Chris@4 134 }
Chris@4 135 $dir = $this->getDirectory($destination);
Chris@5 136 // TODO: remove after 8.6 is no longer supported in
Chris@5 137 // https://www.drupal.org/project/migrate_plus/issues/3035587
Chris@5 138 if (version_compare(\Drupal::VERSION, '8.7', '>=')) {
Chris@5 139 $success = $this->fileSystem->prepareDirectory($dir, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
Chris@5 140 if (!$success) {
Chris@5 141 throw new MigrateSkipProcessException("Could not create directory '$dir'");
Chris@5 142 }
Chris@5 143 }
Chris@5 144 elseif (file_prepare_directory($dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
Chris@4 145 throw new MigrateSkipProcessException("Could not create directory '$dir'");
Chris@4 146 }
Chris@4 147 if ($this->putFile($destination, $blob, $replace)) {
Chris@4 148 return $destination;
Chris@4 149 }
Chris@4 150 throw new MigrateSkipProcessException("Blob data could not be copied to $destination.");
Chris@4 151 }
Chris@4 152
Chris@4 153 /**
Chris@4 154 * Try to save the file.
Chris@4 155 *
Chris@4 156 * @param string $destination
Chris@4 157 * The destination path or URI.
Chris@4 158 * @param string $blob
Chris@4 159 * The base64 encoded file contents.
Chris@4 160 * @param int $replace
Chris@5 161 * (optional) either FileSystemInterface::EXISTS_REPLACE; (default) or
Chris@5 162 * FileSystemInterface::EXISTS_ERROR, depending on the configuration.
Chris@4 163 *
Chris@4 164 * @return bool|string
Chris@4 165 * File path on success, FALSE on failure.
Chris@4 166 */
Chris@5 167 protected function putFile($destination, $blob, $replace = FileSystemInterface::EXISTS_REPLACE) {
Chris@5 168 // TODO: remove after 8.6 is no longer supported in
Chris@5 169 // https://www.drupal.org/project/migrate_plus/issues/3035587
Chris@5 170 if (!isset($replace)) {
Chris@5 171 $replace = FILE_EXISTS_REPLACE;
Chris@5 172 }
Chris@5 173 if (version_compare(\Drupal::VERSION, '8.7', '>=')) {
Chris@5 174 $path = $this->fileSystem->getDestinationFilename($destination, $replace);
Chris@5 175 }
Chris@5 176 else {
Chris@5 177 $path = file_destination($destination, $replace);
Chris@5 178 }
Chris@5 179
Chris@5 180 if ($path) {
Chris@4 181 if (file_put_contents($path, $blob)) {
Chris@4 182 return $path;
Chris@4 183 }
Chris@4 184 else {
Chris@4 185 return FALSE;
Chris@4 186 }
Chris@4 187 }
Chris@4 188
Chris@4 189 // File was already copied.
Chris@4 190 return $destination;
Chris@4 191 }
Chris@4 192
Chris@4 193 /**
Chris@4 194 * Determines how to handle file conflicts.
Chris@4 195 *
Chris@4 196 * @return int
Chris@5 197 * Either FileSystemInterface::EXISTS_REPLACE; (default) or
Chris@5 198 * FileSystemInterface::EXISTS_ERROR, depending on the configuration.
Chris@4 199 */
Chris@4 200 protected function getOverwriteMode() {
Chris@5 201 // TODO: remove after 8.6 is no longer supported in
Chris@5 202 // https://www.drupal.org/project/migrate_plus/issues/3035587
Chris@5 203 if (isset($this->configuration['reuse']) && !empty($this->configuration['reuse'])) {
Chris@5 204 if (version_compare(\Drupal::VERSION, '8.7', '>=')) {
Chris@5 205 return FileSystemInterface::EXISTS_ERROR;
Chris@5 206 }
Chris@5 207 else {
Chris@5 208 return FILE_EXISTS_ERROR;
Chris@5 209 }
Chris@5 210
Chris@4 211 }
Chris@4 212
Chris@5 213 if (version_compare(\Drupal::VERSION, '8.7', '>=')) {
Chris@5 214 return FileSystemInterface::EXISTS_REPLACE;
Chris@5 215 }
Chris@5 216 else {
Chris@5 217 return FILE_EXISTS_REPLACE;
Chris@5 218 }
Chris@4 219 }
Chris@4 220
Chris@4 221 /**
Chris@4 222 * Returns the directory component of a URI or path.
Chris@4 223 *
Chris@4 224 * For URIs like public://foo.txt, the full physical path of public://
Chris@4 225 * will be returned, since a scheme by itself will trip up certain file
Chris@4 226 * API functions (such as file_prepare_directory()).
Chris@4 227 *
Chris@4 228 * @param string $uri
Chris@4 229 * The URI or path.
Chris@4 230 *
Chris@4 231 * @return string|false
Chris@4 232 * The directory component of the path or URI, or FALSE if it could not
Chris@4 233 * be determined.
Chris@4 234 */
Chris@4 235 protected function getDirectory($uri) {
Chris@4 236 $dir = $this->fileSystem->dirname($uri);
Chris@4 237 if (substr($dir, -3) == '://') {
Chris@4 238 return $this->fileSystem->realpath($dir);
Chris@4 239 }
Chris@4 240 return $dir;
Chris@4 241 }
Chris@4 242
Chris@4 243 }