Score:0

Temporary file issue when creating a file from an external script

mw flag
H M

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);
cn flag
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?
H M avatar
mw flag
H M
@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();`
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.