Change record status: 
Project: 
Introduced in branch: 
8.x-3.x
Introduced in version: 
8.x-3.3
Description: 

The following protected methods in \Drupal\feeds\Feeds\Target\File have been deprecated in feeds:3.3.0 and will be removed in feeds:4.0.0:

  • getFileName()
  • getContent()
  • writeData()
  • getFileLegacy()

File resolution logic has been moved to the feeds.file_resolver service. Custom modules that extend the File target and override these methods should migrate to decorating the feeds.file_resolver service instead. The File resolver service also has extra features to deal with files that the File target did not have, such as accepting a server path as input.

Impact

Modules that override getFileName(), getContent(), or writeData() in a File target subclass will receive deprecation warnings. The overrides continue to work during the deprecation period (until feeds:4.0.0) via a legacy code path.

Migration

Use a service decorator that extends FileResolver and overrides the appropriate protected method. Register it with decorates: feeds.file_resolver in your module's services.yml.

getContent() — adding authentication

Before:

class MyFileTarget extends File {

  protected function getContent($url) {
    $response = $this->client->request('GET', $url, [
      'headers' => ['Authorization' => 'Bearer ' . $this->getToken()],
    ]);
    if ($response->getStatusCode() >= 400) {
      throw new TargetValidationException('Download failed');
    }
    return (string) $response->getBody();
  }

}

After: Decorate FileResolver and override downloadFile():

class MyFileResolver extends FileResolver {

  protected function downloadFile(string $url): string {
    $options = [
      'headers' => ['Authorization' => 'Bearer ' . $this->getAuthToken($url)],
    ];
    $response = $this->client->request('GET', $url, $options);
    if ($response->getStatusCode() >= 400) {
      throw new DownloadException($url, $response->getStatusCode());
    }
    return (string) $response->getBody();
  }

}

getFileName() — custom filename format

Before:

class MyFileTarget extends File {

  protected function getFileName($url) {
    $filename = parent::getFileName($url);
    return date('Y-m-d') . '-' . $filename;
  }

}

After: Decorate FileResolver and override getFileNameFromUrl():

class MyFileResolver extends FileResolver {

  protected function getFileNameFromUrl(string $url): string {
    $filename = parent::getFileNameFromUrl($url);
    return date('Y-m-d') . '-' . $filename;
  }

}

writeData() — pre-processing content before save

Before:

class MyFileTarget extends File {

  protected function writeData($data, $destination = NULL, $replace = FileExists::Rename) {
    $data = $this->processContent($data);
    return parent::writeData($data, $destination, $replace);
  }

}

After: Decorate FileResolver and override saveFileAndCreateEntity():

class MyFileResolver extends FileResolver {

  protected function saveFileAndCreateEntity(string $content, string $destination, FileExists|int|string $replace, array $options = []): ?FileInterface {
    $content = $this->processContent($content);
    return parent::saveFileAndCreateEntity($content, $destination, $replace, $options);
  }

}

Service definition

Register the decorator in mymodule.services.yml:

services:
  mymodule.file_resolver:
    class: Drupal\mymodule\Utility\MyFileResolver
    decorates: feeds.file_resolver
    factory: Drupal\mymodule\Utility\MyFileResolver::create
    arguments: ['@service_container']

The decorator extends FileResolver and implements a create() factory (see docs/EXTENDING_FILERESOLVER.md). The original service is available as @mymodule.file_resolver.inner if you need to delegate to it.

See docs/EXTENDING_FILERESOLVER.md for detailed examples.

Impacts: 
Module developers