FilePostDump.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. <?php
  2. namespace Drupal\reinstall\EventSubscriber;
  3. use Drupal\Component\Utility\Unicode;
  4. use Drupal\Core\File\FileSystem;
  5. use Drupal\Core\File\FileSystemInterface;
  6. use Drupal\file\Entity\File;
  7. use Drupal\reinstall\DumperEvent;
  8. use Drupal\reinstall\ReinstallEvents;
  9. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  10. /**
  11. * Class FilePostDump performs physical file copy on file dump.
  12. */
  13. class FilePostDump implements EventSubscriberInterface {
  14. /**
  15. * The file_system service.
  16. *
  17. * @var \Drupal\Core\File\FileSystemInterface
  18. */
  19. protected $fileSystem;
  20. /**
  21. * The export/import path.
  22. *
  23. * @var string
  24. */
  25. protected $importPath;
  26. /**
  27. * FilePostDump constructor.
  28. *
  29. * @param \Drupal\Core\File\FileSystemInterface $fileSystem
  30. * The file_system service.
  31. * @param string $importPath
  32. * The reinstall.path parameter.
  33. */
  34. public function __construct(FileSystemInterface $fileSystem, string $importPath) {
  35. $this->fileSystem = $fileSystem;
  36. $this->importPath = $importPath;
  37. }
  38. /**
  39. * {@inheritdoc}
  40. */
  41. public static function getSubscribedEvents() {
  42. return [
  43. ReinstallEvents::POST_DUMP => 'onDumpPost',
  44. ];
  45. }
  46. /**
  47. * Callback for POST_DUMP event.
  48. *
  49. * @param \Drupal\reinstall\DumperEvent $event
  50. * The post-dump event.
  51. */
  52. public function onDumpPost(DumperEvent $event) {
  53. if ($event->storage->getEntityTypeId() !== 'file') {
  54. return;
  55. }
  56. $this->dumpFiles($event->bundleName, $event->entities);
  57. }
  58. /**
  59. * Dump the actual files for a file entity bundle.
  60. *
  61. * @param string $bundleName
  62. * The bundle name.
  63. * @param array $files
  64. * The file URLs.
  65. */
  66. public function dumpFiles(string $bundleName, array $files) {
  67. $importPath = $this->importPath;
  68. $dir = "$importPath/$bundleName";
  69. $usedNamespaces = array_keys(array_reduce($files, [__CLASS__, 'namespacesReducer'], []));
  70. $lists = [];
  71. foreach ($usedNamespaces as $ns) {
  72. // XXX Consider using \0 to support xargs: file names MAY contain spaces.
  73. $path = "$dir/$ns.list.txt";
  74. $nsDir = "$dir/$ns";
  75. if (!is_dir($nsDir)) {
  76. echo "Creating $nsDir\n";
  77. mkdir($nsDir, 0777, TRUE);
  78. }
  79. // fopen() is in text mode by default.
  80. $lists[$ns] = [
  81. 'dir' => $nsDir,
  82. 'handle' => fopen($path, 'w'),
  83. ];
  84. }
  85. /** @var \Drupal\file\Entity\File $file */
  86. foreach ($files as $file) {
  87. $uri = $file->getFileUri();
  88. $target = file_uri_target($uri);
  89. $ns = $this->fileSystem->uriScheme($uri);
  90. fwrite($lists[$ns]['handle'], $target . "\n");
  91. $dest = $lists[$ns]['dir'] . '/' . $target;
  92. $dir = dirname($dest);
  93. if (!is_dir($dir)) {
  94. mkdir($dir, 0777, TRUE);
  95. }
  96. file_unmanaged_copy($uri, $dest, FILE_EXISTS_REPLACE);
  97. }
  98. foreach ($lists as $list) {
  99. fclose($list['handle']);
  100. }
  101. }
  102. /**
  103. * An array_reduce() callback to collect namespaces from file entities.
  104. *
  105. * @param string[] $accu
  106. * The namespaces accumulator.
  107. * @param \Drupal\file\Entity\File $fileItem
  108. * A file description.
  109. *
  110. * @return string[]
  111. * An array of namespaces used on the site.
  112. *
  113. * @see \Drupal\reinstall\Dumper::dumpFiles()
  114. */
  115. protected static function namespacesReducer(array $accu, File $fileItem) {
  116. $uri = $fileItem->getFileUri();
  117. // Plain filenames without a namespace. Should not happen, but...
  118. if (FALSE === ($len = Unicode::strpos($uri, '://'))) {
  119. return $accu;
  120. };
  121. $namespace = Unicode::substr($uri, 0, $len);
  122. $accu[$namespace] = TRUE;
  123. return $accu;
  124. }
  125. }