0

I would like to create a file and write some data to it using a PHP CLI script. The script will bootstrap Drupal and then use either the file_system or file.repository services to perform the task.

For sanity check, I tested the methods using Drush:

drush eval 'use Drupal\Core\File\FileSystemInterface; $fileRepository = \Drupal::service("file.repository"); $fileRepository->writeData("hello", "public://foo.txt", FileSystemInterface::EXISTS_REPLACE);'

I then created a script (saved at $DOCROOT/scripts/create-file.php):

#!/usr/bin/env php
<?php

use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\File\FileSystemInterface;

// Specify relative path to the drupal root.
$autoloader = require_once __DIR__ . '/../web/autoload.php';

$request = Request::createFromGlobals();

// Bootstrap drupal to different levels
$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
$kernel->boot();

/** @var \Drupal\file\FileRepositoryInterface $fileRepository */
$fileRepository = \Drupal::service('file.repository');
$fileRepository->writeData("Hello, World!", "public://foo.txt", FileSystemInterface::EXISTS_REPLACE);

I made the script executable (chmod +x scripts/create-file.php) and ran it (./scripts/create-file.php).

But I received a FileWriteException error: Drupal\Core\File\Exception\FileWriteException: Temporary file 'temporary://<somefile>' could not be created. It creates a zero-byte temp file and stops there as if there are permission issues.

I confirmed the temp directory locations (in both Drupal and PHP installation): drush eval 'print \Drupal::service("file_system")->getTempDirectory();' and php -r 'echo sys_get_temp_dir() . "\n";' Both return /tmp as the temporary directory.

I tested this on both a Linux server and a Docker container (DDEV instance). The behavior is the same in both. If I remove the Drupal bootstrap stuff and execute the script with drush scr, it works (similar to the sanity check above).

Is there something preventing a shell user from executing a script that then tries to write a file?

Update:

The issue seems to exist only when using the Drupal service. For instance, the following method works:

$fp = fopen("/var/www/html/web/sites/default/files/foo.txt","wb");
fwrite($fp,$output);
fclose($fp);

Using Symfony also works:

use Symfony\Component\Filesystem\Filesystem;

$filesystem = new Filesystem();
$filesystem->dumpFile('/var/www/html/web/sites/default/files/foo.txt', $output);

But the following does not work:

$filesystem = \Drupal::service('file_system');
$filesystem->saveData($output, "/var/www/html/web/sites/default/files/foo.txt", FileSystemInterface::EXISTS_REPLACE);
2
  • If you add print \Drupal::service("file_system")->getTempDirectory(); to the create-file.php script (after the kernel boot) does it output the expected path? And can you successfully touch /THAT_TMP_PATH/foo with the same user you're executing the script as?
    – Clive
    May 28, 2023 at 20:13
  • @Clive - Yes, print \Drupal::service("file_system")->getTempDirectory() . PHP_EOL; does return the expected path. And adding something like the following works: /** @var Symfony\Component\Process\Process $process */ $process = new Process('echo "Does this work?" > /tmp/foo.txt'); $process->run();
    – H M
    May 29, 2023 at 2:21

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.