Problem/Motivation
TikaExtractor::extract() runs Tika with shell_exec() and treats a NULL return value as "Tika Extractor is not available."
shell_exec() returns NULL both when the command fails and when the command succeeds with empty stdout. A file that Tika can read but that contains no text (for example a JPEG without OCR text) therefore throws. FilesExtractor logs Error extracting text from file @file_id for @entity_type @entity_id. Exception: Tika Extractor is not available.
The settings form can still report that Tika can be reached, because that check only runs java -jar tika.jar -V and looks at the exit code. Reproduced with 10.0.11. The same shell_exec() / is_null() check is still present in 10.0.12.
Steps to reproduce
- Configure the Tika app extractor with a working Java binary and
tika-appjar. - On the settings form, confirm the message that Tika can be reached and executed.
- From the command line, run Tika in text mode against a JPEG that contains no extractable text. Tika exits 0, stdout is empty, and stderr may only contain an INFO line about inline images.
- Index that file through Search API Attachments.
- The log shows
Tika Extractor is not available, even though Tika ran successfully and simply found no text.
Proposed resolution
Read stdout with proc_open() and the process exit code. Throw only when stdout cannot be read, or when stdout is empty and the exit code is not 0. A non-zero exit that still produced text is returned unchanged, matching the previous shell_exec() behavior. Successful text output stays byte-for-byte identical.
Drupal and Symfony Process already require proc_open(), so this does not add a new PHP function requirement.
diff --git a/src/Plugin/search_api_attachments/TikaExtractor.php b/src/Plugin/search_api_attachments/TikaExtractor.php --- a/src/Plugin/search_api_attachments/TikaExtractor.php +++ b/src/Plugin/search_api_attachments/TikaExtractor.php @@ -67,11 +67,19 @@ if ($this->configuration['debug_mode']) { $cmd .= ' 2>/dev/null'; } - $output = shell_exec($cmd); - if (is_null($output)) { + // shell_exec() returns NULL both on failure and on empty stdout, e.g. an + // image without text. Read stdout as-is and check the exit code instead. + $process = proc_open($cmd, [1 => ['pipe', 'w']], $pipes); + if (!is_resource($process)) { + throw new \Exception('Tika Extractor is not available.'); + } + $output = stream_get_contents($pipes[1]); + fclose($pipes[1]); + $return_code = proc_close($process); + if ($output === FALSE || ($output === '' && $return_code !== 0)) { throw new \Exception('Tika Extractor is not available.'); } return $output; }
Remaining tasks
- Review
- Merge request against 10.0.x
User interface changes
None.
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #4 | 3628601-empty_stdout.patch | 1.4 KB | izus |
Issue fork search_api_attachments-3628601
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #3
izus commentedhi, thanks for the finding
i'm not sure we can assume proc_open is available for all sites using the module
core itself does some
if (!\function_exists('proc_open')) {and only assumes it's available for starting a webserver in /Core/Command/ServerCommand.php (production sites avoid that)i wonder if we can correct it differently
Comment #4
izus commentedhere is a patch with a suggestion that doesn't use proc_open
please test it
Comment #5
izus commentedthis is now merged
it is part of another commit here https://git.drupalcode.org/project/search_api_attachments/-/commit/2c426...